char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
1#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 2#pragma clang diagnostic ignored "-Wunused-function" 3#pragma clang diagnostic ignored "-Wunused-variable" 4#pragma clang diagnostic ignored "-Wunused-but-set-variable" 5 6#include <HAP_farf.h> 7#include <HAP_perf.h> 8#include <HAP_compute_res.h> 9 10#include <math.h> 11#include <string.h> 12#include <stdatomic.h> 13 14#include "hex-dma.h" 15#include "hvx-utils.h" 16#include "hvx-dump.h" 17#include "hvx-arith.h" 18#include "hvx-reduce.h" 19 20#define GGML_COMMON_DECL_C 21#include "ggml-common.h" 22#include "htp-ctx.h" 23#include "htp-ops.h" 24#include "htp-tensor.h" 25#include "matmul-ops.h" 26#include "htp-vtcm.h" 27 28static void hvx_tensor_add_f32_grid ( 29const struct htp_tensor * restrictdst , 30const struct htp_tensor * restrictsrc2 , 31uint32_t start_row , 32uint32_t end_row , 33uint32_t start_col , 34uint32_t end_col , 35const struct fastdiv_values * div_ne11_12 , 36const struct fastdiv_values * div_ne11 37); 38 39typedef struct { 40float * dst ; 41const float * src2 ; 42const float * activation ; 43const __fp16 * weight ; 44int m ; 45int k ; 46int n ; 47int act_stride ; 48int weight_stride ; 49int dst_stride ; 50uint32_t src2_stride ; 51int ne02 ; 52int ne03 ; 53int ne12 ; 54int ne13 ; 55size_t src0_nb2 ; 56size_t src0_nb3 ; 57size_t src1_nb2 ; 58size_t src1_nb3 ; 59size_t src2_nb2 ; 60size_t src2_nb3 ; 61size_t dst_nb2 ; 62size_t dst_nb3 ; 63int r2 ; 64int r3 ; 65struct fastdiv_values div_r2 ; 66struct fastdiv_values div_r3 ; 67}hmx_mm_f16_f32_batched_params_t ; 68 69struct htp_mm_context { 70const char * type ; 71struct htp_ops_context * octx ; 72const struct htp_tensor * act ; 73 74void (* vec_dot_1x1 )(const uint32_t n ,float * restricts0 , 75const void * restrictvx0 , 76const void * restrictvy0 ); 77 78void (* vec_dot_2x1 )(const uint32_t n ,float * restricts0 , 79const void * restrictvx0 ,const void * restrictvx1 , 80const void * restrictvy0 ); 81 82void (* vec_dot_2x2 )(const uint32_t n ,float * restricts0 ,float * restricts1 , 83const void * restrictvx0 ,const void * restrictvx1 , 84const void * restrictvy0 ,const void * restrictvy1 ); 85 86void (* vec_dot_32x1 )(const uint32_t n ,float * restricts , 87const void * restrictvx , 88const void * restrictvy ,uint32_t valid_rows , 89const float * restrictsz ); 90 91// Precomputed values 92uint32_t src0_nrows_per_thread ; 93uint32_t src0_row_start ; 94uint32_t src0_row_end ; 95uint32_t src0_row_size_padded ; 96uint32_t src1_nrows ; 97 98struct fastdiv_values mm_div_ne12_ne1 ; 99struct fastdiv_values mm_div_ne1 ; 100struct fastdiv_values mm_div_r2 ; 101struct fastdiv_values mm_div_r3 ; 102struct fastdiv_values mm_div_ne11 ; 103 104// Per thread quant tasks 105// Precomputed block-parallel quantization values 106worker_callback_t quant_task_func ; 107uint32_t quant_ib_first [WORK_QUEUE_MAX_N_THREADS ]; 108uint32_t quant_ib_last [WORK_QUEUE_MAX_N_THREADS ]; 109uint32_t quant_r [WORK_QUEUE_MAX_N_THREADS ]; 110uint32_t quant_c [WORK_QUEUE_MAX_N_THREADS ]; 111uint32_t n_quant_tasks ; 112uint32_t n_quant_rows_per_thread ; 113atomic_uint quant_barrier ; 114 115// Fields for scattered mapping & HMX support in MUL_MAT_ID 116const uint32_t * matrix_row_counts ; 117const struct mmid_row_mapping * matrix_rows ; 118uint32_t mapping_stride ; 119 120// Dynamic VTCM pointers allocated sequentially 121uint8_t * vtcm_src0 ; 122uint8_t * vtcm_src1 ; 123uint8_t * vtcm_src2 ; 124uint8_t * vtcm_src3 ; 125uint8_t * vtcm_dst ; 126 127// Cached strides 128uint32_t vtcm_src0_stride ; 129uint32_t vtcm_src1_stride ; 130uint32_t vtcm_src2_stride ; 131uint32_t vtcm_src3_stride ; 132 133// Cached thread offsets/sizes 134uint32_t vtcm_src0_size_per_thread ; 135uint32_t vtcm_src1_size_per_thread ; 136uint32_t vtcm_src2_size_per_thread ; 137uint32_t vtcm_src3_size_per_thread ; 138uint32_t vtcm_dst_size_per_thread ; 139}; 140 141static int htp_mm_init_context ( 142struct htp_ops_context * octx , 143const struct htp_mm_kernel_params * kparams 144) { 145if (!htp_ops_context_set_n_threads (octx , (uint32_t )kparams -> n_threads )) { 146return HTP_STATUS_INVAL_PARAMS ; 147 } 148 149if (kparams -> n_hmx ) { 150if (kparams -> n_act_threads <=0 || kparams -> n_act_threads > (int32_t )octx -> n_threads ) { 151return HTP_STATUS_INVAL_PARAMS ; 152 } 153 } 154 155return HTP_STATUS_OK ; 156} 157 158// vdelta control to expand first 32 e8m0 values into 32 uint32 elements 159static const uint8_t __attribute__((aligned (128 )))expand_x32_e8m0 [128 ]= { 1600x00 ,0x00 ,0x00 ,0x00 ,0x01 ,0x04 ,0x00 ,0x00 ,0x02 ,0x00 ,0x08 ,0x08 ,0x01 ,0x02 ,0x00 ,0x04 ,0x04 ,0x00 ,0x00 , 1610x00 ,0x11 ,0x10 ,0x10 ,0x10 ,0x02 ,0x00 ,0x04 ,0x00 ,0x01 ,0x02 ,0x08 ,0x08 ,0x08 ,0x08 ,0x00 ,0x00 ,0x01 ,0x04 , 1620x00 ,0x00 ,0x22 ,0x20 ,0x20 ,0x20 ,0x21 ,0x22 ,0x20 ,0x24 ,0x04 ,0x00 ,0x00 ,0x00 ,0x09 ,0x08 ,0x00 ,0x00 ,0x02 , 1630x00 ,0x04 ,0x00 ,0x11 ,0x12 ,0x10 ,0x10 ,0x10 ,0x10 ,0x10 ,0x10 ,0x01 ,0x04 ,0x00 ,0x00 ,0x02 ,0x00 ,0x08 ,0x08 , 1640x01 ,0x02 ,0x00 ,0x04 ,0x44 ,0x40 ,0x40 ,0x40 ,0x41 ,0x40 ,0x40 ,0x40 ,0x42 ,0x40 ,0x44 ,0x40 ,0x41 ,0x42 ,0x48 , 1650x48 ,0x08 ,0x08 ,0x00 ,0x00 ,0x01 ,0x04 ,0x00 ,0x00 ,0x12 ,0x10 ,0x10 ,0x10 ,0x01 ,0x02 ,0x00 ,0x04 ,0x04 ,0x00 , 1660x00 ,0x00 ,0x09 ,0x08 ,0x00 ,0x00 ,0x22 ,0x20 ,0x24 ,0x20 ,0x21 ,0x22 ,0x20 ,0x20 , 167}; 168 169// IQ4_NL dequantization LUT: maps 4-bit index (0-15) to int8 kvalue 170// kvalues: -127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113 171static const uint8_t __attribute__((aligned (VLEN )))kvalues_iq4nl_lut []= { 1720x81 ,0 ,0x98 ,0 ,0xAD ,0 ,0xBF ,0 ,0xCF ,0 ,0xDD ,0 ,0xEA ,0 ,0xF6 ,0 ,0x01 ,0 ,0x0D ,0 ,0x19 ,0 ,0x26 ,0 , 1730x35 ,0 ,0x45 ,0 ,0x59 ,0 ,0x71 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1740 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1750 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1760 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 177}; 178 179static const uint8_t __attribute__((aligned (VLEN )))kvalues_mxfp4_lut []= { 1800 ,0 ,1 ,0 ,2 ,0 ,3 ,0 ,4 ,0 ,6 ,0 ,8 ,0 ,12 ,0 ,0 ,0 ,0xff ,0 ,0xfe ,0 ,0xfd ,0 ,0xfc ,0 , 1810xfa ,0 ,0xf8 ,0 ,0xf4 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1820 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1830 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 1840 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 185}; 186 187#define htp_matmul_tensors_preamble \ 188 const struct htp_tensor * restrict src0 = octx->src[0]; \ 189 const struct htp_tensor * restrict src1 = octx->src[1]; \ 190 const struct htp_tensor * restrict src2 = octx->src[2]; \ 191 const struct htp_tensor * restrict dst = octx->dst; \ 192 \ 193 const uint32_t ne00 = src0->ne[0]; \ 194 const uint32_t ne01 = src0->ne[1]; \ 195 const uint32_t ne02 = src0->ne[2]; \ 196 const uint32_t ne03 = src0->ne[3]; \ 197 \ 198 const uint32_t ne10 = src1->ne[0]; \ 199 const uint32_t ne11 = src1->ne[1]; \ 200 const uint32_t ne12 = src1->ne[2]; \ 201 const uint32_t ne13 = src1->ne[3]; \ 202 \ 203 const uint32_t ne20 = src2 ? src2->ne[0] : 0; \ 204 const uint32_t ne21 = src2 ? src2->ne[1] : 0; \ 205 const uint32_t ne22 = src2 ? src2->ne[2] : 0; \ 206 const uint32_t ne23 = src2 ? src2->ne[3] : 0; \ 207 \ 208 const uint32_t ne0 = dst->ne[0]; \ 209 const uint32_t ne1 = dst->ne[1]; \ 210 const uint32_t ne2 = dst->ne[2]; \ 211 const uint32_t ne3 = dst->ne[3]; \ 212 \ 213 const uint32_t nb00 = src0->nb[0]; \ 214 const uint32_t nb01 = src0->nb[1]; \ 215 const uint32_t nb02 = src0->nb[2]; \ 216 const uint32_t nb03 = src0->nb[3]; \ 217 \ 218 const uint32_t nb10 = src1->nb[0]; \ 219 const uint32_t nb11 = src1->nb[1]; \ 220 const uint32_t nb12 = src1->nb[2]; \ 221 const uint32_t nb13 = src1->nb[3]; \ 222 \ 223 const uint32_t nb0 = dst->nb[0]; \ 224 const uint32_t nb1 = dst->nb[1]; \ 225 const uint32_t nb2 = dst->nb[2]; \ 226 const uint32_t nb3 = dst->nb[3]; 227 228#define htp_matmul_preamble \ 229 struct htp_mm_context * mmctx = data; \ 230 struct htp_ops_context * octx = mmctx->octx; \ 231 dma_queue *dma_queue = octx->ctx->dma[ith]; \ 232 uint32_t src0_nrows_per_thread = mmctx->src0_nrows_per_thread; \ 233 htp_matmul_tensors_preamble; 234 235static inline void hvx_mm_run_quant_task (struct htp_mm_context * mmctx ,unsigned int ith ) { 236if (mmctx -> quant_task_func ) { 237if (ith < mmctx -> n_quant_tasks ) { 238mmctx -> quant_task_func (mmctx -> n_quant_tasks ,ith ,mmctx ); 239atomic_fetch_sub (& mmctx -> quant_barrier ,1 ); 240 } 241while (atomic_load (& mmctx -> quant_barrier )> 0 ) { 242// spin 243 } 244 } 245} 246 247// *** matmul with support for 4d tensors and full broadcasting 248 249static void hvx_mm_4d (unsigned int nth ,unsigned int ith ,void * data ) { 250htp_matmul_preamble ; 251 252assert (ne12 %ne02 == 0 ); 253assert (ne13 %ne03 == 0 ); 254 255// This is the size of the first dimension of the result, so we can iterate that way. (see the ASSERT above, these are the same numbers) 256const uint32_t nr0 = ne0 ; 257 258// This is the size of the rest of the dimensions of the result 259const uint32_t nr1 = ne1 * ne2 * ne3 ; 260 261const uint32_t src0_nrows = mmctx -> src0_row_end - mmctx -> src0_row_start ; 262 263// distribute the thread work across the inner or outer loop based on which one is larger 264uint32_t dr0 ,dr1 ,ith0 ,ith1 ; 265if (nr0 > nr1 ) { 266dr0 = fastdiv (src0_nrows + nth - 1 ,& octx -> n_threads_div ); 267dr1 = nr1 ; 268ith0 = ith ; 269ith1 = 0 ; 270 }else { 271dr0 = src0_nrows ; 272dr1 = fastdiv (nr1 + nth - 1 ,& octx -> n_threads_div ); 273ith0 = 0 ; 274ith1 = ith ; 275 } 276 277const uint32_t ir0_start = mmctx -> src0_row_start + dr0 * ith0 ; 278const uint32_t ir0_end = MIN (ir0_start + dr0 ,mmctx -> src0_row_end ); 279 280const uint32_t ir1_start = dr1 * ith1 ; 281const uint32_t ir1_end = MIN (ir1_start + dr1 ,nr1 ); 282 283// no work for this thread 284if (ir0_start >=ir0_end || ir1_start >=ir1_end ) { 285return ; 286 } 287 288struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 289htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0_start ); 290 291const uint32_t blck_0 = 64 ; 292const uint32_t blck_1 = 64 ; 293 294for (uint32_t iir1 = ir1_start ;iir1 < ir1_end ;iir1 += blck_1 ) { 295for (uint32_t iir0 = ir0_start ;iir0 < ir0_end ;iir0 += blck_0 ) { 296for (uint32_t ir1 = iir1 ;ir1 < MIN (iir1 + blck_1 ,ir1_end );ir1 ++ ) { 297const uint32_t i13 = fastdiv (ir1 ,& mmctx -> mm_div_ne12_ne1 ); 298const uint32_t i12 = fastdiv (ir1 - i13 * ne12 * ne1 ,& mmctx -> mm_div_ne1 ); 299const uint32_t i11 = (ir1 - i13 * ne12 * ne1 - i12 * ne1 ); 300 301// broadcast src0 into src1 302const uint32_t i03 = fastdiv (i13 ,& mmctx -> mm_div_r3 ); 303const uint32_t i02 = fastdiv (i12 ,& mmctx -> mm_div_r2 ); 304 305const uint32_t i1 = i11 ; 306const uint32_t i2 = i12 ; 307const uint32_t i3 = i13 ; 308 309const uint8_t * restrictsrc0_base = (const uint8_t * )src0 -> data + (0 + i02 * nb02 + i03 * nb03 ); 310const uint8_t * restrictsrc1_col = (const uint8_t * )src1 -> data + (i11 * nb11 + i12 * nb12 + i13 * nb13 ); 311float * dst_col = (float * ) ((uint8_t * restrict)dst -> data + (i1 * nb1 + i2 * nb2 + i3 * nb3 )); 312 313const uint32_t ir0_block_end = MIN (iir0 + blck_0 ,ir0_end ); 314for (uint32_t ir0 = iir0 ;ir0 < ir0_block_end ;ir0 ++ ) { 315const uint8_t * restrictsrc0_row = src0_base + ir0 * nb01 ; 316mmctx -> vec_dot_1x1 (ne00 ,& dst_col [ir0 ],src0_row ,src1_col ); 317 } 318 } 319 } 320 } 321 322htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0_start ); 323if (src2 ) { 324hvx_tensor_add_f32_grid (dst ,src2 ,ir1_start ,ir1_end ,ir0_start ,ir0_end ,& mmctx -> mm_div_ne12_ne1 ,& mmctx -> mm_div_ne1 ); 325 } 326} 327 328#include "hmx-mm-kernels-tiled.h" 329#include "hvx-mm-kernels-tiled.h" 330#include "hvx-mm-kernels-flat.h" 331 332// Specialized repacked matmul macros 333#define MATMUL_2D_REPACKED_IMPL (SUFFIX ,TILE_SIZE ,DOT_2X2 ,DOT_2X1 ) \ 334static void hvx_mm_2d_repacked_##SUFFIX(unsigned int nth, unsigned int ith, void * data) { \ 335 htp_matmul_preamble; \ 336 \ 337 const uint32_t src0_nrows = mmctx->src0_row_end - mmctx->src0_row_start; \ 338 const uint32_t src1_nrows = ne11 * ne12 * ne13; \ 339 \ 340 const uint32_t src0_start_row = mmctx->src0_row_start + src0_nrows_per_thread * ith; \ 341 const uint32_t src0_end_row = MIN(src0_start_row + src0_nrows_per_thread, mmctx->src0_row_end); \ 342 \ 343 struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \ 344 \ 345 const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params; \ 346 const uint32_t n_prefetch = kparams->n_prefetch; \ 347 assert(n_prefetch >= 2 && n_prefetch <= HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1)) == 0); \ 348 \ 349 const size_t dst_row_size = nb1; \ 350 const size_t src1_row_size = nb11; \ 351 const size_t src1_stride = mmctx->vtcm_src1_stride; \ 352 const size_t src2_stride = src2 ? ((src2->ne[1] == 1) ? 0 : src2->nb[1]) : 0; \ 353 \ 354 uint8_t * restrict vtcm_dst_ptr = mmctx->vtcm_dst + mmctx->vtcm_dst_size_per_thread * ith; \ 355 uint8_t * restrict vtcm_src0_ptr = mmctx->vtcm_src0 + mmctx->vtcm_src0_size_per_thread * ith; \ 356 uint8_t * restrict src1_data = mmctx->vtcm_src1; \ 357 \ 358 const uint8_t * restrict src0_row = (const uint8_t *) src0->data; \ 359 \ 360 const uint32_t tile_size = TILE_SIZE; \ 361 const uint32_t aligned_tile_size = hex_align_up(tile_size, 128); \ 362 \ 363 uint32_t n_k_tiles_w = ne00 / 32; \ 364 uint32_t n_k_tiles_a = ne10 / 32; \ 365 uint32_t tile_row_stride = n_k_tiles_w * tile_size; \ 366 uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size; \ 367 \ 368 uint32_t ct_start = src0_start_row / 32; \ 369 uint32_t ct_end = (src0_end_row + 31) / 32; \ 370 \ 371 uint32_t push_ct = ct_start; \ 372 if (src0_start_row < src0_end_row) { \ 373 for (uint32_t d = 0; d < n_prefetch && push_ct < ct_end; d++, push_ct++) { \ 374 dma_queue_push(dma_queue, dma_make_ptr(vtcm_src0_ptr + d * tile_row_transfer_size_aligned, \ 375 src0_row + push_ct * tile_row_stride), aligned_tile_size, tile_size, tile_size, n_k_tiles_a); \ 376 } \ 377 } \ 378 \ 379 hvx_mm_run_quant_task(mmctx, ith); \ 380 \ 381 if (src0_start_row >= src0_end_row) { \ 382 return; \ 383 } \ 384 \ 385 for (uint32_t ct = ct_start; ct < ct_end; ct++) { \ 386 const uint8_t * w_tile = dma_queue_pop(dma_queue).dst; \ 387 \ 388 int valid_rows = (int)ne0 - (int)(ct * 32); \ 389 valid_rows = MIN(32, MAX(0, valid_rows)); \ 390 \ 391 htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, ct); \ 392 uint32_t ir1 = 0; \ 393 for (; ir1 + 1 < src1_nrows; ir1 += 2) { \ 394 const uint8_t * restrict src1_col0 = (const uint8_t *) (src1_data + (ir1+0) * src1_stride); \ 395 const uint8_t * restrict src1_col1 = (const uint8_t *) (src1_data + (ir1+1) * src1_stride); \ 396 float * restrict dst_row0 = (float *) (dst->data + ((ir1+0) * dst_row_size)); \ 397 float * restrict dst_row1 = (float *) (dst->data + ((ir1+1) * dst_row_size)); \ 398 \ 399 float * dst_ptr0 = &dst_row0[ct * 32]; \ 400 float * dst_ptr1 = &dst_row1[ct * 32]; \ 401 \ 402 const float * src2_ptr0 = NULL; \ 403 const float * src2_ptr1 = NULL; \ 404 if (src2) { \ 405 const float * restrict src2_row0 = (const float *) ((const uint8_t *) src2->data + ((ir1+0) * src2_stride)); \ 406 const float * restrict src2_row1 = (const float *) ((const uint8_t *) src2->data + ((ir1+1) * src2_stride)); \ 407 src2_ptr0 = &src2_row0[ct * 32]; \ 408 src2_ptr1 = &src2_row1[ct * 32]; \ 409 } \ 410 DOT_2X2(ne10, dst_ptr0, dst_ptr1, w_tile, src1_col0, src1_col1, valid_rows, src2_ptr0, src2_ptr1); \ 411 } \ 412 \ 413 for (; ir1 < src1_nrows; ++ir1) { \ 414 const uint8_t * restrict src1_col = (const uint8_t *) (src1_data + ir1 * src1_stride); \ 415 float * restrict dst_row = (float *) (dst->data + (ir1 * dst_row_size)); \ 416 float * dst_ptr = &dst_row[ct * 32]; \ 417 \ 418 const float * src2_ptr = NULL; \ 419 if (src2) { \ 420 const float * restrict src2_row = (const float *) ((const uint8_t *) src2->data + (ir1 * src2_stride)); \ 421 src2_ptr = &src2_row[ct * 32]; \ 422 } \ 423 DOT_2X1(ne10, dst_ptr, w_tile, src1_col, valid_rows, src2_ptr); \ 424 } \ 425 htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, ct); \ 426 \ 427 if (push_ct < ct_end) { \ 428 dma_queue_push(dma_queue, dma_make_ptr((uint8_t *)w_tile, src0_row + push_ct * tile_row_stride), \ 429 aligned_tile_size, tile_size, tile_size, n_k_tiles_a); \ 430 push_ct++; \ 431 } \ 432 } \ 433} 434 435#define MATVEC_2D_REPACKED_IMPL (SUFFIX ,TILE_SIZE ,DOT_2X1 ) \ 436static void hvx_mv_2d_repacked_##SUFFIX(unsigned int nth, unsigned int ith, void * data) { \ 437 htp_matmul_preamble; \ 438 \ 439 const uint32_t src0_nrows = mmctx->src0_row_end - mmctx->src0_row_start; \ 440 \ 441 const uint32_t src0_start_row = mmctx->src0_row_start + src0_nrows_per_thread * ith; \ 442 const uint32_t src0_end_row = MIN(src0_start_row + src0_nrows_per_thread, mmctx->src0_row_end); \ 443 \ 444 struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \ 445 \ 446 const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params; \ 447 const uint32_t n_prefetch = kparams->n_prefetch; \ 448 assert(n_prefetch >= 2 && n_prefetch <= HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1)) == 0); \ 449 \ 450 const size_t dst_row_size = nb1; \ 451 const size_t src1_row_size = nb11; \ 452 const size_t src1_stride = mmctx->vtcm_src1_stride; \ 453 \ 454 uint8_t * vtcm_dst_ptr = mmctx->vtcm_dst + mmctx->vtcm_dst_size_per_thread * ith; \ 455 uint8_t * vtcm_src0_ptr = mmctx->vtcm_src0 + mmctx->vtcm_src0_size_per_thread * ith; \ 456 uint8_t * src1_data = mmctx->vtcm_src1; \ 457 \ 458 float * tmp = (float *) vtcm_dst_ptr; \ 459 \ 460 const uint8_t * restrict src0_row = (const uint8_t *) src0->data; \ 461 \ 462 const uint8_t * restrict src1_col = (const uint8_t *) src1_data; \ 463 float * restrict dst_col = (float *) dst->data; \ 464 \ 465 const uint32_t tile_size = TILE_SIZE; \ 466 const uint32_t aligned_tile_size = hex_align_up(tile_size, 128); \ 467 \ 468 uint32_t n_k_tiles_w = ne00 / 32; \ 469 uint32_t n_k_tiles_a = ne10 / 32; \ 470 uint32_t tile_row_stride = n_k_tiles_w * tile_size; \ 471 uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size; \ 472 \ 473 uint32_t ct_start = src0_start_row / 32; \ 474 uint32_t ct_end = (src0_end_row + 31) / 32; \ 475 \ 476 uint32_t push_ct = ct_start; \ 477 if (src0_start_row < src0_end_row) { \ 478 if (src2) { \ 479 float * vtcm_src2_ptr = (float *) mmctx->vtcm_src2 + src0_start_row; \ 480 const float * src2_ptr = (const float *) src2->data + src0_start_row; \ 481 int slice_size = (int)MIN(src0_end_row, ne0) - (int)src0_start_row; \ 482 if (slice_size > 0) { \ 483 dma_queue_push(dma_queue, dma_make_ptr(vtcm_src2_ptr, src2_ptr), \ 484 slice_size * sizeof(float), slice_size * sizeof(float), slice_size * sizeof(float), 1); \ 485 dma_queue_pop_nowait(dma_queue); \ 486 } \ 487 } \ 488 for (uint32_t d = 0; d < n_prefetch && push_ct < ct_end; d++, push_ct++) { \ 489 dma_queue_push(dma_queue, dma_make_ptr(vtcm_src0_ptr + d * tile_row_transfer_size_aligned, \ 490 src0_row + push_ct * tile_row_stride), aligned_tile_size, tile_size, tile_size, n_k_tiles_a); \ 491 } \ 492 } \ 493 \ 494 hvx_mm_run_quant_task(mmctx, ith); \ 495 \ 496 if (src0_start_row >= src0_end_row) { \ 497 return; \ 498 } \ 499 \ 500 for (uint32_t ct = ct_start; ct < ct_end; ct++) { \ 501 const uint8_t * w_tile = dma_queue_pop(dma_queue).dst; \ 502 \ 503 float * dst_ptr = &tmp[ct * 32 - src0_start_row]; \ 504 int valid_rows = (int)ne0 - (int)(ct * 32); \ 505 valid_rows = MIN(32, MAX(0, valid_rows)); \ 506 \ 507 htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, ct); \ 508 DOT_2X1(ne10, dst_ptr, w_tile, src1_col, valid_rows, NULL); \ 509 htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, ct); \ 510 \ 511 if (push_ct < ct_end) { \ 512 dma_queue_push(dma_queue, dma_make_ptr((uint8_t *)w_tile, src0_row + push_ct * tile_row_stride), \ 513 aligned_tile_size, tile_size, tile_size, n_k_tiles_a); \ 514 push_ct++; \ 515 } \ 516 } \ 517 \ 518 int copy_cnt = (int)MIN(src0_end_row, ne0) - (int)src0_start_row; \ 519 if (copy_cnt > 0) { \ 520 htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, ct_end); \ 521 if (src2) { \ 522 hvx_add_f32_uaa((uint8_t *) &dst_col[src0_start_row], \ 523 (const uint8_t *) tmp, \ 524 (const uint8_t *) ((const float *) mmctx->vtcm_src2 + src0_start_row), \ 525 copy_cnt); \ 526 } else { \ 527 hvx_copy_f32_ua((uint8_t *) &dst_col[src0_start_row], (uint8_t *) tmp, copy_cnt); \ 528 } \ 529 htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, ct_end); \ 530 } \ 531} 532 533#define MATMUL_NX_2D_REPACKED_IMPL (SUFFIX ,TILE_SIZE ,DOT_2X2 ,DOT_2X1 ) \ 534static void hvx_mm_nx_2d_repacked_##SUFFIX(unsigned int nth, unsigned int ith, void * data) { \ 535 struct htp_mm_context * mmctx = data; \ 536 struct htp_ops_context * octx = mmctx->octx; \ 537 const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params *) octx->kernel_params; \ 538 const uint32_t n_weights = kparams->n_weights; \ 539 \ 540 const struct htp_tensor * restrict act = octx->src[n_weights];/* x */ \ 541const uint32_t ne10 = act -> ne [0 ]; \ 542const uint32_t src1_nrows = act -> ne [1 ]* act -> ne [2 ]* act -> ne [3 ]; \ 543const size_t src1_stride = mmctx -> vtcm_src1_stride ; \ 544 \ 545uint8_t * restrictvtcm_weight_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; \ 546uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; \ 547 \ 548struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; \ 549const uint32_t n_prefetch = kparams -> n_prefetch ; \ 550assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); \ 551 \ 552const uint32_t tile_size = TILE_SIZE ; \ 553const uint32_t aligned_tile_size = hex_align_up (tile_size ,128 ); \ 554uint32_t n_k_tiles_a = ne10 /32 ; \ 555uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size ; \ 556 \ 557dma_queue * dma_queue = octx -> ctx -> dma [ith ]; \ 558 \ 559hvx_mm_run_quant_task (mmctx ,ith ); \ 560 \ 561for (uint32_t widx = 0 ;widx < n_weights ;widx ++ ) { \ 562const struct htp_tensor * restrictsrc_w = octx -> src [widx ]; \ 563const struct htp_tensor * restrictdst = octx -> dsts [widx ]; \ 564if (!src_w || !dst )continue ; \ 565 \ 566const uint32_t ne00 = src_w -> ne [0 ]; \ 567const uint32_t ne01 = src_w -> ne [1 ]; \ 568const size_t dst_row_size = dst -> nb [1 ]; \ 569const uint8_t * restrictsrc_w_row = (const uint8_t * )src_w -> data ; \ 570 \ 571uint32_t n_k_tiles_w = ne00 /32 ; \ 572uint32_t tile_row_stride = n_k_tiles_w * tile_size ; \ 573 \ 574uint32_t src0_start_row = 0 ; \ 575uint32_t src0_end_row = ne01 ; \ 576if (octx -> ctx -> mdev .count > 1 ) { \ 577const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); \ 578const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (ne01 ,can_split ?32 :0 , \ 579octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); \ 580src0_start_row = range .start ; \ 581src0_end_row = range .start + range .count ; \ 582 } \ 583 \ 584const uint32_t nrows = src0_end_row - src0_start_row ; \ 585uint32_t src0_nrows_per_thread = fastdiv (nrows + nth - 1 ,& octx -> n_threads_div ); \ 586src0_nrows_per_thread = hex_round_up (src0_nrows_per_thread ,32 ); \ 587 \ 588const uint32_t start_row = src0_start_row + src0_nrows_per_thread * ith ; \ 589const uint32_t end_row = MIN (start_row + src0_nrows_per_thread ,src0_end_row ); \ 590if (start_row >=end_row )continue ; \ 591 \ 592uint32_t ct_start = start_row /32 ; \ 593uint32_t ct_end = (end_row + 31 ) /32 ; \ 594 \ 595uint32_t push_ct = ct_start ; \ 596for (uint32_t d = 0 ;d < n_prefetch && push_ct < ct_end ;d ++ ,push_ct ++ ) { \ 597dma_queue_push (dma_queue ,dma_make_ptr (vtcm_weight_ptr + d * tile_row_transfer_size_aligned , \ 598src_w_row + push_ct * tile_row_stride ),aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); \ 599 } \ 600 \ 601for (uint32_t ct = ct_start ;ct < ct_end ;ct ++ ) { \ 602const uint8_t * w_tile = dma_queue_pop (dma_queue ).dst ; \ 603int valid_rows = (int )ne01 - (int )(ct * 32 ); \ 604valid_rows = MIN (32 ,MAX (0 ,valid_rows )); \ 605 \ 606htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); \ 607uint32_t ir1 = 0 ; \ 608for (;ir1 + 1 < src1_nrows ;ir1 += 2 ) { \ 609const uint8_t * restrictsrc1_col0 = (const uint8_t * ) (src1_data + (ir1 + 0 )* src1_stride ); \ 610const uint8_t * restrictsrc1_col1 = (const uint8_t * ) (src1_data + (ir1 + 1 )* src1_stride ); \ 611 \ 612float * restrictdst_row0 = (float * ) (dst -> data + ((ir1 + 0 )* dst_row_size )); \ 613float * restrictdst_row1 = (float * ) (dst -> data + ((ir1 + 1 )* dst_row_size )); \ 614float * dst_ptr0 = & dst_row0 [ct * 32 ]; \ 615float * dst_ptr1 = & dst_row1 [ct * 32 ]; \ 616 \ 617DOT_2X2 (ne10 ,dst_ptr0 ,dst_ptr1 ,w_tile ,src1_col0 ,src1_col1 ,valid_rows ,NULL ,NULL ); \ 618 } \ 619 \ 620for (;ir1 < src1_nrows ;++ ir1 ) { \ 621const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + ir1 * src1_stride ); \ 622float * restrictdst_row = (float * ) (dst -> data + (ir1 * dst_row_size )); \ 623float * dst_ptr = & dst_row [ct * 32 ]; \ 624DOT_2X1 (ne10 ,dst_ptr ,w_tile ,src1_col ,valid_rows ,NULL ); \ 625 } \ 626htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); \ 627 \ 628if (push_ct < ct_end ) { \ 629dma_queue_push (dma_queue ,dma_make_ptr ((uint8_t * )w_tile ,src_w_row + push_ct * tile_row_stride ), \ 630aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); \ 631push_ct ++ ; \ 632 } \ 633 } \ 634 } \ 635} 636 637MATMUL_2D_REPACKED_IMPL (q4_0 ,576 ,tiled_vec_dot_q4_0_32x2 ,tiled_vec_dot_q4_0_32x1 ) 638MATMUL_2D_REPACKED_IMPL (q4_1 ,640 ,tiled_vec_dot_q4_1_32x2 ,tiled_vec_dot_q4_1_32x1 ) 639MATMUL_2D_REPACKED_IMPL (q8_0 ,1088 ,tiled_vec_dot_q8_0_32x2 ,tiled_vec_dot_q8_0_32x1 ) 640MATMUL_2D_REPACKED_IMPL (iq4nl ,576 ,tiled_vec_dot_iq4nl_32x2 ,tiled_vec_dot_iq4nl_32x1 ) 641MATMUL_2D_REPACKED_IMPL (mxfp4 ,544 ,tiled_vec_dot_mxfp4_32x2 ,tiled_vec_dot_mxfp4_32x1 ) 642 643MATMUL_2D_REPACKED_IMPL (q4_0_flat ,576 ,flat_vec_dot_q4_0_32x2 ,flat_vec_dot_q4_0_32x1 ) 644MATMUL_2D_REPACKED_IMPL (q4_1_flat ,640 ,flat_vec_dot_q4_1_32x2 ,flat_vec_dot_q4_1_32x1 ) 645MATMUL_2D_REPACKED_IMPL (q8_0_flat ,1088 ,flat_vec_dot_q8_0_32x2 ,flat_vec_dot_q8_0_32x1 ) 646MATMUL_2D_REPACKED_IMPL (iq4nl_flat ,576 ,flat_vec_dot_iq4nl_32x2 ,flat_vec_dot_iq4nl_32x1 ) 647MATMUL_2D_REPACKED_IMPL (mxfp4_flat ,544 ,flat_vec_dot_mxfp4_32x2 ,flat_vec_dot_mxfp4_32x1 ) 648 649#define QUANTIZE_IMPL (name ,log_name ,kernel_fn ,dst_row_size_expr ) \ 650static void name(unsigned int nth, unsigned int ith, void * data) { \ 651 struct htp_mm_context * mmctx = data; \ 652 struct htp_ops_context * octx = mmctx->octx; \ 653 const struct htp_tensor * src = mmctx->act; \ 654 const uint32_t ne0 = src->ne[0]; \ 655 const uint32_t ne1 = src->ne[1]; \ 656 const uint32_t ne2 = src->ne[2]; \ 657 const uint32_t ne3 = src->ne[3]; \ 658 const uint32_t nrows = ne1 * ne2 * ne3; \ 659 const uint32_t nrows_per_thread = mmctx->n_quant_rows_per_thread; \ 660 \ 661 const uint32_t ir_first = nrows_per_thread * ith; \ 662 if (ir_first >= nrows) { \ 663 return; \ 664 } \ 665 \ 666 struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \ 667 htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_A_QUANT, ir_first); \ 668 \ 669 uint8_t * restrict dst = mmctx->vtcm_src1; \ 670 const uint32_t ir_last = MIN(ir_first + nrows_per_thread, nrows); \ 671 const size_t src_row_size = src->nb[1]; \ 672 const size_t dst_row_size = (dst_row_size_expr); \ 673 const uint8_t * restrict src_data = (const uint8_t *) src->data + (src_row_size * ir_first); \ 674 uint8_t * restrict dst_data = (uint8_t *) dst + (dst_row_size * ir_first); \ 675 uint8_t * restrict tmp_data = (uint8_t *) mmctx->vtcm_dst + (mmctx->vtcm_dst_size_per_thread * ith); \ 676 kernel_fn(src_data, dst_data, tmp_data, ne0, ir_last - ir_first, src_row_size, dst_row_size); \ 677 \ 678 htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_A_QUANT, ir_first); \ 679} 680 681QUANTIZE_IMPL (quantize_f32_q8_0_tiled ,"quantize-f32-q8_0_tiled" ,quantize_f32_q8_0_tiled_kernel ,htp_mm_q8_0_tiled_row_size (ne0 )) 682QUANTIZE_IMPL (quantize_f32_q8_1_tiled ,"quantize-f32-q8_1_tiled" ,quantize_f32_q8_1_tiled_kernel ,htp_mm_q8_1_tiled_row_size (ne0 )) 683QUANTIZE_IMPL (quantize_f32_q8_0_flat ,"quantize-f32-q8_0_flat" ,quantize_f32_q8_0_flat_kernel ,htp_mm_q8_0_flat_row_size (ne0 )) 684QUANTIZE_IMPL (quantize_f32_q8_1_flat ,"quantize-f32-q8_1_flat" ,quantize_f32_q8_1_flat_kernel ,htp_mm_q8_1_flat_row_size (ne0 )) 685QUANTIZE_IMPL (quantize_f32_f32_flat ,"quantize-f32-f32" ,quantize_f32_f32_flat_kernel ,mmctx -> vtcm_src1_stride ) 686QUANTIZE_IMPL (quantize_f32_f16_flat ,"quantize-f32-f16" ,quantize_f32_f16_flat_kernel ,mmctx -> vtcm_src1_stride ) 687QUANTIZE_IMPL (quantize_f16_f16_flat ,"quantize-f16-f16" ,quantize_f16_f16_flat_kernel ,mmctx -> vtcm_src1_stride ) 688 689static void quantize_f32_q8_0_tiled_block (unsigned int nth ,unsigned int ith ,void * data ) { 690struct htp_mm_context * mmctx = data ; 691struct htp_ops_context * octx = mmctx -> octx ; 692struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 693htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,mmctx -> quant_ib_first [ith ]); 694 695const struct htp_tensor * src = mmctx -> act ; 696 697quantize_f32_q8_0_tiled_block_kernel ( 698 (const float * )src -> data , 699mmctx -> vtcm_src1 , 700 (uint8_t * )mmctx -> vtcm_dst + (mmctx -> vtcm_dst_size_per_thread * ith ), 701src -> ne [0 ], 702mmctx -> quant_ib_first [ith ], 703mmctx -> quant_ib_last [ith ], 704src -> nb [1 ], 705htp_mm_q8_0_tiled_row_size (src -> ne [0 ]), 706mmctx -> quant_r [ith ], 707mmctx -> quant_c [ith ] 708 ); 709 710htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,mmctx -> quant_ib_first [ith ]); 711} 712 713static void quantize_f32_q8_1_tiled_block (unsigned int nth ,unsigned int ith ,void * data ) { 714struct htp_mm_context * mmctx = data ; 715struct htp_ops_context * octx = mmctx -> octx ; 716struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 717htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,mmctx -> quant_ib_first [ith ]); 718 719const struct htp_tensor * src = mmctx -> act ; 720 721quantize_f32_q8_1_tiled_block_kernel ( 722 (const float * )src -> data , 723mmctx -> vtcm_src1 , 724 (uint8_t * )mmctx -> vtcm_dst + (mmctx -> vtcm_dst_size_per_thread * ith ), 725src -> ne [0 ], 726mmctx -> quant_ib_first [ith ], 727mmctx -> quant_ib_last [ith ], 728src -> nb [1 ], 729htp_mm_q8_1_tiled_row_size (src -> ne [0 ]), 730mmctx -> quant_r [ith ], 731mmctx -> quant_c [ith ] 732 ); 733 734htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,mmctx -> quant_ib_first [ith ]); 735} 736 737MATVEC_2D_REPACKED_IMPL (q4_0 ,576 ,tiled_vec_dot_q4_0_32x1 ) 738MATVEC_2D_REPACKED_IMPL (q4_1 ,640 ,tiled_vec_dot_q4_1_32x1 ) 739MATVEC_2D_REPACKED_IMPL (q8_0 ,1088 ,tiled_vec_dot_q8_0_32x1 ) 740MATVEC_2D_REPACKED_IMPL (iq4nl ,576 ,tiled_vec_dot_iq4nl_32x1 ) 741MATVEC_2D_REPACKED_IMPL (mxfp4 ,544 ,tiled_vec_dot_mxfp4_32x1 ) 742 743MATVEC_2D_REPACKED_IMPL (q4_0_flat ,576 ,flat_vec_dot_q4_0_32x1 ) 744MATVEC_2D_REPACKED_IMPL (q4_1_flat ,640 ,flat_vec_dot_q4_1_32x1 ) 745MATVEC_2D_REPACKED_IMPL (q8_0_flat ,1088 ,flat_vec_dot_q8_0_32x1 ) 746MATVEC_2D_REPACKED_IMPL (iq4nl_flat ,576 ,flat_vec_dot_iq4nl_32x1 ) 747MATVEC_2D_REPACKED_IMPL (mxfp4_flat ,544 ,flat_vec_dot_mxfp4_32x1 ) 748 749 750MATMUL_NX_2D_REPACKED_IMPL (q4_0 ,576 ,tiled_vec_dot_q4_0_32x2 ,tiled_vec_dot_q4_0_32x1 ) 751MATMUL_NX_2D_REPACKED_IMPL (q4_1 ,640 ,tiled_vec_dot_q4_1_32x2 ,tiled_vec_dot_q4_1_32x1 ) 752MATMUL_NX_2D_REPACKED_IMPL (q8_0 ,1088 ,tiled_vec_dot_q8_0_32x2 ,tiled_vec_dot_q8_0_32x1 ) 753MATMUL_NX_2D_REPACKED_IMPL (iq4nl ,576 ,tiled_vec_dot_iq4nl_32x2 ,tiled_vec_dot_iq4nl_32x1 ) 754MATMUL_NX_2D_REPACKED_IMPL (mxfp4 ,544 ,tiled_vec_dot_mxfp4_32x2 ,tiled_vec_dot_mxfp4_32x1 ) 755 756MATMUL_NX_2D_REPACKED_IMPL (q4_0_flat ,576 ,flat_vec_dot_q4_0_32x2 ,flat_vec_dot_q4_0_32x1 ) 757MATMUL_NX_2D_REPACKED_IMPL (q4_1_flat ,640 ,flat_vec_dot_q4_1_32x2 ,flat_vec_dot_q4_1_32x1 ) 758MATMUL_NX_2D_REPACKED_IMPL (q8_0_flat ,1088 ,flat_vec_dot_q8_0_32x2 ,flat_vec_dot_q8_0_32x1 ) 759MATMUL_NX_2D_REPACKED_IMPL (iq4nl_flat ,576 ,flat_vec_dot_iq4nl_32x2 ,flat_vec_dot_iq4nl_32x1 ) 760MATMUL_NX_2D_REPACKED_IMPL (mxfp4_flat ,544 ,flat_vec_dot_mxfp4_32x2 ,flat_vec_dot_mxfp4_32x1 ) 761 762static void hvx_mm_2d (unsigned int nth ,unsigned int ith ,void * data ) { 763htp_matmul_preamble ; 764 765const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 766const uint32_t n_prefetch = kparams -> n_prefetch ; 767assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 768const uint32_t prefetch_mask = n_prefetch - 1 ; 769 770const uint32_t src0_nrows = mmctx -> src0_row_end - mmctx -> src0_row_start ;// src0 rows 771const uint32_t src1_nrows = ne11 * ne12 * ne13 ;// src1 rows 772 773const uint32_t src0_start_row = mmctx -> src0_row_start + src0_nrows_per_thread * ith ; 774const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,mmctx -> src0_row_end ); 775const uint32_t src0_end_row_x2 = src0_start_row + ((src0_end_row - src0_start_row )& ~1U ); 776 777struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 778 779const size_t dst_row_size = nb1 ; 780const size_t src0_row_size = nb01 ; 781const size_t src1_row_size = nb11 ; 782 783const size_t src0_stride = mmctx -> vtcm_src0_stride ; 784const size_t src1_stride = mmctx -> vtcm_src1_stride ; 785 786// Per-thread VTCMs for all tensors 787uint8_t * restrictvtcm_dst_ptr = mmctx -> vtcm_dst + mmctx -> vtcm_dst_size_per_thread * ith ; 788uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 789uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 790 791const uint8_t * restrictsrc0_row = (const uint8_t * )src0 -> data ; 792 793// Prefill vtcm with src0 rows 794if (src0_start_row < src0_end_row ) { 795for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 796const int is0 = (ir0 - src0_start_row ); 797if (is0 >= (int )n_prefetch ) { 798break ; 799 } 800dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 801src0_stride ,src0_row_size ,src0_row_size ,2 ); 802 } 803 } 804 805hvx_mm_run_quant_task (mmctx ,ith ); 806 807if (src0_start_row >=src0_end_row ) { 808return ; 809 } 810 811// Process src0 rows 812for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 813const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 814 815htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 816// Process src1 columns in pairs (2x2 tiling) 817uint32_t ir1 = 0 ; 818for (;ir1 + 1 < src1_nrows ;ir1 += 2 ) { 819const uint8_t * restrictsrc1_col0 = (const uint8_t * ) (src1_data + (ir1 + 0 )* src1_stride ); 820const uint8_t * restrictsrc1_col1 = (const uint8_t * ) (src1_data + (ir1 + 1 )* src1_stride ); 821float * restrictdst_row0 = (float * ) (dst -> data + ((ir1 + 0 )* dst_row_size )); 822float * restrictdst_row1 = (float * ) (dst -> data + ((ir1 + 1 )* dst_row_size )); 823mmctx -> vec_dot_2x2 (ne00 ,& dst_row0 [ir0 ],& dst_row1 [ir0 ],ss0 ,ss0 + src0_stride ,src1_col0 ,src1_col1 ); 824 } 825 826// Handle remaining src1 rows (fallback to 2x1) 827for (;ir1 < src1_nrows ;++ ir1 ) { 828const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + ir1 * src1_stride ); 829float * restrictdst_row = (float * ) (dst -> data + (ir1 * dst_row_size )); 830mmctx -> vec_dot_2x1 (ne00 ,& dst_row [ir0 ],ss0 ,ss0 + src0_stride ,src1_col ); 831 } 832htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 833 834// Prefetch next (n + vtcm_nrows) row 835const int pr0 = (ir0 + n_prefetch ); 836const int is0 = (pr0 - src0_start_row )& prefetch_mask ; 837if (pr0 < src0_end_row_x2 ) { 838dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + pr0 * src0_row_size ), 839src0_stride ,src0_row_size ,src0_row_size ,2 ); 840 } 841 } 842 843// Process the last row (if any) 844if (src0_end_row != src0_end_row_x2 ) { 845uint32_t ir0 = src0_end_row_x2 ; 846const int is0 = (ir0 - src0_start_row )& prefetch_mask ; 847dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 848src0_stride ,src0_row_size ,src0_row_size ,1 ); 849const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 850 851htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 852#pragma unroll(2) 853for (uint32_t ir1 = 0 ;ir1 < src1_nrows ;++ ir1 ) { 854const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + ir1 * src1_stride ); 855float * restrictdst_row = (float * ) (dst -> data + (ir1 * dst_row_size )); 856mmctx -> vec_dot_1x1 (ne00 ,& dst_row [ir0 ],ss0 ,src1_col ); 857 } 858htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 859 } 860if (src2 ) { 861hvx_tensor_add_f32_grid (dst ,src2 ,0 ,src1_nrows ,src0_start_row ,src0_end_row ,& kparams -> div_ne12_ne1 ,& kparams -> div_ne1 ); 862 } 863} 864 865static void hvx_mv_2d (unsigned int nth ,unsigned int ith ,void * data ) { 866htp_matmul_preamble ; 867 868const uint32_t src0_nrows = mmctx -> src0_row_end - mmctx -> src0_row_start ; 869 870const uint32_t src0_start_row = mmctx -> src0_row_start + src0_nrows_per_thread * ith ; 871const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,mmctx -> src0_row_end ); 872 873struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 874 875const size_t dst_row_size = nb1 ; 876const size_t src0_row_size = nb01 ; 877const size_t src1_row_size = nb11 ; 878 879const size_t src0_stride = mmctx -> vtcm_src0_stride ; 880const size_t src1_stride = mmctx -> vtcm_src1_stride ; 881 882// Per-thread VTCMs for all tensors 883uint8_t * vtcm_dst_ptr = mmctx -> vtcm_dst + mmctx -> vtcm_dst_size_per_thread * ith ; 884uint8_t * vtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 885uint8_t * src1_data = mmctx -> vtcm_src1 ; 886 887float * tmp = (float * )vtcm_dst_ptr ; 888 889const uint8_t * restrictsrc0_row = (const uint8_t * )src0 -> data ; 890const uint8_t * restrictsrc1_col = (const uint8_t * )src1_data ; 891float * restrictdst_col = (float * )dst -> data ; 892 893const uint32_t src0_end_row_x2 = src0_start_row + ((src0_end_row - src0_start_row )& ~1U ); 894 895const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 896const uint32_t n_prefetch = kparams -> n_prefetch ; 897assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 898const uint32_t prefetch_mask = n_prefetch - 1 ; 899 900// Prefill vtcm with 2x src0 rows 901if (src0_start_row < src0_end_row ) { 902if (src2 ) { 903float * vtcm_src2_ptr = (float * )mmctx -> vtcm_src2 + src0_start_row ; 904const float * src2_ptr = (const float * )src2 -> data + src0_start_row ; 905int slice_size = (int )src0_end_row - (int )src0_start_row ; 906if (slice_size > 0 ) { 907dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src2_ptr ,src2_ptr ), 908slice_size * sizeof (float ),slice_size * sizeof (float ),slice_size * sizeof (float ),1 ); 909dma_queue_pop_nowait (dma_queue ); 910 } 911 } 912for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 913const uint32_t is0 = (ir0 - src0_start_row ); 914if (is0 >=n_prefetch ) { 915break ; 916 } 917dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 918src0_stride ,src0_row_size ,src0_row_size ,2 ); 919 } 920 } 921 922hvx_mm_run_quant_task (mmctx ,ith ); 923 924if (src0_start_row >=src0_end_row ) { 925return ; 926 } 927 928// Process src0 rows 929for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 930const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 931htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 932mmctx -> vec_dot_2x1 (ne00 ,& tmp [ir0 - src0_start_row ],ss0 ,ss0 + src0_stride ,src1_col ); 933htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 934 935// Prefetch next (n + vtcm_nrows) row 936const uint32_t pr0 = (ir0 + n_prefetch ); 937const uint32_t is0 = (pr0 - src0_start_row )& prefetch_mask ; 938if (pr0 < src0_end_row_x2 ) { 939dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + pr0 * src0_row_size ), 940src0_stride ,src0_row_size ,src0_row_size ,2 ); 941 } 942 } 943 944// Process the last row (if any) 945if (src0_end_row != src0_end_row_x2 ) { 946const uint32_t ir0 = src0_end_row_x2 ; 947const uint32_t is0 = (ir0 - src0_start_row )& prefetch_mask ; 948dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 949src0_stride ,src0_row_size ,src0_row_size ,1 ); 950const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 951htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 952mmctx -> vec_dot_1x1 (ne00 ,& tmp [ir0 - src0_start_row ],ss0 ,src1_col ); 953htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 954 } 955 956int copy_cnt = src0_end_row - src0_start_row ; 957if (copy_cnt > 0 ) { 958htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,src0_end_row ); 959if (src2 ) { 960hvx_add_f32_uaa ((uint8_t * )& dst_col [src0_start_row ], 961 (const uint8_t * )tmp , 962 (const uint8_t * ) ((const float * )mmctx -> vtcm_src2 + src0_start_row ), 963copy_cnt ); 964 }else { 965hvx_copy_f32_ua ((uint8_t * )& dst_col [src0_start_row ], (uint8_t * )tmp ,copy_cnt ); 966 } 967htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,src0_end_row ); 968 } 969} 970 971#define MMID_MATRIX_ROW (row_id ,i1 ) matrix_rows[(row_id) * mmctx->mapping_stride + (i1)] 972 973static void hvx_mm_id (unsigned int nth ,unsigned int ith ,void * data ) { 974htp_matmul_preamble ; 975 976const struct htp_tensor * restrictids = octx -> src [2 ]; 977 978const uint32_t src0_nrows = mmctx -> src0_row_end - mmctx -> src0_row_start ;// src0 rows per expert 979const uint32_t src1_nrows = ne11 ; 980const uint32_t src0_start_row = mmctx -> src0_row_start + src0_nrows_per_thread * ith ; 981const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,mmctx -> src0_row_end ); 982 983hvx_mm_run_quant_task (mmctx ,ith ); 984 985if (src0_start_row >=src0_end_row ) { 986return ; 987 } 988 989struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 990 991const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 992const uint32_t n_prefetch = kparams -> n_prefetch ; 993assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 994 995const uint32_t n_ids = ids -> ne [0 ];// n_expert_used 996const uint32_t n_as = ne02 ;// n_expert 997 998const uint32_t * matrix_row_counts = mmctx -> matrix_row_counts ; 999const struct mmid_row_mapping * matrix_rows = mmctx -> matrix_rows ; 1000 1001const size_t dst_row_size = nb1 ; 1002const size_t src1_row_size = htp_mm_q8_0_tiled_row_size (ne10 ); 1003 1004const size_t src1_stride = mmctx -> vtcm_src1_stride ; 1005 1006// Per-thread VTCMs for all tensors 1007uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 1008uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 1009 1010for (uint32_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 1011const int32_t cne1 = matrix_row_counts [cur_a ]; 1012if (cne1 == 0 ) { 1013continue ; 1014 } 1015 1016const uint8_t * src0_row = (const uint8_t * )src0 -> data + cur_a * nb02 ; 1017 1018const uint32_t tile_size = htp_mm_get_weight_tile_size (src0 -> type ); 1019const uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size (src0 -> type ); 1020const uint32_t n_k_tiles_w = ne00 /32 ; 1021const uint32_t n_k_tiles_a = ne10 /32 ; 1022const uint32_t tile_row_stride = n_k_tiles_w * tile_size ; 1023const uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size ; 1024 1025const uint32_t ct_start = src0_start_row /32 ; 1026const uint32_t ct_end = (src0_end_row + 31 ) /32 ; 1027 1028uint32_t push_ct = ct_start ; 1029for (uint32_t d = 0 ;d < n_prefetch && push_ct < ct_end ;d ++ ,push_ct ++ ) { 1030dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + d * tile_row_transfer_size_aligned ,src0_row + push_ct * tile_row_stride ), 1031aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1032 } 1033 1034for (uint32_t ct = ct_start ;ct < ct_end ;ct ++ ) { 1035const uint8_t * w_tile = dma_queue_pop (dma_queue ).dst ; 1036 1037int valid_rows = (int )ne01 - (int )(ct * 32 ); 1038valid_rows = MIN (32 ,MAX (0 ,valid_rows )); 1039 1040htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1041for (uint32_t cid = 0 ;cid < cne1 ;++ cid ) { 1042struct mmid_row_mapping row_mapping = MMID_MATRIX_ROW (cur_a ,cid ); 1043const int rm1 = row_mapping .i1 ;// expert idx 1044const int rm2 = row_mapping .i2 ;// token idx 1045 1046const uint32_t ir1 = fastmodulo (rm1 ,ne11 ,& mmctx -> mm_div_ne11 );// src1 row idx 1047const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + (ir1 + rm2 * ne11 + 0 )* src1_stride ); 1048float * restrictdst_row = (float * ) (dst -> data + (rm1 * nb1 + rm2 * nb2 + 0 )); 1049 1050mmctx -> vec_dot_32x1 (ne10 ,& dst_row [ct * 32 ],w_tile ,src1_col ,valid_rows ,NULL ); 1051 } 1052htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1053 1054if (push_ct < ct_end ) { 1055dma_queue_push (dma_queue ,dma_make_ptr ((uint8_t * )w_tile ,src0_row + push_ct * tile_row_stride ), 1056aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1057push_ct ++ ; 1058 } 1059 } 1060 } 1061} 1062 1063static void hvx_mv_id (unsigned int nth ,unsigned int ith ,void * data ) { 1064htp_matmul_preamble ; 1065 1066const struct htp_tensor * restrictids = octx -> src [2 ]; 1067 1068const uint32_t src0_nrows = mmctx -> src0_row_end - mmctx -> src0_row_start ;// src0 rows per expert 1069const uint32_t src0_start_row = mmctx -> src0_row_start + src0_nrows_per_thread * ith ; 1070const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,mmctx -> src0_row_end ); 1071 1072hvx_mm_run_quant_task (mmctx ,ith ); 1073 1074if (src0_start_row >=src0_end_row ) { 1075return ; 1076 } 1077 1078struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 1079 1080const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 1081const uint32_t n_prefetch = kparams -> n_prefetch ; 1082assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 1083 1084assert (ne13 %ne03 == 0 ); 1085 1086const size_t dst_row_size = nb1 ; 1087const size_t src1_row_size = htp_mm_q8_0_tiled_row_size (ne10 ); 1088 1089const uint32_t n_aids = src2 -> ne [0 ];// num activated experts 1090const uint32_t n_ids = ne02 ;// num experts 1091 1092// Per-thread VTCMs for all tensors 1093uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 1094uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 1095 1096for (uint32_t ie1 = 0 ;ie1 < n_aids ;++ ie1 ) {// for each expert 1097const int32_t eid = * (const int32_t * ) ((const uint8_t * )src2 -> data + ie1 * src2 -> nb [0 ]); 1098if (eid < 0 ) { 1099continue ; 1100 } 1101assert (eid < (int32_t )n_ids ); 1102 1103const uint8_t * restrictsrc0_row = (const uint8_t * )src0 -> data + eid * nb02 ; 1104const uint8_t * restrictsrc1_col = (const uint8_t * )src1_data ; 1105float * restrictdst_row = (float * ) (dst -> data + ie1 * nb1 ); 1106 1107const uint32_t tile_size = htp_mm_get_weight_tile_size (src0 -> type ); 1108const uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size (src0 -> type ); 1109const uint32_t n_k_tiles_w = ne00 /32 ; 1110const uint32_t n_k_tiles_a = ne10 /32 ; 1111const uint32_t tile_row_stride = n_k_tiles_w * tile_size ; 1112const uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size ; 1113 1114const uint32_t ct_start = src0_start_row /32 ; 1115const uint32_t ct_end = (src0_end_row + 31 ) /32 ; 1116 1117uint32_t push_ct = ct_start ; 1118for (uint32_t d = 0 ;d < n_prefetch && push_ct < ct_end ;d ++ ,push_ct ++ ) { 1119dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + d * tile_row_transfer_size_aligned ,src0_row + push_ct * tile_row_stride ), 1120aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1121 } 1122 1123for (uint32_t ct = ct_start ;ct < ct_end ;ct ++ ) { 1124const uint8_t * w_tile = dma_queue_pop (dma_queue ).dst ; 1125 1126int valid_rows = (int )ne01 - (int )(ct * 32 ); 1127valid_rows = MIN (32 ,MAX (0 ,valid_rows )); 1128 1129htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1130mmctx -> vec_dot_32x1 (ne10 ,& dst_row [ct * 32 ],w_tile ,src1_col ,valid_rows ,NULL ); 1131htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1132 1133if (push_ct < ct_end ) { 1134dma_queue_push (dma_queue ,dma_make_ptr ((uint8_t * )w_tile ,src0_row + push_ct * tile_row_stride ), 1135aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1136push_ct ++ ; 1137 } 1138 } 1139 } 1140} 1141 1142static void hvx_mv_id_nx (unsigned int nth ,unsigned int ith ,void * data ) { 1143struct htp_mm_context * mmctx = (struct htp_mm_context * )data ; 1144struct htp_ops_context * octx = mmctx -> octx ; 1145dma_queue * dma_queue = octx -> ctx -> dma [ith ]; 1146const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 1147const uint32_t n_weights = kparams -> n_weights ; 1148const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 1149const struct htp_tensor * restrictact = octx -> src [n_weights ]; 1150const struct htp_tensor * restrictids = octx -> src [n_weights + 1 ]; 1151 1152hvx_mm_run_quant_task (mmctx ,ith ); 1153 1154struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 1155 1156const uint32_t n_prefetch = kparams -> n_prefetch ; 1157assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 1158 1159const uint32_t n_aids = ids -> ne [0 ]; 1160const uint32_t n_ids = src0 -> ne [2 ]; 1161 1162uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 1163uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 1164 1165for (uint32_t ie1 = 0 ;ie1 < n_aids ;++ ie1 ) { 1166const int32_t eid = * (const int32_t * ) ((const uint8_t * )ids -> data + ie1 * ids -> nb [0 ]); 1167if (eid < 0 )continue ; 1168assert (eid < (int32_t )n_ids ); 1169 1170for (uint32_t p = 0 ;p < n_weights ;++ p ) { 1171const struct htp_tensor * restrictsrc_w = octx -> src [p ]; 1172const struct htp_tensor * restrictdst = octx -> dsts [p ]; 1173if (!src_w || !dst )continue ; 1174 1175const uint32_t ne01 = src_w -> ne [1 ]; 1176uint32_t start_row = 0 ; 1177uint32_t end_row = ne01 ; 1178if (octx -> ctx -> mdev .count > 1 ) { 1179const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 1180const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (ne01 ,can_split ?32 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 1181start_row = range .start ; 1182end_row = range .start + range .count ; 1183 } 1184 1185const uint32_t nrows = end_row - start_row ; 1186uint32_t src0_nrows_per_thread = fastdiv (nrows + nth - 1 ,& octx -> n_threads_div ); 1187src0_nrows_per_thread = hex_round_up (src0_nrows_per_thread ,32 ); 1188 1189const uint32_t src0_start_row = start_row + src0_nrows_per_thread * ith ; 1190const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,end_row ); 1191if (src0_start_row >=src0_end_row )continue ; 1192 1193const uint8_t * restrictsrc0_row = (const uint8_t * )src_w -> data + eid * src_w -> nb [2 ]; 1194const uint8_t * restrictsrc1_col = (const uint8_t * )src1_data ; 1195float * restrictdst_row = (float * ) (dst -> data + ie1 * dst -> nb [1 ]); 1196 1197const uint32_t tile_size = htp_mm_get_weight_tile_size (src_w -> type ); 1198const uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size (src_w -> type ); 1199const uint32_t n_k_tiles_w = src_w -> ne [0 ] /32 ; 1200const uint32_t n_k_tiles_a = act -> ne [0 ] /32 ; 1201const uint32_t tile_row_stride = n_k_tiles_w * tile_size ; 1202const uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size ; 1203 1204const uint32_t ct_start = src0_start_row /32 ; 1205const uint32_t ct_end = (src0_end_row + 31 ) /32 ; 1206 1207uint32_t push_ct = ct_start ; 1208for (uint32_t d = 0 ;d < n_prefetch && push_ct < ct_end ;d ++ ,push_ct ++ ) { 1209dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + d * tile_row_transfer_size_aligned ,src0_row + push_ct * tile_row_stride ), 1210aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1211 } 1212 1213for (uint32_t ct = ct_start ;ct < ct_end ;ct ++ ) { 1214const uint8_t * w_tile = dma_queue_pop (dma_queue ).dst ; 1215 1216int valid_rows = (int )src_w -> ne [1 ]- (int )(ct * 32 ); 1217valid_rows = MIN (32 ,MAX (0 ,valid_rows )); 1218 1219htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1220mmctx -> vec_dot_32x1 (act -> ne [0 ],& dst_row [ct * 32 ],w_tile ,src1_col ,valid_rows ,NULL ); 1221htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1222 1223if (push_ct < ct_end ) { 1224dma_queue_push (dma_queue ,dma_make_ptr ((uint8_t * )w_tile ,src0_row + push_ct * tile_row_stride ), 1225aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1226push_ct ++ ; 1227 } 1228 } 1229 } 1230 } 1231} 1232 1233static void hvx_mm_id_nx (unsigned int nth ,unsigned int ith ,void * data ) { 1234struct htp_mm_context * mmctx = (struct htp_mm_context * )data ; 1235struct htp_ops_context * octx = mmctx -> octx ; 1236dma_queue * dma_queue = octx -> ctx -> dma [ith ]; 1237const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 1238const uint32_t n_weights = kparams -> n_weights ; 1239const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 1240const struct htp_tensor * restrictact = octx -> src [n_weights ]; 1241const struct htp_tensor * restrictids = octx -> src [n_weights + 1 ]; 1242 1243hvx_mm_run_quant_task (mmctx ,ith ); 1244 1245struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 1246 1247const uint32_t n_prefetch = kparams -> n_prefetch ; 1248assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 1249 1250const uint32_t n_as = src0 -> ne [2 ]; 1251 1252const uint32_t * matrix_row_counts = mmctx -> matrix_row_counts ; 1253const struct mmid_row_mapping * matrix_rows = mmctx -> matrix_rows ; 1254 1255const size_t src1_stride = mmctx -> vtcm_src1_stride ; 1256 1257uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 1258uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 1259 1260for (uint32_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 1261const int32_t cne1 = matrix_row_counts [cur_a ]; 1262if (cne1 == 0 )continue ; 1263 1264for (uint32_t p = 0 ;p < n_weights ;++ p ) { 1265const struct htp_tensor * restrictsrc_w = octx -> src [p ]; 1266const struct htp_tensor * restrictdst = octx -> dsts [p ]; 1267if (!src_w || !dst )continue ; 1268 1269const uint32_t ne01 = src_w -> ne [1 ]; 1270uint32_t start_row = 0 ; 1271uint32_t end_row = ne01 ; 1272if (octx -> ctx -> mdev .count > 1 ) { 1273const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 1274const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (ne01 ,can_split ?32 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 1275start_row = range .start ; 1276end_row = range .start + range .count ; 1277 } 1278 1279const uint32_t nrows = end_row - start_row ; 1280uint32_t src0_nrows_per_thread = fastdiv (nrows + nth - 1 ,& octx -> n_threads_div ); 1281src0_nrows_per_thread = hex_round_up (src0_nrows_per_thread ,32 ); 1282 1283const uint32_t src0_start_row = start_row + src0_nrows_per_thread * ith ; 1284const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,end_row ); 1285if (src0_start_row >=src0_end_row )continue ; 1286 1287const uint8_t * src0_row = (const uint8_t * )src_w -> data + cur_a * src_w -> nb [2 ]; 1288 1289const uint32_t tile_size = htp_mm_get_weight_tile_size (src_w -> type ); 1290const uint32_t aligned_tile_size = htp_mm_get_weight_aligned_tile_size (src_w -> type ); 1291const uint32_t n_k_tiles_w = src_w -> ne [0 ] /32 ; 1292const uint32_t n_k_tiles_a = act -> ne [0 ] /32 ; 1293const uint32_t tile_row_stride = n_k_tiles_w * tile_size ; 1294const uint32_t tile_row_transfer_size_aligned = n_k_tiles_a * aligned_tile_size ; 1295 1296const uint32_t ct_start = src0_start_row /32 ; 1297const uint32_t ct_end = (src0_end_row + 31 ) /32 ; 1298 1299uint32_t push_ct = ct_start ; 1300for (uint32_t d = 0 ;d < n_prefetch && push_ct < ct_end ;d ++ ,push_ct ++ ) { 1301dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + d * tile_row_transfer_size_aligned ,src0_row + push_ct * tile_row_stride ), 1302aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1303 } 1304 1305for (uint32_t ct = ct_start ;ct < ct_end ;ct ++ ) { 1306const uint8_t * w_tile = dma_queue_pop (dma_queue ).dst ; 1307 1308int valid_rows = (int )src_w -> ne [1 ]- (int )(ct * 32 ); 1309valid_rows = MIN (32 ,MAX (0 ,valid_rows )); 1310 1311htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1312for (uint32_t cid = 0 ;cid < (uint32_t )cne1 ;++ cid ) { 1313struct mmid_row_mapping row_mapping = MMID_MATRIX_ROW (cur_a ,cid ); 1314const int rm1 = row_mapping .i1 ; 1315const int rm2 = row_mapping .i2 ; 1316 1317const uint32_t ir1 = fastmodulo (rm1 ,act -> ne [1 ],& mmctx -> mm_div_ne11 ); 1318const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + (ir1 + rm2 * act -> ne [1 ])* src1_stride ); 1319float * restrictdst_row = (float * ) (dst -> data + (rm1 * dst -> nb [1 ]+ rm2 * dst -> nb [2 ])); 1320 1321mmctx -> vec_dot_32x1 (act -> ne [0 ],& dst_row [ct * 32 ],w_tile ,src1_col ,valid_rows ,NULL ); 1322 } 1323htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ct ); 1324 1325if (push_ct < ct_end ) { 1326dma_queue_push (dma_queue ,dma_make_ptr ((uint8_t * )w_tile ,src0_row + push_ct * tile_row_stride ), 1327aligned_tile_size ,tile_size ,tile_size ,n_k_tiles_a ); 1328push_ct ++ ; 1329 } 1330 } 1331 } 1332 } 1333} 1334 1335static int hvx_mm_init_vec_dot (struct htp_mm_context * mmctx ,enum htp_data_type type ) { 1336switch (type ) { 1337case HTP_TYPE_Q4_0 : 1338mmctx -> type = "q4_0_tiled-f32" ; 1339mmctx -> vec_dot_32x1 = tiled_vec_dot_q4_0_32x1 ; 1340return 0 ; 1341case HTP_TYPE_Q4_1 : 1342mmctx -> type = "q4_1_tiled-f32" ; 1343mmctx -> vec_dot_32x1 = tiled_vec_dot_q4_1_32x1 ; 1344return 0 ; 1345case HTP_TYPE_Q8_0 : 1346mmctx -> type = "q8_0_tiled-f32" ; 1347mmctx -> vec_dot_32x1 = tiled_vec_dot_q8_0_32x1 ; 1348return 0 ; 1349case HTP_TYPE_IQ4_NL : 1350mmctx -> type = "iq4nl_tiled-f32" ; 1351mmctx -> vec_dot_32x1 = tiled_vec_dot_iq4nl_32x1 ; 1352return 0 ; 1353case HTP_TYPE_MXFP4 : 1354mmctx -> type = "mxfp4_tiled-f32" ; 1355mmctx -> vec_dot_32x1 = tiled_vec_dot_mxfp4_32x1 ; 1356return 0 ; 1357default : 1358return -1 ; 1359 } 1360} 1361 1362static int hvx_mm_matmul (struct htp_ops_context * octx ) { 1363htp_matmul_tensors_preamble ; 1364 1365struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 1366htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 1367 1368struct htp_mm_context mmctx_struct = {0 }; 1369struct htp_mm_context * mmctx = & mmctx_struct ; 1370mmctx -> octx = octx ; 1371mmctx -> act = src1 ; 1372 1373const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 1374 1375const uint32_t src0_nrows = ne01 ; 1376const uint32_t src1_nrows = ne11 * ne12 * ne13 ; 1377 1378uint32_t src0_row_start = 0 ; 1379uint32_t src0_row_end = src0_nrows ; 1380 1381if (octx -> ctx -> mdev .count > 1 ) { 1382const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 1383const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (src0_nrows ,can_split ?32 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 1384src0_row_start = range .start ; 1385src0_row_end = range .start + range .count ; 1386 } 1387 1388if (src0_row_start >=src0_row_end ) { 1389return HTP_STATUS_OK ; 1390 } 1391 1392const uint32_t nrows = src0_row_end - src0_row_start ; 1393mmctx -> src0_row_start = src0_row_start ; 1394mmctx -> src0_row_end = src0_row_end ; 1395 1396bool is_repacked = (src0 -> type == HTP_TYPE_Q4_0 || src0 -> type == HTP_TYPE_Q4_1 || 1397src0 -> type == HTP_TYPE_Q8_0 || src0 -> type == HTP_TYPE_IQ4_NL || 1398src0 -> type == HTP_TYPE_MXFP4 ); 1399 1400// Compute src0_nrows_per_thread 1401mmctx -> src0_nrows_per_thread = fastdiv (nrows + octx -> n_threads - 1 ,& octx -> n_threads_div ); 1402if (is_repacked ) { 1403mmctx -> src0_nrows_per_thread = hex_round_up (mmctx -> src0_nrows_per_thread ,32 ); 1404 }else { 1405mmctx -> src0_nrows_per_thread += (mmctx -> src0_nrows_per_thread & 1 );// round up to even 1406 } 1407 1408const size_t src0_row_size = nb01 ; 1409const size_t dst_row_size = nb1 ; 1410size_t src1_row_size = nb11 ; 1411 1412const size_t src0_row_size_padded = hex_round_up (src0_row_size ,128 ); 1413size_t src1_row_size_padded ; 1414 1415worker_callback_t quant_task_func ; 1416worker_callback_t matmul_job_func ; 1417uint32_t n_quant_tasks = 1 ; 1418if (src1_nrows > 1 ) { 1419if (is_repacked ) { 1420switch (src0 -> type ) { 1421case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mm_2d_repacked_q4_0 ;break ; 1422case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mm_2d_repacked_q4_1 ;break ; 1423case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mm_2d_repacked_q8_0 ;break ; 1424case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mm_2d_repacked_iq4nl ;break ; 1425case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mm_2d_repacked_mxfp4 ;break ; 1426default :return HTP_STATUS_NO_SUPPORT ; 1427 } 1428 }else { 1429matmul_job_func = hvx_mm_2d ; 1430 } 1431 }else { 1432if (is_repacked ) { 1433switch (src0 -> type ) { 1434case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mv_2d_repacked_q4_0 ;break ; 1435case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mv_2d_repacked_q4_1 ;break ; 1436case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mv_2d_repacked_q8_0 ;break ; 1437case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mv_2d_repacked_iq4nl ;break ; 1438case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mv_2d_repacked_mxfp4 ;break ; 1439default :return HTP_STATUS_NO_SUPPORT ; 1440 } 1441 }else { 1442matmul_job_func = hvx_mv_2d ; 1443 } 1444 } 1445 1446bool need_quant = true; 1447 1448switch (kparams -> kernel_type ) { 1449case HTP_MM_KERNEL_HVX_F16_F16_VTCM : 1450quant_task_func = (src1 -> type == HTP_TYPE_F32 ) ?quantize_f32_f16_flat :quantize_f16_f16_flat ; 1451mmctx -> type = "f16-f16" ; 1452mmctx -> vec_dot_1x1 = vec_dot_f16_f16_aa_1x1 ; 1453mmctx -> vec_dot_2x1 = vec_dot_f16_f16_aa_2x1 ; 1454mmctx -> vec_dot_2x2 = vec_dot_f16_f16_aa_2x2 ; 1455src1_row_size = hex_round_up (ne10 * 2 ,128 ); 1456break ; 1457 1458case HTP_MM_KERNEL_HVX_F16_F32_DDR : 1459mmctx -> type = "f16-f32" ; 1460mmctx -> vec_dot_1x1 = vec_dot_f16_f32_uu_1x1 ; 1461matmul_job_func = hvx_mm_4d ; 1462mmctx -> mm_div_ne12_ne1 = kparams -> div_ne12_ne1 ; 1463mmctx -> mm_div_ne1 = kparams -> div_ne1 ; 1464mmctx -> mm_div_r2 = kparams -> div_r2 ; 1465mmctx -> mm_div_r3 = kparams -> div_r3 ; 1466need_quant = false; 1467quant_task_func = NULL ; 1468src1_row_size = nb11 ; 1469break ; 1470 1471case HTP_MM_KERNEL_HVX_F16_F16_DDR : 1472mmctx -> type = "f16-f16" ; 1473mmctx -> vec_dot_1x1 = vec_dot_f16_f16_uu_1x1 ; 1474matmul_job_func = hvx_mm_4d ; 1475mmctx -> mm_div_ne12_ne1 = kparams -> div_ne12_ne1 ; 1476mmctx -> mm_div_ne1 = kparams -> div_ne1 ; 1477mmctx -> mm_div_r2 = kparams -> div_r2 ; 1478mmctx -> mm_div_r3 = kparams -> div_r3 ; 1479src1_row_size = nb11 ; 1480need_quant = false; 1481quant_task_func = NULL ; 1482break ; 1483 1484case HTP_MM_KERNEL_HVX_F32_F32_VTCM : 1485quant_task_func = quantize_f32_f32_flat ; 1486mmctx -> type = "f32-f32" ; 1487mmctx -> vec_dot_1x1 = vec_dot_f32_f32_aa_1x1 ; 1488mmctx -> vec_dot_2x1 = vec_dot_f32_f32_aa_2x1 ; 1489mmctx -> vec_dot_2x2 = vec_dot_f32_f32_aa_2x2 ; 1490src1_row_size = hex_round_up (ne10 * 4 ,128 ); 1491break ; 1492 1493case HTP_MM_KERNEL_HVX_F32_F32_DDR : 1494quant_task_func = NULL ; 1495mmctx -> type = "f32-f32" ; 1496mmctx -> vec_dot_1x1 = vec_dot_f32_f32_uu_1x1 ; 1497mmctx -> mm_div_ne12_ne1 = kparams -> div_ne12_ne1 ; 1498mmctx -> mm_div_ne1 = kparams -> div_ne1 ; 1499mmctx -> mm_div_r2 = kparams -> div_r2 ; 1500mmctx -> mm_div_r3 = kparams -> div_r3 ; 1501src1_row_size = nb11 ; 1502need_quant = false; 1503matmul_job_func = hvx_mm_4d ; 1504break ; 1505 1506case HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT : { 1507n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 1508quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_flat :quantize_f32_q8_0_flat ; 1509src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_flat_row_size (ne10 ) :htp_mm_q8_0_flat_row_size (ne10 ); 1510 1511if (src1_nrows > 1 ) { 1512switch (src0 -> type ) { 1513case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mm_2d_repacked_q4_0_flat ;break ; 1514case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mm_2d_repacked_q4_1_flat ;break ; 1515case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mm_2d_repacked_q8_0_flat ;break ; 1516case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mm_2d_repacked_iq4nl_flat ;break ; 1517case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mm_2d_repacked_mxfp4_flat ;break ; 1518default :return HTP_STATUS_NO_SUPPORT ; 1519 } 1520 }else { 1521switch (src0 -> type ) { 1522case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mv_2d_repacked_q4_0_flat ;break ; 1523case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mv_2d_repacked_q4_1_flat ;break ; 1524case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mv_2d_repacked_q8_0_flat ;break ; 1525case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mv_2d_repacked_iq4nl_flat ;break ; 1526case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mv_2d_repacked_mxfp4_flat ;break ; 1527default :return HTP_STATUS_NO_SUPPORT ; 1528 } 1529 } 1530break ; 1531 } 1532 1533case HTP_MM_KERNEL_HVX_QUANT_BLOCK : 1534case HTP_MM_KERNEL_HVX_QUANT_ROW : 1535default : 1536if (hvx_mm_init_vec_dot (mmctx ,src0 -> type )!= 0 ) { 1537return HTP_STATUS_NO_SUPPORT ; 1538 } 1539 1540const uint32_t qk = QK_Q8_0_TILED ; 1541const uint32_t nb = (ne10 + qk - 1 ) /qk ; 1542const uint32_t total_nb = src1_nrows * nb ; 1543 1544if (src1_nrows < octx -> n_threads ) { 1545n_quant_tasks = MIN (total_nb ,octx -> n_threads ); 1546quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled_block :quantize_f32_q8_0_tiled_block ; 1547for (uint32_t ith = 0 ;ith < n_quant_tasks ;++ ith ) { 1548uint32_t ib_first = (total_nb * ith ) /n_quant_tasks ; 1549uint32_t ib_last = (total_nb * (ith + 1 )) /n_quant_tasks ; 1550mmctx -> quant_ib_first [ith ]= ib_first ; 1551mmctx -> quant_ib_last [ith ]= ib_last ; 1552mmctx -> quant_r [ith ]= ib_first /nb ; 1553mmctx -> quant_c [ith ]= ib_first %nb ; 1554 } 1555 }else { 1556n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 1557quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled :quantize_f32_q8_0_tiled ; 1558 } 1559src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 1560break ; 1561 } 1562 1563struct htp_mm_hvx_vtcm_layout L ; 1564htp_mm_hvx_vtcm_layout_build (& L ,kparams -> kernel_type ,src0 -> type ,ne10 ,src1_nrows ,octx -> n_threads , 1565dst_row_size ,src0_row_size ,src1_row_size ,src2 ?src2 -> nb [1 ] :0 ,kparams -> n_prefetch , false, false); 1566 1567if (kparams -> kernel_type == HTP_MM_KERNEL_HVX_F16_F16_VTCM || 1568kparams -> kernel_type == HTP_MM_KERNEL_HVX_F32_F32_VTCM || 1569kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || 1570kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK ) { 1571mmctx -> vtcm_src1_size_per_thread = L .src1_bytes ; 1572 }else { 1573mmctx -> vtcm_src1_size_per_thread = fastdiv (L .src1_bytes ,& octx -> n_threads_div ); 1574 } 1575 1576mmctx -> vtcm_src0_size_per_thread = fastdiv (L .src0_bytes ,& octx -> n_threads_div ); 1577mmctx -> vtcm_dst_size_per_thread = fastdiv (L .dst_bytes ,& octx -> n_threads_div ); 1578 1579const size_t vtcm_size = L .total_bytes ; 1580 1581FARF (HIGH ,"matmul-%s : src0-vtcm-size %zu src1-vtcm-size %zu dst-vtcm-size %zu (%zu)\n" ,mmctx -> type , 1582L .src0_bytes ,L .src1_bytes ,L .dst_bytes ,vtcm_size ); 1583 1584FARF (HIGH ,"matmul-%s : %ux%ux%ux%u * %ux%ux%ux%u-> %ux%ux%ux%u (0x%p, 0x%p, 0x%p)\n" ,mmctx -> type ,src0 -> ne [0 ], 1585src0 -> ne [1 ],src0 -> ne [2 ],src0 -> ne [3 ],src1 -> ne [0 ],src1 -> ne [1 ],src1 -> ne [2 ],src1 -> ne [3 ],dst -> ne [0 ], 1586dst -> ne [1 ],dst -> ne [2 ],dst -> ne [3 ],src0 -> data ,src1 -> data ,dst -> data ); 1587 1588if (octx -> ctx -> vtcm_size < vtcm_size ) { 1589FARF (ERROR ,"matmul-%s : current VTCM reservation %zu is too small, needed %zu\n" ,mmctx -> type , 1590octx -> ctx -> vtcm_size ,vtcm_size ); 1591return HTP_STATUS_VTCM_TOO_SMALL ; 1592 } 1593 1594uint8_t * const base = (uint8_t * )octx -> ctx -> vtcm_base ; 1595mmctx -> vtcm_src1 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src1 ); 1596mmctx -> vtcm_src0 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src0 ); 1597mmctx -> vtcm_src2 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src2 ); 1598mmctx -> vtcm_dst = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_dst ); 1599 1600octx -> src1_spad .src = NULL ; 1601octx -> src0_spad .src = NULL ; 1602octx -> dst_spad .src = NULL ; 1603 1604mmctx -> vtcm_src0_stride = src0_row_size_padded ; 1605mmctx -> vtcm_src1_stride = src1_row_size ; 1606 1607if (need_quant ) { 1608mmctx -> n_quant_rows_per_thread = (src1_nrows + n_quant_tasks - 1 ) /n_quant_tasks ; 1609mmctx -> quant_task_func = quant_task_func ; 1610mmctx -> n_quant_tasks = n_quant_tasks ; 1611atomic_init (& mmctx -> quant_barrier ,n_quant_tasks ); 1612 }else { 1613mmctx -> quant_task_func = NULL ; 1614mmctx -> n_quant_tasks = 0 ; 1615 } 1616 1617htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 1618 1619worker_pool_run_func (octx -> ctx -> worker_pool ,matmul_job_func ,mmctx ,octx -> n_threads ); 1620 1621return HTP_STATUS_OK ; 1622} 1623 1624static void hvx_mm_nx_2d (unsigned int nth ,unsigned int ith ,void * data ) { 1625struct htp_mm_context * mmctx = data ; 1626struct htp_ops_context * octx = mmctx -> octx ; 1627const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 1628const uint32_t n_weights = kparams -> n_weights ; 1629 1630const struct htp_tensor * restrictact = octx -> src [n_weights ]; 1631const uint32_t src1_nrows = act -> ne [1 ]* act -> ne [2 ]* act -> ne [3 ]; 1632const size_t src1_stride = mmctx -> vtcm_src1_stride ; 1633 1634uint8_t * restrictvtcm_src0_ptr = mmctx -> vtcm_src0 + mmctx -> vtcm_src0_size_per_thread * ith ; 1635uint8_t * restrictsrc1_data = mmctx -> vtcm_src1 ; 1636 1637dma_queue * dma_queue = octx -> ctx -> dma [ith ]; 1638const uint32_t n_prefetch = kparams -> n_prefetch ; 1639assert (n_prefetch >=2 && n_prefetch <=HTP_MM_MAX_PREFETCH && (n_prefetch & (n_prefetch - 1 ))== 0 ); 1640const uint32_t prefetch_mask = n_prefetch - 1 ; 1641 1642struct htp_thread_trace * tr = & octx -> ctx -> trace [ith ]; 1643 1644hvx_mm_run_quant_task (mmctx ,ith ); 1645 1646for (uint32_t widx = 0 ;widx < n_weights ;widx ++ ) { 1647const struct htp_tensor * restrictsrc_w = octx -> src [widx ]; 1648const struct htp_tensor * restrictdst = octx -> dsts [widx ]; 1649if (!src_w || !dst )continue ; 1650 1651const uint32_t ne00 = src_w -> ne [0 ]; 1652const uint32_t ne01 = src_w -> ne [1 ]; 1653uint32_t start_row = 0 ; 1654uint32_t end_row = ne01 ; 1655if (octx -> ctx -> mdev .count > 1 ) { 1656const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 1657const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (ne01 ,can_split ?32 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 1658start_row = range .start ; 1659end_row = range .start + range .count ; 1660 } 1661 1662const uint32_t nrows = end_row - start_row ; 1663uint32_t src0_nrows_per_thread = fastdiv (nrows + nth - 1 ,& octx -> n_threads_div ); 1664src0_nrows_per_thread += (src0_nrows_per_thread & 1 ); 1665 1666const uint32_t src0_start_row = start_row + src0_nrows_per_thread * ith ; 1667const uint32_t src0_end_row = MIN (src0_start_row + src0_nrows_per_thread ,end_row ); 1668const uint32_t src0_end_row_x2 = src0_start_row + ((src0_end_row - src0_start_row )& ~1U ); 1669if (src0_start_row >=src0_end_row )continue ; 1670 1671const size_t dst_row_size = dst -> nb [1 ]; 1672const size_t src0_row_size = src_w -> nb [1 ]; 1673const size_t src0_stride = hex_round_up (src0_row_size ,128 ); 1674 1675const uint8_t * restrictsrc0_row = (const uint8_t * )src_w -> data ; 1676 1677for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 1678const int is0 = (ir0 - src0_start_row ); 1679if (is0 >= (int )n_prefetch )break ; 1680dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 1681src0_stride ,src0_row_size ,src0_row_size ,2 ); 1682 } 1683 1684for (uint32_t ir0 = src0_start_row ;ir0 < src0_end_row_x2 ;ir0 += 2 ) { 1685const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 1686htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 1687uint32_t ir1 = 0 ; 1688for (;ir1 + 1 < src1_nrows ;ir1 += 2 ) { 1689const uint8_t * restrictsrc1_col0 = (const uint8_t * ) (src1_data + (ir1 + 0 )* src1_stride ); 1690const uint8_t * restrictsrc1_col1 = (const uint8_t * ) (src1_data + (ir1 + 1 )* src1_stride ); 1691float * restrictdst_row0 = (float * ) (dst -> data + ((ir1 + 0 )* dst_row_size )); 1692float * restrictdst_row1 = (float * ) (dst -> data + ((ir1 + 1 )* dst_row_size )); 1693mmctx -> vec_dot_2x2 (ne00 ,& dst_row0 [ir0 ],& dst_row1 [ir0 ],ss0 ,ss0 + src0_stride ,src1_col0 ,src1_col1 ); 1694 } 1695for (;ir1 < src1_nrows ;++ ir1 ) { 1696const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + ir1 * src1_stride ); 1697float * restrictdst_row = (float * ) (dst -> data + (ir1 * dst_row_size )); 1698mmctx -> vec_dot_2x1 (ne00 ,& dst_row [ir0 ],ss0 ,ss0 + src0_stride ,src1_col ); 1699 } 1700htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 1701 1702const int pr0 = (ir0 + n_prefetch ); 1703const int is0 = (pr0 - src0_start_row )& prefetch_mask ; 1704if (pr0 < src0_end_row_x2 ) { 1705dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + pr0 * src0_row_size ), 1706src0_stride ,src0_row_size ,src0_row_size ,2 ); 1707 } 1708 } 1709 1710if (src0_end_row != src0_end_row_x2 ) { 1711uint32_t ir0 = src0_end_row_x2 ; 1712const int is0 = (ir0 - src0_start_row )& prefetch_mask ; 1713dma_queue_push (dma_queue ,dma_make_ptr (vtcm_src0_ptr + is0 * src0_stride ,src0_row + ir0 * src0_row_size ), 1714src0_stride ,src0_row_size ,src0_row_size ,1 ); 1715const uint8_t * ss0 = dma_queue_pop (dma_queue ).dst ; 1716htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 1717for (uint32_t ir1 = 0 ;ir1 < src1_nrows ;++ ir1 ) { 1718const uint8_t * restrictsrc1_col = (const uint8_t * ) (src1_data + ir1 * src1_stride ); 1719float * restrictdst_row = (float * ) (dst -> data + (ir1 * dst_row_size )); 1720mmctx -> vec_dot_1x1 (ne00 ,& dst_row [ir0 ],ss0 ,src1_col ); 1721 } 1722htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_COMP ,ir0 ); 1723 } 1724 } 1725} 1726 1727#define DEQUANTIZE_WORKER_LOOP_IMPL (SUFFIX ) \ 1728static void dequantize_tiled_worker_loop_##SUFFIX(unsigned int n, unsigned int i, void *data) { \ 1729 tiled_dequantize_state_t *state = (tiled_dequantize_state_t *)data; \ 1730 struct htp_thread_trace * tr = &state->traces[i]; \ 1731 htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_W_DEQUANT, i); \ 1732 for (unsigned int task_id = i; task_id < (unsigned int)state->n_tasks; task_id += n) { \ 1733 int start = task_id * state->n_tiles_per_task; \ 1734 int end = hex_smin(start + state->n_tiles_per_task, state->n_tot_tiles); \ 1735 dequantize_tiled_weight_to_fp16_task_##SUFFIX(state, start, end); \ 1736 } \ 1737 htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_W_DEQUANT, i); \ 1738} 1739 1740DEQUANTIZE_WORKER_LOOP_IMPL (q4_0 ) 1741DEQUANTIZE_WORKER_LOOP_IMPL (q4_1 ) 1742DEQUANTIZE_WORKER_LOOP_IMPL (iq4_nl ) 1743DEQUANTIZE_WORKER_LOOP_IMPL (mxfp4 ) 1744DEQUANTIZE_WORKER_LOOP_IMPL (q8_0 ) 1745 1746static void convert_f16_worker_loop (unsigned int n ,unsigned int i ,void * data ) { 1747tiled_dequantize_state_t * state = (tiled_dequantize_state_t * )data ; 1748struct htp_thread_trace * tr = & state -> traces [i ]; 1749htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_W_DEQUANT ,i ); 1750for (unsigned int task_id = i ;task_id < (unsigned int )state -> n_tasks ;task_id += n ) { 1751int start = task_id * state -> n_tiles_per_task ; 1752int end = hex_smin (start + state -> n_tiles_per_task ,state -> n_tot_tiles ); 1753convert_f16_weight_to_fp16_tiles_task (state ,start ,end ); 1754 } 1755htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_W_DEQUANT ,i ); 1756} 1757 1758static void quantize_f32_worker_loop (unsigned int n ,unsigned int i ,void * data ) { 1759tiled_dequantize_state_t * state = (tiled_dequantize_state_t * )data ; 1760 1761struct htp_thread_trace * tr = & state -> traces [i ]; 1762htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,i ); 1763 1764for (unsigned int task_id = i ;task_id < (unsigned int )state -> n_tasks ;task_id += n ) { 1765int start = task_id * state -> n_tiles_per_task ; 1766int end = hex_smin (start + state -> n_tiles_per_task ,state -> n_tot_tiles ); 1767quantize_f32_weight_to_fp16_tiles_task (state ,start ,end ); 1768 } 1769 1770htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_QUANT ,i ); 1771} 1772 1773static void transfer_output_chunk_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 1774output_transfer_task_state_t * st = (output_transfer_task_state_t * )data ; 1775 1776struct htp_thread_trace * tr = & st -> traces [i ]; 1777 1778int start_chunk_idx = i * st -> n_chunks_per_task ; 1779htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC ,start_chunk_idx ); 1780 1781for (unsigned int task_id = i ;task_id < (unsigned int )st -> n_tasks ;task_id += n ) { 1782int chunk_idx = task_id * st -> n_chunks_per_task ; 1783size_t chunk_size = hex_smin (st -> n_tot_chunks - chunk_idx ,st -> n_chunks_per_task ); 1784 1785float * dst = st -> dst + chunk_idx * st -> dst_stride ; 1786const float * src2 = st -> src2 ? (st -> src2 + chunk_idx * st -> src2_stride ) :NULL ; 1787transfer_output_chunk_fp16_to_fp32 (dst ,src2 ,st -> vtcm_src ,chunk_idx ,chunk_size ,st -> n_cols ,st -> dst_stride ,st -> src2_stride ,st -> dst_cols ); 1788 } 1789 1790htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC ,start_chunk_idx ); 1791} 1792 1793typedef struct { 1794struct htp_context * ctx ; 1795struct htp_thread_trace * traces ; 1796__fp16 * dst ; 1797const float * src ; 1798const struct mmid_row_mapping * matrix_rows ; 1799float * vtcm_f32_act ; 1800uint32_t n_tasks ; 1801uint32_t n_tot_chunks ; 1802uint32_t n_chunks_per_task ; 1803uint32_t k_block ; 1804uint32_t k_stride ; 1805uint32_t k_valid ; 1806size_t vtcm_f32_act_bytes_per_thread ; 1807uint32_t dma_step_rows ; 1808uint32_t dma_step_rows_shift ; 1809}activation_transfer_task_state_t ; 1810 1811typedef struct { 1812struct htp_context * ctx ; 1813struct htp_thread_trace * traces ; 1814__fp16 * dst ; 1815const float * src ; 1816float * vtcm_f32_act ; 1817uint32_t n_rows ; 1818uint32_t k_block ; 1819uint32_t k_stride ; 1820uint32_t k_valid ; 1821uint32_t n_col_chunks ; 1822struct fastdiv_values n_threads_div ; 1823size_t vtcm_f32_act_bytes ; 1824uint32_t dma_step_rows ; 1825uint32_t dma_step_rows_shift ; 1826}activation_transfer_col_chunk_state_t ; 1827 1828static void transfer_activation_chunk_fp32_to_fp16_dma_pipelined_col_chunk ( 1829dma_queue * dma_q , 1830__fp16 * restrictvtcm_dst , 1831const float * restrictsrc , 1832uint32_t n_rows , 1833uint32_t k_block , 1834uint32_t k_stride , 1835uint32_t k_chunk_valid , 1836uint32_t c_first , 1837uint32_t c_len , 1838float * thread_f32_act , 1839struct htp_thread_trace * tr , 1840uint32_t dma_step_rows , 1841uint32_t dma_step_rows_shift ) { 1842 1843const uint32_t R = dma_step_rows ; 1844const uint32_t n_rows_padded = hex_align_up (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 1845 1846const uint32_t n_steps = n_rows_padded >>dma_step_rows_shift ; 1847 1848// Push step 0 1849if (n_steps > 0 && n_rows > 0 ) { 1850uint32_t nrows_to_fetch = hex_smin (n_rows ,R ); 1851dma_queue_push (dma_q ,dma_make_ptr (thread_f32_act ,src + c_first ), 1852c_len * sizeof (float ),k_stride * sizeof (float ),k_chunk_valid * sizeof (float ),nrows_to_fetch ); 1853 } 1854// Push step 1 1855if (n_steps > 1 ) { 1856uint32_t next_r = R * 1 ; 1857if (next_r < n_rows ) { 1858uint32_t nrows_to_fetch = hex_smin (n_rows - next_r ,R ); 1859const float * next_src = src + next_r * k_stride + c_first ; 1860float * next_buf = thread_f32_act + 1 * R * c_len ; 1861dma_queue_push (dma_q ,dma_make_ptr (next_buf ,next_src ), 1862c_len * sizeof (float ),k_stride * sizeof (float ),k_chunk_valid * sizeof (float ),nrows_to_fetch ); 1863 } 1864 } 1865for (uint32_t s = 0 ;s < n_steps ;++ s ) { 1866uint32_t r = s <<dma_step_rows_shift ; 1867float * curr_buf = thread_f32_act ; 1868 1869if (r < n_rows ) { 1870curr_buf = (float * )dma_queue_pop (dma_q ).dst ; 1871 } 1872 1873htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,r ); 1874for (uint32_t p = 0 ;p < (R >>1 );++ p ) { 1875uint32_t row_idx = r + (p <<1 ); 1876float * pair_buf = curr_buf + (p <<1 )* c_len ; 1877bool r0_valid = ((row_idx + 0 )< n_rows ); 1878bool r1_valid = ((row_idx + 1 )< n_rows ); 1879 1880transfer_activation_row_pair_fp32_to_fp16_col_chunk ( 1881vtcm_dst ,pair_buf ,pair_buf + c_len ,row_idx ,k_block ,c_first ,c_len ,k_chunk_valid ,r0_valid ,r1_valid 1882 ); 1883 } 1884htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,r ); 1885 1886// Push step s + 2 1887uint32_t next_s = s + 2 ; 1888uint32_t next_r = next_s <<dma_step_rows_shift ; 1889if (next_r < n_rows ) { 1890uint32_t nrows_to_fetch = hex_smin (n_rows - next_r ,R ); 1891const float * next_src = src + next_r * k_stride + c_first ; 1892dma_queue_push (dma_q ,dma_make_ptr (curr_buf ,next_src ), 1893c_len * sizeof (float ),k_stride * sizeof (float ),k_chunk_valid * sizeof (float ),nrows_to_fetch ); 1894 } 1895 } 1896} 1897 1898static void transfer_activation_chunk_fp32_to_fp16_col_chunk ( 1899__fp16 * restrictvtcm_dst , 1900const float * restrictsrc , 1901uint32_t n_rows , 1902uint32_t k_block , 1903uint32_t k_stride , 1904uint32_t c_first , 1905uint32_t c_len , 1906uint32_t k_chunk_valid ) { 1907const uint32_t n_rows_padded = hex_align_up (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 1908const uint32_t n_rows_tiled = (n_rows /HTP_MM_HMX_TILE_N_ROWS )* HTP_MM_HMX_TILE_N_ROWS ; 1909 1910uint32_t r = 0 ; 1911 1912#pragma unroll(2) 1913for (r = 0 ;r < n_rows_tiled ;r += 2 ) { 1914const float * ptr_in0 = src + (r + 0 )* k_stride + c_first ; 1915const float * ptr_in1 = src + (r + 1 )* k_stride + c_first ; 1916 1917transfer_activation_row_pair_fp32_to_fp16_col_chunk ( 1918vtcm_dst ,ptr_in0 ,ptr_in1 ,r ,k_block ,c_first ,c_len ,k_chunk_valid , true, true 1919 ); 1920 } 1921 1922for (;r < n_rows_padded ;r += 2 ) { 1923const bool row0_valid = r < n_rows ; 1924const bool row1_valid = (r + 1 )< n_rows ; 1925 1926const float * ptr_in0 = row0_valid ? (src + (r + 0 )* k_stride + c_first ) :NULL ; 1927const float * ptr_in1 = row1_valid ? (src + (r + 1 )* k_stride + c_first ) :NULL ; 1928 1929transfer_activation_row_pair_fp32_to_fp16_col_chunk ( 1930vtcm_dst ,ptr_in0 ,ptr_in1 ,r ,k_block ,c_first ,c_len ,k_chunk_valid ,row0_valid ,row1_valid 1931 ); 1932 } 1933} 1934 1935static void transfer_activation_chunk_col_chunk_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 1936activation_transfer_col_chunk_state_t * st = (activation_transfer_col_chunk_state_t * )data ; 1937struct htp_thread_trace * tr = & st -> traces [i ]; 1938 1939uint32_t n_blocks = st -> k_block /32 ; 1940uint32_t b_first = fastdiv (n_blocks * i ,& st -> n_threads_div ); 1941uint32_t b_last = fastdiv (n_blocks * (i + 1 ),& st -> n_threads_div ); 1942uint32_t c_first = b_first * 32 ; 1943uint32_t c_last = b_last * 32 ; 1944uint32_t c_len = c_last - c_first ; 1945 1946if (c_len == 0 ) { 1947return ; 1948 } 1949 1950uint32_t k_chunk_valid = 0 ; 1951if (st -> k_valid > c_first ) { 1952k_chunk_valid = hex_smin (st -> k_valid ,c_last )- c_first ; 1953 } 1954 1955__fp16 * dst = st -> dst ; 1956const float * src = st -> src ; 1957 1958if (st -> vtcm_f32_act ) { 1959size_t thread_scratch_bytes = hex_align_down (fastdiv (st -> vtcm_f32_act_bytes ,& st -> n_threads_div ),128 ); 1960float * thread_f32_act = (float * )((char * )st -> vtcm_f32_act + i * thread_scratch_bytes ); 1961 1962transfer_activation_chunk_fp32_to_fp16_dma_pipelined_col_chunk ( 1963st -> ctx -> dma [i ],dst ,src ,st -> n_rows ,st -> k_block ,st -> k_stride ,k_chunk_valid , 1964c_first ,c_len ,thread_f32_act ,tr ,st -> dma_step_rows ,st -> dma_step_rows_shift 1965 ); 1966 }else { 1967htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,c_first ); 1968transfer_activation_chunk_fp32_to_fp16_col_chunk ( 1969dst ,src ,st -> n_rows ,st -> k_block ,st -> k_stride ,c_first ,c_len ,k_chunk_valid 1970 ); 1971htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,c_first ); 1972 } 1973} 1974 1975static void transfer_activation_chunk_fp32_to_fp16_dma_pipelined ( 1976dma_queue * dma_q , 1977__fp16 * restrictvtcm_dst , 1978const float * restrictsrc , 1979uint32_t n_rows , 1980uint32_t k_block , 1981uint32_t k_stride , 1982uint32_t k_valid , 1983float * thread_f32_act , 1984struct htp_thread_trace * tr , 1985uint32_t dma_step_rows , 1986uint32_t dma_step_rows_shift ) { 1987 1988const uint32_t R = dma_step_rows ; 1989const uint32_t n_rows_padded = hex_align_up (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 1990 1991const uint32_t n_steps = n_rows_padded >>dma_step_rows_shift ; 1992 1993// Push step 0 1994if (n_steps > 0 && n_rows > 0 ) { 1995uint32_t nrows_to_fetch = hex_smin (n_rows ,R ); 1996dma_queue_push (dma_q ,dma_make_ptr (thread_f32_act ,src ), 1997k_block * sizeof (float ),k_stride * sizeof (float ),k_valid * sizeof (float ),nrows_to_fetch ); 1998 } 1999// Push step 1 (if valid) 2000if (n_steps > 1 ) { 2001uint32_t next_r = R * 1 ; 2002if (next_r < n_rows ) { 2003uint32_t nrows_to_fetch = hex_smin (n_rows - next_r ,R ); 2004const float * next_src = src + next_r * k_stride ; 2005float * next_buf = thread_f32_act + 1 * R * k_block ; 2006dma_queue_push (dma_q ,dma_make_ptr (next_buf ,next_src ), 2007k_block * sizeof (float ),k_stride * sizeof (float ),k_valid * sizeof (float ),nrows_to_fetch ); 2008 } 2009 } 2010for (uint32_t s = 0 ;s < n_steps ;++ s ) { 2011uint32_t r = s <<dma_step_rows_shift ; 2012float * curr_buf = thread_f32_act ; 2013 2014if (r < n_rows ) { 2015curr_buf = (float * )dma_queue_pop (dma_q ).dst ; 2016 } 2017 2018htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,r ); 2019for (uint32_t p = 0 ;p < (R >>1 );++ p ) { 2020uint32_t row_idx = r + (p <<1 ); 2021float * pair_buf = curr_buf + (p <<1 )* k_block ; 2022bool r0_valid = ((row_idx + 0 )< n_rows ); 2023bool r1_valid = ((row_idx + 1 )< n_rows ); 2024 2025transfer_activation_row_pair_fp32_to_fp16 (vtcm_dst ,pair_buf ,pair_buf + k_block ,row_idx ,k_block ,k_valid ,r0_valid ,r1_valid ); 2026 } 2027htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,r ); 2028 2029// Push step s + 2 2030uint32_t next_s = s + 2 ; 2031uint32_t next_r = next_s <<dma_step_rows_shift ; 2032if (next_r < n_rows ) { 2033uint32_t nrows_to_fetch = hex_smin (n_rows - next_r ,R ); 2034const float * next_src = src + next_r * k_stride ; 2035dma_queue_push (dma_q ,dma_make_ptr (curr_buf ,next_src ), 2036k_block * sizeof (float ),k_stride * sizeof (float ),k_valid * sizeof (float ),nrows_to_fetch ); 2037 } 2038 } 2039} 2040 2041static void transfer_activation_chunk_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 2042activation_transfer_task_state_t * st = (activation_transfer_task_state_t * )data ; 2043 2044struct htp_thread_trace * tr = & st -> traces [i ]; 2045 2046for (unsigned int task_id = i ;task_id < (unsigned int )st -> n_tasks ;task_id += n ) { 2047int chunk_idx = task_id * st -> n_chunks_per_task ; 2048size_t chunk_size = hex_smin (st -> n_tot_chunks - chunk_idx ,st -> n_chunks_per_task ); 2049 2050__fp16 * dst = st -> dst + chunk_idx * st -> k_block ; 2051const float * src = st -> src + chunk_idx * st -> k_stride ; 2052 2053if (st -> vtcm_f32_act ) { 2054float * thread_f32_act = (float * )((char * )st -> vtcm_f32_act + i * st -> vtcm_f32_act_bytes_per_thread ); 2055transfer_activation_chunk_fp32_to_fp16_dma_pipelined ( 2056st -> ctx -> dma [i ],dst ,src ,chunk_size ,st -> k_block ,st -> k_stride ,st -> k_valid ,thread_f32_act ,tr ,st -> dma_step_rows ,st -> dma_step_rows_shift 2057 ); 2058 }else { 2059htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2060transfer_activation_chunk_fp32_to_fp16 (dst ,src ,chunk_size ,st -> k_block ,st -> k_stride ,st -> k_valid ); 2061htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2062 } 2063 } 2064} 2065 2066typedef struct { 2067struct htp_thread_trace * traces ; 2068const struct mmid_row_mapping * matrix_rows ; 2069__fp16 * dst ; 2070const float * src ; 2071uint32_t n_tasks ; 2072uint32_t n_tot_chunks ; 2073uint32_t n_chunks_per_task ; 2074uint32_t k_block ; 2075uint32_t cur_a ; 2076uint32_t mapping_stride ; 2077uint32_t ne11 ; 2078struct fastdiv_values ne11_div ; 2079size_t nb11 ; 2080size_t nb12 ; 2081uint32_t start_row ; 2082uint32_t cne1 ; 2083uint32_t k_valid ; 2084}activation_transfer_gathered_task_state_t ; 2085 2086typedef struct { 2087struct htp_thread_trace * traces ; 2088const struct mmid_row_mapping * matrix_rows ; 2089const __fp16 * vtcm_src ; 2090float * dst ; 2091uint32_t n_tasks ; 2092uint32_t n_tot_chunks ; 2093uint32_t n_chunks_per_task ; 2094uint32_t n_cols ; 2095uint32_t cur_a ; 2096uint32_t mapping_stride ; 2097size_t dst_nb1 ; 2098size_t dst_nb2 ; 2099uint32_t start_row ; 2100uint32_t cne1 ; 2101}output_transfer_scattered_task_state_t ; 2102 2103static void transfer_activation_chunk_gathered_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 2104activation_transfer_gathered_task_state_t * st = data ; 2105struct htp_thread_trace * tr = & st -> traces [i ]; 2106int chunk_idx = i ; 2107int chunk_size = st -> n_chunks_per_task ; 2108int vtcm_start_row = chunk_idx * chunk_size ; 2109int start_row = st -> start_row + vtcm_start_row ; 2110int n_rows = hex_smin (st -> cne1 - start_row ,chunk_size ); 2111if (n_rows > 0 ) { 2112htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2113transfer_activation_chunk_fp32_to_fp16_gathered ( 2114st -> dst ,st -> src ,start_row ,vtcm_start_row ,n_rows ,st -> k_block , 2115st -> matrix_rows ,st -> cur_a ,st -> mapping_stride , 2116st -> ne11 ,& st -> ne11_div ,st -> nb11 ,st -> nb12 ,st -> cne1 ,st -> k_valid ); 2117htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2118 } 2119} 2120 2121static void transfer_activation_chunk_gathered_worker_flat_fn (unsigned int n ,unsigned int i ,void * data ) { 2122activation_transfer_gathered_task_state_t * st = data ; 2123struct htp_thread_trace * tr = & st -> traces [i ]; 2124int chunk_idx = i ; 2125int chunk_size = st -> n_chunks_per_task ; 2126int vtcm_start_row = chunk_idx * chunk_size ; 2127int start_row = st -> start_row + vtcm_start_row ; 2128int n_rows = hex_smin (st -> cne1 - start_row ,chunk_size ); 2129if (n_rows > 0 ) { 2130htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2131transfer_activation_chunk_fp32_to_fp16_gathered_flat ( 2132st -> dst ,st -> src ,start_row ,vtcm_start_row ,n_rows ,st -> k_block , 2133st -> matrix_rows ,st -> cur_a ,st -> mapping_stride , 2134st -> nb12 ,st -> cne1 ,st -> k_valid ); 2135htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_A_PREP ,chunk_idx ); 2136 } 2137} 2138 2139static void transfer_output_chunk_scattered_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 2140output_transfer_scattered_task_state_t * st = data ; 2141struct htp_thread_trace * tr = & st -> traces [i ]; 2142int chunk_idx = i ; 2143int chunk_size = st -> n_chunks_per_task ; 2144int vtcm_start_row = chunk_idx * chunk_size ; 2145int start_row = st -> start_row + vtcm_start_row ; 2146int n_rows = hex_smin (st -> cne1 - start_row ,chunk_size ); 2147if (n_rows > 0 ) { 2148htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC ,chunk_idx ); 2149transfer_output_chunk_fp16_to_fp32_scattered ( 2150st -> dst ,st -> vtcm_src ,start_row ,vtcm_start_row ,n_rows ,st -> n_cols , 2151st -> matrix_rows ,st -> cur_a ,st -> mapping_stride , 2152st -> dst_nb1 ,st -> dst_nb2 ,st -> cne1 ); 2153htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC ,chunk_idx ); 2154 } 2155} 2156 2157// --- HMX Dispatchers & Entry Points --- 2158 2159static void dequantize_tiled_weight_chunk_to_fp16_tiles ( 2160struct htp_context * ctx ,__fp16 * vtcm_dst , 2161const void * weight_src_ddr , 2162int n_cols ,int k_block , 2163size_t row_stride ,int weight_type , 2164int n_k_tiles ,struct fastdiv_values n_k_tiles_div , 2165worker_callback_t dequant_worker_fn ,int n_threads ) { 2166 2167assert (n_cols %HTP_MM_HMX_TILE_N_COLS == 0 ); 2168assert (k_block %HTP_MM_HMX_TILE_N_COLS == 0 ); 2169 2170size_t n_col_tiles = n_cols /HTP_MM_HMX_TILE_N_COLS ; 2171size_t n_tot_tiles = n_col_tiles * n_k_tiles ; 2172 2173size_t n_tiles_per_task = (n_threads == 1 ) ?n_tot_tiles :hmx_ceil_div (n_tot_tiles ,n_threads ); 2174 2175tiled_dequantize_state_t state ; 2176state .n_tasks = (n_tot_tiles + n_tiles_per_task - 1 ) /n_tiles_per_task ; 2177state .n_tot_tiles = n_tot_tiles ; 2178state .n_tiles_per_task = n_tiles_per_task ; 2179state .dst = vtcm_dst ; 2180state .src = (const uint8_t * )weight_src_ddr ; 2181state .n_cols = n_cols ; 2182state .k_block = k_block ; 2183state .row_stride = row_stride ; 2184state .weight_type = weight_type ; 2185state .n_k_tiles = n_k_tiles ; 2186state .n_k_tiles_div = n_k_tiles_div ; 2187state .traces = ctx -> trace ; 2188state .ctx = ctx ; 2189 2190state .tile_size = htp_mm_get_weight_tile_size (weight_type ); 2191state .aligned_tile_size = htp_mm_get_weight_aligned_tile_size (weight_type ); 2192 2193if (state .n_tasks == 1 || n_threads == 1 ) { 2194dequant_worker_fn (1 ,0 ,& state ); 2195 }else { 2196int n_tasks = hex_smin ((int )state .n_tasks ,n_threads ); 2197worker_pool_run_func (ctx -> worker_pool ,dequant_worker_fn ,& state ,n_tasks ); 2198 } 2199} 2200 2201typedef struct { 2202struct htp_context * ctx ; 2203struct htp_thread_trace * traces ; 2204float * dst ; 2205const __fp16 * vtcm_src ; 2206const float * src2 ; 2207uint32_t n_rows ; 2208uint32_t n_cols ; 2209uint32_t dst_stride ; 2210uint32_t src2_stride ; 2211uint32_t dst_cols ; 2212struct fastdiv_values n_threads_div ; 2213}output_transfer_col_chunk_state_t ; 2214 2215static void transfer_output_chunk_col_chunk_worker_fn (unsigned int n ,unsigned int i ,void * data ) { 2216 (void )n ; 2217output_transfer_col_chunk_state_t * st = (output_transfer_col_chunk_state_t * )data ; 2218struct htp_thread_trace * tr = & st -> traces [i ]; 2219 2220uint32_t n_blocks = st -> n_cols /32 ; 2221uint32_t b_first = fastdiv (n_blocks * i ,& st -> n_threads_div ); 2222uint32_t b_last = fastdiv (n_blocks * (i + 1 ),& st -> n_threads_div ); 2223uint32_t c_first = b_first * 32 ; 2224uint32_t c_last = b_last * 32 ; 2225uint32_t c_len = c_last - c_first ; 2226 2227if (c_len == 0 )return ; 2228 2229htp_trace_event_start (tr ,HTP_TRACE_EVT_HVX_O_PROC ,c_first ); 2230 2231const __fp16 * vtcm_src = st -> vtcm_src + b_first * HTP_MM_HMX_TILE_N_ELMS ; 2232const float * src2 = st -> src2 ? (st -> src2 + c_first ) :NULL ; 2233float * dst = st -> dst + c_first ; 2234 2235int chunk_dst_cols = (int )st -> dst_cols - (int )c_first ; 2236if (chunk_dst_cols > 0 ) { 2237transfer_output_chunk_fp16_to_fp32_col_chunk ( 2238dst ,src2 ,vtcm_src ,0 ,st -> n_rows ,c_len ,st -> n_cols , 2239st -> dst_stride ,st -> src2_stride , (uint32_t )chunk_dst_cols 2240 ); 2241 } 2242 2243htp_trace_event_stop (tr ,HTP_TRACE_EVT_HVX_O_PROC ,c_first ); 2244} 2245 2246static void transfer_output_chunk_threaded (struct htp_context * ctx ,float * dst ,const float * src2 ,const __fp16 * vtcm_src , 2247int n_rows ,int n_cols ,int dst_stride ,uint32_t src2_stride ,int dst_cols ,int n_threads ) { 2248assert (n_cols %HTP_MM_HMX_TILE_N_COLS == 0 ); 2249 2250if (n_rows <=0 )return ; 2251 2252uint32_t n_blocks = (uint32_t )n_cols /32 ; 2253if (n_threads > 1 && n_blocks >= (uint32_t )n_threads ) { 2254struct fastdiv_values n_threads_div = (n_threads == (int )ctx -> n_threads ) ?ctx -> n_threads_div :init_fastdiv_values (n_threads ); 2255output_transfer_col_chunk_state_t col_state ; 2256col_state .dst = dst ; 2257col_state .src2 = src2 ; 2258col_state .vtcm_src = vtcm_src ; 2259col_state .n_rows = (uint32_t )n_rows ; 2260col_state .n_cols = (uint32_t )n_cols ; 2261col_state .dst_stride = (uint32_t )dst_stride ; 2262col_state .src2_stride = src2_stride ; 2263col_state .dst_cols = (uint32_t )dst_cols ; 2264col_state .n_threads_div = n_threads_div ; 2265col_state .traces = ctx -> trace ; 2266col_state .ctx = ctx ; 2267 2268worker_pool_run_func (ctx -> worker_pool ,transfer_output_chunk_col_chunk_worker_fn ,& col_state ,n_threads ); 2269return ; 2270 } 2271 2272size_t n_tot_chunks = n_rows ; 2273size_t n_chunks_per_task = (n_threads == 1 ) ?n_tot_chunks :hmx_ceil_div (n_rows ,n_threads ); 2274n_chunks_per_task = hex_align_up (n_chunks_per_task ,2 ); 2275 2276int actual_threads = hmx_ceil_div (n_rows ,n_chunks_per_task ); 2277 2278output_transfer_task_state_t state ; 2279state .n_tasks = actual_threads ; 2280state .n_tot_chunks = n_tot_chunks ; 2281state .n_chunks_per_task = n_chunks_per_task ; 2282state .dst = dst ; 2283state .src2 = src2 ; 2284state .vtcm_src = vtcm_src ; 2285state .n_cols = n_cols ; 2286state .dst_stride = dst_stride ; 2287state .src2_stride = src2_stride ; 2288state .dst_cols = dst_cols ; 2289state .traces = ctx -> trace ; 2290 2291if (actual_threads <=1 ) { 2292transfer_output_chunk_worker_fn (1 ,0 ,& state ); 2293 }else { 2294worker_pool_run_func (ctx -> worker_pool ,transfer_output_chunk_worker_fn ,& state ,actual_threads ); 2295 } 2296} 2297 2298struct activation_transfer_params { 2299struct htp_context * ctx ; 2300__fp16 * dst ; 2301const float * src ; 2302int n_rows ; 2303int k_block ; 2304int k_stride ; 2305int n_threads ; 2306const struct fastdiv_values * act_threads_div ; 2307const struct fastdiv_values * k_div ; 2308int k_valid ; 2309float * vtcm_f32_act ; 2310size_t vtcm_f32_act_bytes ; 2311}; 2312 2313static void transfer_activation_chunk_threaded (const struct activation_transfer_params * params ) { 2314struct htp_context * ctx = params -> ctx ; 2315__fp16 * dst = params -> dst ; 2316const float * src = params -> src ; 2317int n_rows = params -> n_rows ; 2318int k_block = params -> k_block ; 2319int k_stride = params -> k_stride ; 2320int n_threads = params -> n_threads ; 2321const struct fastdiv_values * act_threads_div = params -> act_threads_div ; 2322const struct fastdiv_values * k_div = params -> k_div ; 2323int k_valid = params -> k_valid ; 2324float * vtcm_f32_act = params -> vtcm_f32_act ; 2325size_t vtcm_f32_act_bytes = params -> vtcm_f32_act_bytes ; 2326 2327if (n_rows <=0 ) { 2328return ; 2329 } 2330 2331const size_t n_tasks = (n_rows + 31 ) >>5 ; 2332if (n_threads > 1 && k_block > 32 && n_tasks < (size_t )n_threads ) { 2333// Calculate step rows parameters for column-chunked dma pipelining 2334uint32_t dma_step_rows = 2 ; 2335uint32_t dma_step_rows_shift = 1 ; 2336if (vtcm_f32_act && vtcm_f32_act_bytes > 0 && k_block > 0 ) { 2337size_t thread_scratch_bytes = hex_align_down (fastdiv (vtcm_f32_act_bytes ,act_threads_div ),128 ); 2338size_t thread_scratch_elements = thread_scratch_bytes /sizeof (float ); 2339size_t dma_step_rows_max = fastdiv (thread_scratch_elements /2 ,k_div ); 2340if (dma_step_rows_max >=4 ) { 2341dma_step_rows = 4 ; 2342dma_step_rows_shift = 2 ; 2343 } 2344 } 2345 2346activation_transfer_col_chunk_state_t col_state ; 2347col_state .dst = dst ; 2348col_state .src = src ; 2349col_state .n_rows = n_rows ; 2350col_state .k_block = k_block ; 2351col_state .k_stride = k_stride ; 2352col_state .k_valid = k_valid ; 2353col_state .n_col_chunks = n_threads ; 2354col_state .n_threads_div = * act_threads_div ; 2355col_state .vtcm_f32_act = vtcm_f32_act ; 2356col_state .vtcm_f32_act_bytes = vtcm_f32_act_bytes ; 2357col_state .traces = ctx -> trace ; 2358col_state .ctx = ctx ; 2359col_state .dma_step_rows = dma_step_rows ; 2360col_state .dma_step_rows_shift = dma_step_rows_shift ; 2361 2362worker_pool_run_func (ctx -> worker_pool ,transfer_activation_chunk_col_chunk_worker_fn ,& col_state ,n_threads ); 2363return ; 2364 } 2365 2366assert (k_block %HTP_MM_HMX_TILE_N_COLS == 0 && k_stride %HTP_MM_HMX_TILE_N_COLS == 0 ); 2367 2368size_t n_tot_chunks = n_rows ; 2369size_t n_chunks_per_task = (n_threads == 1 ) ?n_tot_chunks :32 ;// must be multiple of 32 to ensure correct destination address 2370 2371activation_transfer_task_state_t state ; 2372state .n_tasks = (n_threads == 1 ) ?1 :hmx_ceil_div (n_tot_chunks ,32 ); 2373state .n_tot_chunks = n_tot_chunks ; 2374state .n_chunks_per_task = n_chunks_per_task ; 2375state .dst = dst ; 2376state .src = src ; 2377state .k_block = k_block ; 2378state .k_stride = k_stride ; 2379state .k_valid = k_valid ; 2380state .traces = ctx -> trace ; 2381state .ctx = ctx ; 2382state .vtcm_f32_act = vtcm_f32_act ; 2383 2384state .vtcm_f32_act_bytes_per_thread = hex_align_down (fastdiv (vtcm_f32_act_bytes ,act_threads_div ),128 ); 2385 2386uint32_t dma_step_rows = 2 ; 2387uint32_t dma_step_rows_shift = 1 ; 2388if (vtcm_f32_act && state .vtcm_f32_act_bytes_per_thread > 0 && k_block > 0 ) { 2389size_t thread_scratch_elements = state .vtcm_f32_act_bytes_per_thread /sizeof (float ); 2390size_t dma_step_rows_max = fastdiv (thread_scratch_elements /2 ,k_div ); 2391if (dma_step_rows_max >=4 ) { 2392dma_step_rows = 4 ; 2393dma_step_rows_shift = 2 ; 2394 } 2395 } 2396state .dma_step_rows = dma_step_rows ; 2397state .dma_step_rows_shift = dma_step_rows_shift ; 2398 2399int active_threads = hex_smin (n_threads , (int )state .n_tasks ); 2400if (state .n_tasks == 1 || n_threads == 1 ) { 2401transfer_activation_chunk_worker_fn (1 ,0 ,& state ); 2402 }else { 2403worker_pool_run_func (ctx -> worker_pool ,transfer_activation_chunk_worker_fn ,& state ,active_threads ); 2404 } 2405} 2406// --- Async HMX matmul job (for pipeline overlap) --- 2407 2408typedef struct { 2409__fp16 * output ; 2410const __fp16 * activation ; 2411const __fp16 * weight ; 2412const __fp16 * scales ; 2413uint32_t n_row_tiles ; 2414uint32_t n_col_tiles ; 2415uint32_t n_dot_tiles ; 2416}hmx_matmul_job_t ; 2417 2418static void hmx_matmul_worker_fn (void * data ) { 2419hmx_matmul_job_t * job = (hmx_matmul_job_t * )data ; 2420FARF (HIGH ,"hmx-mm-job: n_row_tiles %u n_col_tiles %u n_dot_tiles %u" ,job -> n_row_tiles ,job -> n_col_tiles ,job -> n_dot_tiles ); 2421core_dot_chunk_fp16 (job -> output ,job -> activation ,job -> weight ,job -> scales ,job -> n_row_tiles ,job -> n_col_tiles ,job -> n_dot_tiles ); 2422} 2423 2424static inline void hmx_matmul_job_init (hmx_matmul_job_t * job , 2425__fp16 * output , 2426const __fp16 * activation , 2427const __fp16 * weight , 2428const __fp16 * scales , 2429uint32_t n_row_tiles , 2430uint32_t n_col_tiles , 2431uint32_t n_dot_tiles ) { 2432job -> output = output ; 2433job -> activation = activation ; 2434job -> weight = weight ; 2435job -> scales = scales ; 2436job -> n_row_tiles = n_row_tiles ; 2437job -> n_col_tiles = n_col_tiles ; 2438job -> n_dot_tiles = n_dot_tiles ; 2439} 2440 2441static int hmx_mm_2d_f32 (struct htp_context * ctx , 2442float * restrictdst , 2443const float * restrictsrc2 , 2444const float * activation , 2445const uint8_t * weight , 2446int m ,int k ,int n , 2447int act_stride , 2448int weight_stride , 2449int weight_type , 2450int k_valid , 2451int dst_stride , 2452uint32_t src2_stride , 2453int dst_cols , 2454int m_chunk , 2455int n_chunk , 2456int pipeline , 2457int n_threads , 2458int act_threads , 2459const struct fastdiv_values * act_threads_div , 2460const struct fastdiv_values * k_div , 2461int tile_size , 2462int aligned_tile_size , 2463int vtcm_size ) { 2464struct htp_thread_trace * tr = & ctx -> trace [0 ]; 2465htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 2466 2467if (k %32 != 0 || n %32 != 0 ) {return -1 ; } 2468if (!hex_is_aligned (dst ,VLEN )|| !hex_is_aligned (activation ,VLEN )) {return -1 ; } 2469 2470size_t row_stride = htp_mm_get_tiled_row_stride (weight_type ,k ); 2471if (row_stride == 0 ) { 2472return -1 ; 2473 } 2474 2475worker_callback_t dequant_worker_fn = NULL ; 2476switch (weight_type ) { 2477case HTP_TYPE_Q4_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_0 ;break ; 2478case HTP_TYPE_IQ4_NL :dequant_worker_fn = dequantize_tiled_worker_loop_iq4_nl ;break ; 2479case HTP_TYPE_Q4_1 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_1 ;break ; 2480case HTP_TYPE_MXFP4 :dequant_worker_fn = dequantize_tiled_worker_loop_mxfp4 ;break ; 2481case HTP_TYPE_Q8_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q8_0 ;break ; 2482case HTP_TYPE_F16 :dequant_worker_fn = convert_f16_worker_loop ;break ; 2483case HTP_TYPE_F32 :dequant_worker_fn = quantize_f32_worker_loop ;break ; 2484default : 2485return -1 ; 2486 } 2487 2488const int n_k_tiles = k /HTP_MM_HMX_TILE_N_COLS ; 2489const struct fastdiv_values n_k_tiles_div = init_fastdiv_values (n_k_tiles ); 2490 2491const bool is_quant = (weight_type != HTP_TYPE_F16 && weight_type != HTP_TYPE_F32 ); 2492const size_t vec_dot_size = k * sizeof (__fp16 ); 2493const size_t vtcm_budget = ctx -> vtcm_size ; 2494 2495const uint32_t dma_dst_stride = is_quant ?aligned_tile_size :row_stride ; 2496const uint32_t dma_src_stride = is_quant ?tile_size :weight_stride ; 2497const uint32_t dma_width_bytes = is_quant ?tile_size :row_stride ; 2498 2499size_t m_chunk_n_rows = m_chunk ; 2500size_t n_chunk_n_cols = n_chunk ; 2501size_t vtcm_used = vtcm_size ; 2502 2503const size_t qweight_row_stride = is_quant ? (size_t )(n_k_tiles * aligned_tile_size ) /32 :0 ; 2504 2505struct htp_mm_hmx_vtcm_layout L ; 2506htp_mm_hmx_vtcm_layout_build (& L ,HTP_MM_KERNEL_HMX_2D ,weight_type ,k ,m_chunk_n_rows ,n_chunk_n_cols ,1 , false,pipeline ,act_threads ,aligned_tile_size ); 2507 2508vtcm_used = L .total_bytes ; 2509if (vtcm_used > vtcm_budget ) { 2510FARF (ERROR ,"hmx-mm-2d-precomputed: VTCM overflow: used %zu budget %zu, m %d k %d n %d mc %zu nc %zu" , 2511vtcm_used ,vtcm_budget ,m ,k ,n ,m_chunk_n_rows ,n_chunk_n_cols ); 2512return -1 ; 2513 } 2514 2515uint8_t * const base = (uint8_t * )ctx -> vtcm_base ; 2516__fp16 * vtcm_weight_raw [2 ]= { 2517VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_weight [0 ]), 2518VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_weight [1 ],pipeline ) 2519 }; 2520 2521__fp16 * vtcm_f16_act = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_act ); 2522float * vtcm_f32_act = VTCM_LAYOUT_PTR (float ,base ,L .off_act_f32 ); 2523__fp16 * vtcm_output = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_dst [0 ]); 2524void * vtcm_scratch0 = VTCM_LAYOUT_PTR (void ,base ,L .off_scratch [0 ]); 2525void * vtcm_scratch1 = VTCM_LAYOUT_PTR_OPTIONAL (void ,base ,L .off_scratch [1 ],pipeline ); 2526void * vtcm_scratch2 = VTCM_LAYOUT_PTR_OPTIONAL (void ,base ,L .off_dst [1 ],pipeline ); 2527__fp16 * vtcm_scales = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_scales ); 2528 2529hmx_init_column_scales (vtcm_scales ,Q6_V_vsplat_R (0x3c00 ));// scale: 1.0, bias: 0.0 in FP16 2530 2531FARF (HIGH ,"hmx-mm-2d: m %d k %d n %d wtype %d mc %zu nc %zu vtcm %zu/%zu" , 2532m ,k ,n ,weight_type ,m_chunk_n_rows ,n_chunk_n_cols ,vtcm_used ,vtcm_budget ); 2533 2534int n_chunk_cnt = hmx_ceil_div (n ,n_chunk_n_cols ); 2535 2536htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 2537 2538if (pipeline ) { 2539// --- Asynchronous Pipelined Loop --- 2540hmx_matmul_job_t job_slots [2 ];// persistent double-buffered job descriptors 2541 2542for (size_t mr = 0 ;mr < m ;mr += m_chunk_n_rows ) { 2543const size_t n_rows = hex_smin (m - mr ,m_chunk_n_rows ); 2544 2545void * vtcm_weight_bufs [2 ]= {vtcm_scratch0 ,vtcm_scratch1 }; 2546void * vtcm_output_bufs [2 ]= {vtcm_output ,vtcm_scratch2 }; 2547 2548struct activation_transfer_params act_params = { 2549 .ctx = ctx , 2550 .dst = vtcm_f16_act , 2551 .src = activation + mr * act_stride , 2552 .n_rows = (int )n_rows , 2553 .k_block = k , 2554 .k_stride = act_stride , 2555 .n_threads = act_threads , 2556 .act_threads_div = act_threads_div , 2557 .k_div = k_div , 2558 .k_valid = k_valid , 2559 .vtcm_f32_act = vtcm_f32_act , 2560 .vtcm_f32_act_bytes = L .act_f32_bytes , 2561 }; 2562transfer_activation_chunk_threaded (& act_params ); 2563 2564// Prologue: push A0 and optionally A1 (if n_chunk_cnt > 1) 2565const size_t n_cols_A0 = hex_smin (n - 0 * n_chunk_n_cols ,n_chunk_n_cols ); 2566const uint32_t height_A0 = is_quant ? (n_cols_A0 /32 )* n_k_tiles :n_cols_A0 ; 2567dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [0 ],weight ), 2568dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_A0 ); 2569 2570if (1 < n_chunk_cnt ) { 2571const size_t n_cols_A1 = hex_smin (n - 1 * n_chunk_n_cols ,n_chunk_n_cols ); 2572const uint32_t height_A1 = is_quant ? (n_cols_A1 /32 )* n_k_tiles :n_cols_A1 ; 2573dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [1 ],weight + n_chunk_n_cols * weight_stride ), 2574dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_A1 ); 2575 } 2576 2577// Main loop: pop A_i -> dequantize A_i -> push A_{i+2} -> submit C_i -> wait C_{i-1} and store D_{i-1} 2578for (int i = 0 ;i < n_chunk_cnt ;++ i ) { 2579const size_t nc = i * n_chunk_n_cols ; 2580const size_t nc_p2 = nc + 2 * n_chunk_n_cols ; 2581 2582const size_t n_cols = hex_smin (n - nc ,n_chunk_n_cols ); 2583const size_t n_cols_p2 = hex_smin (n - nc_p2 ,n_chunk_n_cols ); 2584 2585// 1. pop A_i 2586void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 2587 2588// 2. dequantize A_i 2589dequantize_tiled_weight_chunk_to_fp16_tiles ( 2590ctx ,vtcm_weight_bufs [i %2 ],curr_raw , 2591n_cols ,k ,row_stride ,weight_type , 2592n_k_tiles ,n_k_tiles_div ,dequant_worker_fn ,n_threads ); 2593 2594// 3. push A_{i+2} (if i+2 < n_chunk_cnt) 2595if (i + 2 < n_chunk_cnt ) { 2596const uint32_t height_p2 = is_quant ? (n_cols_p2 /32 )* n_k_tiles :n_cols_p2 ; 2597dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,weight + nc_p2 * weight_stride ), 2598dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_p2 ); 2599 } 2600 2601// 4. submit C_i 2602hmx_matmul_job_init (& job_slots [i %2 ], (__fp16 * )vtcm_output_bufs [i %2 ], 2603 (__fp16 * )vtcm_f16_act , (__fp16 * )vtcm_weight_bufs [i %2 ], 2604vtcm_scales ,hmx_ceil_div (n_rows ,HTP_MM_HMX_TILE_N_ROWS ), 2605hmx_ceil_div (n_cols ,HTP_MM_HMX_TILE_N_COLS ),k /HTP_MM_HMX_TILE_N_ROWS ); 2606hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job_slots [i %2 ])); 2607 2608// 5. wait C_{i-1} and store D_{i-1} (multi-thread HVX, parallel with C_i) 2609if (i > 0 ) { 2610hmx_queue_pop (ctx -> hmx_queue ); 2611const size_t nc_prev = (i - 1 )* n_chunk_n_cols ; 2612const size_t n_cols_prev = hex_smin (n - nc_prev ,n_chunk_n_cols ); 2613float * output_chunk = dst + (mr * dst_stride + nc_prev ); 2614const float * src2_chunk = src2 ? (src2 + mr * src2_stride + nc_prev ) :NULL ; 2615int chunk_dst_cols = dst_cols - (int )nc_prev ; 2616if (chunk_dst_cols > 0 ) { 2617transfer_output_chunk_threaded (ctx ,output_chunk ,src2_chunk ,vtcm_output_bufs [(i - 1 ) %2 ],n_rows ,n_cols_prev ,dst_stride ,src2_stride ,chunk_dst_cols ,n_threads ); 2618 } 2619 } 2620 } 2621 2622// Epilogue: wait C_{last} and store D_{last} 2623hmx_queue_pop (ctx -> hmx_queue ); 2624const size_t nc_last = (n_chunk_cnt - 1 )* n_chunk_n_cols ; 2625const size_t n_cols_last = hex_smin (n - nc_last ,n_chunk_n_cols ); 2626float * output_chunk = dst + (mr * dst_stride + nc_last ); 2627const float * src2_chunk = src2 ? (src2 + mr * src2_stride + nc_last ) :NULL ; 2628int chunk_dst_cols = dst_cols - (int )nc_last ; 2629if (chunk_dst_cols > 0 ) { 2630transfer_output_chunk_threaded (ctx ,output_chunk ,src2_chunk ,vtcm_output_bufs [(n_chunk_cnt - 1 ) %2 ],n_rows ,n_cols_last ,dst_stride ,src2_stride ,chunk_dst_cols ,n_threads ); 2631 } 2632 } 2633 }else { 2634// --- Synchronous loop (m <= 32 or fallback) --- 2635hmx_matmul_job_t job ; 2636for (size_t mr = 0 ;mr < m ;mr += m_chunk_n_rows ) { 2637const size_t n_rows = hex_smin (m - mr ,m_chunk_n_rows ); 2638 2639struct activation_transfer_params act_params = { 2640 .ctx = ctx , 2641 .dst = vtcm_f16_act , 2642 .src = activation + mr * act_stride , 2643 .n_rows = (int )n_rows , 2644 .k_block = k , 2645 .k_stride = act_stride , 2646 .n_threads = act_threads , 2647 .act_threads_div = act_threads_div , 2648 .k_div = k_div , 2649 .k_valid = k_valid , 2650 .vtcm_f32_act = vtcm_f32_act , 2651 .vtcm_f32_act_bytes = L .act_f32_bytes , 2652 }; 2653transfer_activation_chunk_threaded (& act_params ); 2654 2655// A0: Pre-fetch the first weight chunk (nc = 0) 2656if (n > 0 ) { 2657const size_t n_cols = hex_smin (n ,n_chunk_n_cols ); 2658const uint32_t height = is_quant ? (n_cols /32 )* n_k_tiles :n_cols ; 2659dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [0 ],weight ),dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height ); 2660 } 2661 2662for (size_t nc = 0 ;nc < n ;nc += n_chunk_n_cols ) { 2663const size_t n_cols = hex_smin (n - nc ,n_chunk_n_cols ); 2664const size_t n_row_tiles = hmx_ceil_div (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 2665const size_t n_col_tiles = hmx_ceil_div (n_cols ,HTP_MM_HMX_TILE_N_COLS ); 2666 2667// A: Wait for weight DMA 2668void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 2669 2670// B: Weight Dequantize (Threaded) 2671dequantize_tiled_weight_chunk_to_fp16_tiles ( 2672ctx ,vtcm_scratch0 ,curr_raw , 2673n_cols ,k ,row_stride ,weight_type , 2674n_k_tiles ,n_k_tiles_div ,dequant_worker_fn ,n_threads ); 2675 2676// Start weight DMA for the next chunk early 2677const size_t nc_next = nc + n_chunk_n_cols ; 2678if (nc_next < n ) { 2679const size_t n_cols_next = hex_smin (n - nc_next ,n_chunk_n_cols ); 2680const uint32_t height_next = is_quant ? (n_cols_next /32 )* n_k_tiles :n_cols_next ; 2681dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,weight + nc_next * weight_stride ),dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_next ); 2682 } 2683 2684// C: HMX Compute (Queue-based) 2685hmx_matmul_job_init (& job ,vtcm_output ,vtcm_f16_act ,vtcm_scratch0 ,vtcm_scales ,n_row_tiles ,n_col_tiles ,k /HTP_MM_HMX_TILE_N_ROWS ); 2686hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job )); 2687hmx_queue_pop (ctx -> hmx_queue ); 2688 2689// D: Output Store 2690float * output_chunk = dst + (mr * dst_stride + nc ); 2691const float * src2_chunk = src2 ? (src2 + mr * src2_stride + nc ) :NULL ; 2692int chunk_dst_cols = dst_cols - (int )nc ; 2693if (chunk_dst_cols > 0 ) { 2694transfer_output_chunk_threaded (ctx ,output_chunk ,src2_chunk ,vtcm_output ,n_rows ,n_cols ,dst_stride ,src2_stride ,chunk_dst_cols ,n_threads ); 2695 } 2696 } 2697 } 2698 } 2699 2700return 0 ; 2701} 2702 2703static int hmx_mm_nx_2d_f32 (struct htp_ops_context * octx ,const struct htp_mm_kernel_params * kparams ) { 2704struct htp_context * ctx = octx -> ctx ; 2705struct htp_thread_trace * tr = & ctx -> trace [0 ]; 2706htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 2707 2708const uint32_t n_weights = kparams -> n_weights ; 2709if (n_weights == 0 || n_weights > HTP_OP_MAX_OUTPUTS ) { 2710return HTP_STATUS_INVAL_PARAMS ; 2711 } 2712 2713const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 2714const struct htp_tensor * restrictact = octx -> src [n_weights ]; 2715 2716const int weight_type = (int )src0 -> type ; 2717const int k = (int )act -> ne [0 ]; 2718const int k_valid = (int )act -> ne [0 ]; 2719const int m = (int ) (act -> ne [1 ]* act -> ne [2 ]* act -> ne [3 ]); 2720const int act_stride = (int ) (act -> nb [1 ] /sizeof (float )); 2721const float * activation = (const float * )act -> data ; 2722 2723if (k %32 != 0 ) {return HTP_STATUS_NO_SUPPORT ; } 2724if (!hex_is_aligned (activation ,VLEN )) {return HTP_STATUS_NO_SUPPORT ; } 2725 2726size_t row_stride = htp_mm_get_tiled_row_stride (weight_type ,k ); 2727if (row_stride == 0 ) { 2728return HTP_STATUS_NO_SUPPORT ; 2729 } 2730 2731worker_callback_t dequant_worker_fn = NULL ; 2732switch (weight_type ) { 2733case HTP_TYPE_Q4_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_0 ;break ; 2734case HTP_TYPE_IQ4_NL :dequant_worker_fn = dequantize_tiled_worker_loop_iq4_nl ;break ; 2735case HTP_TYPE_Q4_1 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_1 ;break ; 2736case HTP_TYPE_MXFP4 :dequant_worker_fn = dequantize_tiled_worker_loop_mxfp4 ;break ; 2737case HTP_TYPE_Q8_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q8_0 ;break ; 2738case HTP_TYPE_F16 :dequant_worker_fn = convert_f16_worker_loop ;break ; 2739case HTP_TYPE_F32 :dequant_worker_fn = quantize_f32_worker_loop ;break ; 2740default : 2741return HTP_STATUS_NO_SUPPORT ; 2742 } 2743 2744const int n_k_tiles = k /HTP_MM_HMX_TILE_N_COLS ; 2745const struct fastdiv_values n_k_tiles_div = init_fastdiv_values (n_k_tiles ); 2746 2747const bool is_quant = (weight_type != HTP_TYPE_F16 && weight_type != HTP_TYPE_F32 ); 2748const size_t vtcm_budget = ctx -> vtcm_size ; 2749 2750const int m_chunk_n_rows = kparams -> m_chunk ; 2751const int n_chunk_n_cols = kparams -> n_chunk ; 2752const int pipeline = kparams -> pipeline ; 2753const int n_threads = octx -> n_threads ; 2754const int act_threads = kparams -> n_act_threads ; 2755const struct fastdiv_values * act_threads_div = & kparams -> div_n_act_threads ; 2756const struct fastdiv_values * k_div = & kparams -> div_ne00_padded ; 2757const int tile_size = kparams -> tile_size ; 2758const int aligned_tile_size = kparams -> aligned_tile_size ; 2759 2760const uint32_t dma_dst_stride = is_quant ?aligned_tile_size :row_stride ; 2761const uint32_t dma_width_bytes = is_quant ?tile_size :row_stride ; 2762 2763struct htp_mm_hmx_vtcm_layout L ; 2764htp_mm_hmx_vtcm_layout_build (& L ,HTP_MM_KERNEL_HMX_2D ,weight_type ,k ,m_chunk_n_rows ,n_chunk_n_cols ,1 , false,pipeline ,act_threads ,aligned_tile_size ); 2765 2766if (L .total_bytes > vtcm_budget ) { 2767FARF (ERROR ,"hmx-mm-nx-2d: VTCM overflow: used %zu budget %zu, m %d k %d mc %d nc %d" , 2768L .total_bytes ,vtcm_budget ,m ,k ,m_chunk_n_rows ,n_chunk_n_cols ); 2769return HTP_STATUS_VTCM_TOO_SMALL ; 2770 } 2771 2772uint8_t * const base = (uint8_t * )ctx -> vtcm_base ; 2773__fp16 * vtcm_weight_raw [2 ]= { 2774VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_weight [0 ]), 2775VTCM_LAYOUT_PTR_OPTIONAL (__fp16 ,base ,L .off_weight [1 ],pipeline ) 2776 }; 2777 2778__fp16 * vtcm_f16_act = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_act ); 2779float * vtcm_f32_act = VTCM_LAYOUT_PTR (float ,base ,L .off_act_f32 ); 2780__fp16 * vtcm_output = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_dst [0 ]); 2781void * vtcm_scratch0 = VTCM_LAYOUT_PTR (void ,base ,L .off_scratch [0 ]); 2782void * vtcm_scratch1 = VTCM_LAYOUT_PTR_OPTIONAL (void ,base ,L .off_scratch [1 ],pipeline ); 2783void * vtcm_scratch2 = VTCM_LAYOUT_PTR_OPTIONAL (void ,base ,L .off_dst [1 ],pipeline ); 2784__fp16 * vtcm_scales = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_scales ); 2785 2786hmx_init_column_scales (vtcm_scales ,Q6_V_vsplat_R (0x3c00 ));// scale: 1.0, bias: 0.0 in FP16 2787 2788int m_start = 0 ; 2789int m_rows = m ; 2790if (octx -> ctx -> mdev .count > 1 ) { 2791const bool can_split = htp_tensor_can_row_partition (octx -> dsts [0 ],sizeof (float )); 2792const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition ((uint32_t )m ,can_split ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 2793m_start = (int )range .start ; 2794m_rows = (int )range .count ; 2795 } 2796 2797if (m_rows == 0 ) { 2798return HTP_STATUS_OK ; 2799 } 2800 2801FARF (HIGH ,"hmx-mm-nx-2d: n_weights %u m %d (%d..%d) k %d wtype %d mc %d nc %d vtcm %zu/%zu" , 2802n_weights ,m ,m_start ,m_start + m_rows ,k ,weight_type ,m_chunk_n_rows ,n_chunk_n_cols ,L .total_bytes ,vtcm_budget ); 2803 2804htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 2805 2806const size_t mr_end = (size_t )(m_start + m_rows ); 2807 2808if (pipeline ) { 2809hmx_matmul_job_t job_slots [2 ]; 2810 2811for (size_t mr = (size_t )m_start ;mr < mr_end ;mr += m_chunk_n_rows ) { 2812const size_t n_rows = hex_smin (mr_end - mr ,m_chunk_n_rows ); 2813 2814void * vtcm_weight_bufs [2 ]= {vtcm_scratch0 ,vtcm_scratch1 }; 2815void * vtcm_output_bufs [2 ]= {vtcm_output ,vtcm_scratch2 }; 2816 2817struct activation_transfer_params act_params = { 2818 .ctx = ctx , 2819 .dst = vtcm_f16_act , 2820 .src = activation + mr * act_stride , 2821 .n_rows = (int )n_rows , 2822 .k_block = k , 2823 .k_stride = act_stride , 2824 .n_threads = act_threads , 2825 .act_threads_div = act_threads_div , 2826 .k_div = k_div , 2827 .k_valid = k_valid , 2828 .vtcm_f32_act = vtcm_f32_act , 2829 .vtcm_f32_act_bytes = L .act_f32_bytes , 2830 }; 2831transfer_activation_chunk_threaded (& act_params ); 2832 2833for (uint32_t p = 0 ;p < n_weights ;p ++ ) { 2834const struct htp_tensor * restrictsrc_w = octx -> src [p ]; 2835const struct htp_tensor * restrictdst = octx -> dsts [p ]; 2836if (!src_w || !dst )continue ; 2837 2838const uint8_t * weight = (const uint8_t * )src_w -> data ; 2839float * dst_ptr = (float * )dst -> data ; 2840const size_t n = src_w -> ne [1 ]; 2841if (n == 0 )continue ; 2842const size_t weight_stride = src_w -> nb [1 ]; 2843const size_t dst_stride = dst -> nb [1 ] /sizeof (float ); 2844const int dst_cols = (int )dst -> ne [0 ]; 2845const int n_chunk_cnt = hmx_ceil_div (n ,n_chunk_n_cols ); 2846 2847const uint32_t dma_src_stride = is_quant ?tile_size :weight_stride ; 2848 2849const size_t n_cols_A0 = hex_smin (n - 0 * n_chunk_n_cols ,n_chunk_n_cols ); 2850const uint32_t height_A0 = is_quant ? (n_cols_A0 /32 )* n_k_tiles :n_cols_A0 ; 2851dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [0 ],weight ), 2852dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_A0 ); 2853 2854if (1 < n_chunk_cnt ) { 2855const size_t n_cols_A1 = hex_smin (n - 1 * n_chunk_n_cols ,n_chunk_n_cols ); 2856const uint32_t height_A1 = is_quant ? (n_cols_A1 /32 )* n_k_tiles :n_cols_A1 ; 2857dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [1 ],weight + n_chunk_n_cols * weight_stride ), 2858dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_A1 ); 2859 } 2860 2861for (int i = 0 ;i < n_chunk_cnt ;++ i ) { 2862const size_t nc = i * n_chunk_n_cols ; 2863const size_t nc_p2 = nc + 2 * n_chunk_n_cols ; 2864 2865const size_t n_cols = hex_smin (n - nc ,n_chunk_n_cols ); 2866const size_t n_cols_p2 = hex_smin (n - nc_p2 ,n_chunk_n_cols ); 2867 2868void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 2869 2870dequantize_tiled_weight_chunk_to_fp16_tiles ( 2871ctx ,vtcm_weight_bufs [i %2 ],curr_raw , 2872n_cols ,k ,row_stride ,weight_type , 2873n_k_tiles ,n_k_tiles_div ,dequant_worker_fn ,n_threads ); 2874 2875if (i + 2 < n_chunk_cnt ) { 2876const uint32_t height_p2 = is_quant ? (n_cols_p2 /32 )* n_k_tiles :n_cols_p2 ; 2877dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,weight + nc_p2 * weight_stride ), 2878dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_p2 ); 2879 } 2880 2881hmx_matmul_job_init (& job_slots [i %2 ], (__fp16 * )vtcm_output_bufs [i %2 ], 2882 (__fp16 * )vtcm_f16_act , (__fp16 * )vtcm_weight_bufs [i %2 ], 2883vtcm_scales ,hmx_ceil_div (n_rows ,HTP_MM_HMX_TILE_N_ROWS ), 2884hmx_ceil_div (n_cols ,HTP_MM_HMX_TILE_N_COLS ),k /HTP_MM_HMX_TILE_N_ROWS ); 2885hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job_slots [i %2 ])); 2886 2887if (i > 0 ) { 2888hmx_queue_pop (ctx -> hmx_queue ); 2889const size_t nc_prev = (i - 1 )* n_chunk_n_cols ; 2890const size_t n_cols_prev = hex_smin (n - nc_prev ,n_chunk_n_cols ); 2891float * output_chunk = dst_ptr + (mr * dst_stride + nc_prev ); 2892int chunk_dst_cols = dst_cols - (int )nc_prev ; 2893if (chunk_dst_cols > 0 ) { 2894transfer_output_chunk_threaded (ctx ,output_chunk ,NULL ,vtcm_output_bufs [(i - 1 ) %2 ],n_rows ,n_cols_prev ,dst_stride ,0 ,chunk_dst_cols ,n_threads ); 2895 } 2896 } 2897 } 2898 2899hmx_queue_pop (ctx -> hmx_queue ); 2900const size_t nc_last = (n_chunk_cnt - 1 )* n_chunk_n_cols ; 2901const size_t n_cols_last = hex_smin (n - nc_last ,n_chunk_n_cols ); 2902float * output_chunk = dst_ptr + (mr * dst_stride + nc_last ); 2903int chunk_dst_cols = dst_cols - (int )nc_last ; 2904if (chunk_dst_cols > 0 ) { 2905transfer_output_chunk_threaded (ctx ,output_chunk ,NULL ,vtcm_output_bufs [(n_chunk_cnt - 1 ) %2 ],n_rows ,n_cols_last ,dst_stride ,0 ,chunk_dst_cols ,n_threads ); 2906 } 2907 } 2908 } 2909 }else { 2910hmx_matmul_job_t job ; 2911for (size_t mr = (size_t )m_start ;mr < mr_end ;mr += m_chunk_n_rows ) { 2912const size_t n_rows = hex_smin (mr_end - mr ,m_chunk_n_rows ); 2913 2914struct activation_transfer_params act_params = { 2915 .ctx = ctx , 2916 .dst = vtcm_f16_act , 2917 .src = activation + mr * act_stride , 2918 .n_rows = (int )n_rows , 2919 .k_block = k , 2920 .k_stride = act_stride , 2921 .n_threads = act_threads , 2922 .act_threads_div = act_threads_div , 2923 .k_div = k_div , 2924 .k_valid = k_valid , 2925 .vtcm_f32_act = vtcm_f32_act , 2926 .vtcm_f32_act_bytes = L .act_f32_bytes , 2927 }; 2928transfer_activation_chunk_threaded (& act_params ); 2929 2930for (uint32_t p = 0 ;p < n_weights ;p ++ ) { 2931const struct htp_tensor * restrictsrc_w = octx -> src [p ]; 2932const struct htp_tensor * restrictdst = octx -> dsts [p ]; 2933if (!src_w || !dst )continue ; 2934 2935const uint8_t * weight = (const uint8_t * )src_w -> data ; 2936float * dst_ptr = (float * )dst -> data ; 2937const size_t n = src_w -> ne [1 ]; 2938if (n == 0 )continue ; 2939const size_t weight_stride = src_w -> nb [1 ]; 2940const size_t dst_stride = dst -> nb [1 ] /sizeof (float ); 2941const int dst_cols = (int )dst -> ne [0 ]; 2942 2943const uint32_t dma_src_stride = is_quant ?tile_size :weight_stride ; 2944 2945if (n > 0 ) { 2946const size_t n_cols = hex_smin (n ,n_chunk_n_cols ); 2947const uint32_t height = is_quant ? (n_cols /32 )* n_k_tiles :n_cols ; 2948dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight_raw [0 ],weight ),dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height ); 2949 } 2950 2951for (size_t nc = 0 ;nc < n ;nc += n_chunk_n_cols ) { 2952const size_t n_cols = hex_smin (n - nc ,n_chunk_n_cols ); 2953const size_t n_row_tiles = hmx_ceil_div (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 2954const size_t n_col_tiles = hmx_ceil_div (n_cols ,HTP_MM_HMX_TILE_N_COLS ); 2955 2956void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 2957 2958dequantize_tiled_weight_chunk_to_fp16_tiles ( 2959ctx ,vtcm_scratch0 ,curr_raw , 2960n_cols ,k ,row_stride ,weight_type , 2961n_k_tiles ,n_k_tiles_div ,dequant_worker_fn ,n_threads ); 2962 2963const size_t nc_next = nc + n_chunk_n_cols ; 2964if (nc_next < n ) { 2965const size_t n_cols_next = hex_smin (n - nc_next ,n_chunk_n_cols ); 2966const uint32_t height_next = is_quant ? (n_cols_next /32 )* n_k_tiles :n_cols_next ; 2967dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,weight + nc_next * weight_stride ),dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_next ); 2968 } 2969 2970hmx_matmul_job_init (& job ,vtcm_output ,vtcm_f16_act ,vtcm_scratch0 ,vtcm_scales ,n_row_tiles ,n_col_tiles ,k /HTP_MM_HMX_TILE_N_ROWS ); 2971hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job )); 2972hmx_queue_pop (ctx -> hmx_queue ); 2973 2974float * output_chunk = dst_ptr + (mr * dst_stride + nc ); 2975int chunk_dst_cols = dst_cols - (int )nc ; 2976if (chunk_dst_cols > 0 ) { 2977transfer_output_chunk_threaded (ctx ,output_chunk ,NULL ,vtcm_output ,n_rows ,n_cols ,dst_stride ,0 ,chunk_dst_cols ,n_threads ); 2978 } 2979 } 2980 } 2981 } 2982 } 2983 2984return HTP_STATUS_OK ; 2985} 2986 2987static inline const __fp16 * hmx_mm_weight_batch_ptr (const hmx_mm_f16_f32_batched_params_t * params , 2988int dst_b2 ,int dst_b3 ) { 2989const size_t b2_idx = (params -> r2 <=1 ) ? (size_t )dst_b2 : (size_t )fastdiv ((uint32_t )dst_b2 ,& params -> div_r2 ); 2990const size_t b3_idx = (params -> r3 <=1 ) ? (size_t )dst_b3 : (size_t )fastdiv ((uint32_t )dst_b3 ,& params -> div_r3 ); 2991return (const __fp16 * ) ((const uint8_t * )params -> weight + 2992b2_idx * params -> src0_nb2 + 2993b3_idx * params -> src0_nb3 ); 2994} 2995 2996static inline const float * hmx_mm_activation_batch_ptr (const hmx_mm_f16_f32_batched_params_t * params , 2997int dst_b2 ,int dst_b3 ) { 2998return (const float * ) ((const uint8_t * )params -> activation + 2999 (size_t )dst_b2 * params -> src1_nb2 + 3000 (size_t )dst_b3 * params -> src1_nb3 ); 3001} 3002 3003static inline float * hmx_mm_dst_batch_ptr (const hmx_mm_f16_f32_batched_params_t * params , 3004int dst_b2 ,int dst_b3 ) { 3005return (float * ) ((uint8_t * )params -> dst + 3006 (size_t )dst_b2 * params -> dst_nb2 + 3007 (size_t )dst_b3 * params -> dst_nb3 ); 3008} 3009 3010static inline const float * hmx_mm_src2_batch_ptr (const hmx_mm_f16_f32_batched_params_t * params , 3011int src2_b2 ,int src2_b3 ) { 3012return params -> src2 ? (const float * ) ((const uint8_t * )params -> src2 + 3013 (size_t )src2_b2 * params -> src2_nb2 + 3014 (size_t )src2_b3 * params -> src2_nb3 ) :NULL ; 3015} 3016 3017static int hmx_mm_f16_f32_batched_simple (struct htp_context * ctx , 3018const hmx_mm_f16_f32_batched_params_t * params , 3019int m_chunk ,int n_chunk ,int pipeline ,int n_threads ,int act_threads ,int vtcm_size , 3020const struct fastdiv_values * act_threads_div ,const struct fastdiv_values * k_div ) { 3021int ret = 0 ; 3022for (int b3 = 0 ;b3 < params -> ne13 && ret == 0 ;++ b3 ) { 3023for (int b2 = 0 ;b2 < params -> ne12 && ret == 0 ;++ b2 ) { 3024ret = hmx_mm_2d_f32 (ctx ,hmx_mm_dst_batch_ptr (params ,b2 ,b3 ), 3025hmx_mm_src2_batch_ptr (params ,b2 ,b3 ), 3026hmx_mm_activation_batch_ptr (params ,b2 ,b3 ), 3027 (const uint8_t * )hmx_mm_weight_batch_ptr (params ,b2 ,b3 ), 3028params -> m ,params -> k ,params -> n , 3029params -> act_stride ,params -> weight_stride * (int )sizeof (__fp16 ), 3030HTP_TYPE_F16 ,params -> k ,params -> dst_stride ,params -> src2_stride ,params -> n , 3031m_chunk ,n_chunk ,pipeline ,n_threads ,act_threads , 3032act_threads_div ,k_div ,0 ,0 ,vtcm_size ); 3033 } 3034 } 3035return ret ; 3036} 3037 3038static int hmx_mm_f16_f32_batched (struct htp_context * ctx ,const hmx_mm_f16_f32_batched_params_t * params , 3039int m_chunk ,int n_chunk ,int pipeline ,int n_threads ,int act_threads , 3040const struct fastdiv_values * act_threads_div , 3041const struct fastdiv_values * k_div , 3042int vtcm_size ) { 3043if (params -> act_stride < params -> k || params -> weight_stride < params -> k || params -> dst_stride < params -> n ) {return -1 ; } 3044if (params -> ne02 <=0 || params -> ne03 <=0 || params -> ne12 <=0 || params -> ne13 <=0 ) {return -1 ; } 3045if (params -> ne12 %params -> ne02 != 0 || params -> ne13 %params -> ne03 != 0 ) {return -1 ; } 3046if (params -> k %32 != 0 || params -> n %32 != 0 ) {return -1 ; } 3047if (!hex_is_aligned (params -> dst ,VLEN )|| !hex_is_aligned (params -> activation ,VLEN )) {return -1 ; } 3048 3049const int group_size = params -> r2 ; 3050const size_t vtcm_budget = ctx -> vtcm_size ; 3051 3052// Check if the precomputed parameters are grouped or simple. 3053// If simple, or if group_size <= 1, we use simple fallback loop. 3054// Grouped path is only valid if group_size > 1 and it fits within VTCM budget. 3055bool run_grouped = (group_size > 1 && (size_t )vtcm_size <=vtcm_budget ); 3056if (!run_grouped ) { 3057return hmx_mm_f16_f32_batched_simple (ctx ,params ,m_chunk ,n_chunk ,pipeline ,n_threads ,act_threads ,vtcm_size ,act_threads_div ,k_div ); 3058 } 3059 3060struct htp_thread_trace * tr = & ctx -> trace [0 ]; 3061htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 3062 3063const size_t vec_dot_size = params -> k * sizeof (__fp16 ); 3064 3065const bool use_dma_activation = (params -> act_stride > params -> k ); 3066const size_t f32_scratch_size = use_dma_activation 3067 ?hex_align_up ((size_t )act_threads * HTP_MM_DMA_ACT_MULTIPLIER * (size_t )params -> k * sizeof (float ),HTP_MM_HMX_TILE_SIZE ) :0 ; 3068 3069size_t m_chunk_n_rows = m_chunk ; 3070size_t n_chunk_n_cols = n_chunk ; 3071size_t vtcm_used = vtcm_size ; 3072 3073struct htp_mm_hmx_vtcm_layout L ; 3074htp_mm_hmx_vtcm_layout_build (& L ,HTP_MM_KERNEL_HMX_F16_BATCHED ,HTP_TYPE_F16 ,params -> k ,m_chunk_n_rows ,n_chunk_n_cols ,group_size ,use_dma_activation , false,act_threads ,0 ); 3075 3076if (L .total_bytes > vtcm_budget ) { 3077FARF (HIGH ,"%s: grouped layout overflowed VTCM, falling back to simple batched loop" ,__func__ ); 3078htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3079return hmx_mm_f16_f32_batched_simple (ctx ,params ,m_chunk ,n_chunk ,pipeline ,n_threads ,act_threads ,vtcm_size ,act_threads_div ,k_div ); 3080 } 3081 3082uint8_t * const base = (uint8_t * )ctx -> vtcm_base ; 3083__fp16 * vtcm_weight = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_weight [0 ]); 3084__fp16 * vtcm_f16_act = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_act ); 3085__fp16 * vtcm_output = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_dst [0 ]); 3086void * vtcm_scratch0 = VTCM_LAYOUT_PTR (void ,base ,L .off_scratch [0 ]); 3087void * vtcm_scratch1 = VTCM_LAYOUT_PTR (void ,base ,L .off_scratch [1 ]); 3088__fp16 * vtcm_scales = VTCM_LAYOUT_PTR (__fp16 ,base ,L .off_scales ); 3089float * vtcm_f32_act = VTCM_LAYOUT_PTR_OPTIONAL (float ,base ,L .off_act_f32 ,use_dma_activation ); 3090 3091hmx_init_column_scales (vtcm_scales ,Q6_V_vsplat_R (0x3c00 ));// scale: 1.0, bias: 0.0 in FP16 3092 3093FARF (HIGH ,"%s: grouped path m=%d k=%d n=%d group=%d streams=%d mc=%zu nc=%zu vtcm=%zu/%zu" , 3094__func__ ,params -> m ,params -> k ,params -> n ,group_size ,params -> ne13 , 3095m_chunk_n_rows ,n_chunk_n_cols , 3096L .total_bytes ,vtcm_budget ); 3097 3098const size_t fp16_row_bytes = (size_t )params -> k * sizeof (__fp16 ); 3099const size_t weight_row_bytes = (size_t )params -> weight_stride * sizeof (__fp16 ); 3100 3101htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3102 3103hmx_matmul_job_t job ; 3104 3105for (int b3 = 0 ;b3 < params -> ne13 ;++ b3 ) { 3106for (int b2_base = 0 ;b2_base < params -> ne12 ;b2_base += group_size ) { 3107const __fp16 * weight_group = hmx_mm_weight_batch_ptr (params ,b2_base ,b3 ); 3108 3109for (size_t mr = 0 ;mr < (size_t )params -> m ;mr += m_chunk_n_rows ) { 3110const size_t n_rows = hex_smin ((size_t )params -> m - mr ,m_chunk_n_rows ); 3111const size_t n_row_tiles = hmx_ceil_div ((int )n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 3112 3113// Pre-load activations for all heads in the group (once per m_chunk). 3114// When the source is strided (permuted Q), use 2D DMA to gather 3115// contiguous rows into a VTCM scratch buffer first, then HVX 3116// converts from the contiguous VTCM buffer. This avoids L2 cache 3117// thrashing from HVX loads at large strides. 3118for (int g = 0 ;g < group_size ;++ g ) { 3119const float * activation_chunk = hmx_mm_activation_batch_ptr (params ,b2_base + g ,b3 )+ mr * params -> act_stride ; 3120__fp16 * vtcm_act_g = vtcm_f16_act + (size_t )g * L .act_head_stride ; 3121struct activation_transfer_params act_params = { 3122 .ctx = ctx , 3123 .dst = vtcm_act_g , 3124 .src = activation_chunk , 3125 .n_rows = (int )n_rows , 3126 .k_block = params -> k , 3127 .k_stride = params -> act_stride , 3128 .n_threads = act_threads , 3129 .act_threads_div = act_threads_div , 3130 .k_div = k_div , 3131 .k_valid = params -> k , 3132 .vtcm_f32_act = vtcm_f32_act , 3133 .vtcm_f32_act_bytes = L .act_f32_bytes , 3134 }; 3135transfer_activation_chunk_threaded (& act_params ); 3136 } 3137 3138// Prologue: Push A0 and A1 (if exists) 3139 { 3140const size_t n_cols_first = hex_smin ((size_t )params -> n ,n_chunk_n_cols ); 3141dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_scratch0 ,weight_group ), 3142fp16_row_bytes ,weight_row_bytes ,fp16_row_bytes ,n_cols_first ); 3143 } 3144if (n_chunk_n_cols < (size_t )params -> n ) { 3145const size_t n_cols_second = hex_smin ((size_t )params -> n - n_chunk_n_cols ,n_chunk_n_cols ); 3146dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_scratch1 ,weight_group + params -> weight_stride ), 3147fp16_row_bytes ,weight_row_bytes ,fp16_row_bytes ,n_cols_second ); 3148 } 3149 3150for (size_t nc = 0 ;nc < (size_t )params -> n ;nc += n_chunk_n_cols ) { 3151const size_t n_cols = hex_smin ((size_t )params -> n - nc ,n_chunk_n_cols ); 3152const size_t n_col_tiles = hmx_ceil_div ((int )n_cols ,HTP_MM_HMX_TILE_N_COLS ); 3153 3154 { 3155void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 3156 3157hmx_interleave_rows_to_tiles (vtcm_weight , (const __fp16 * )curr_raw ,n_cols ,params -> k ,params -> k ,0 ,n_cols ); 3158 3159const size_t nc_next = nc + n_chunk_n_cols * 2 ; 3160if (nc_next < (size_t )params -> n ) { 3161const size_t n_cols_next = hex_smin ((size_t )params -> n - nc_next ,n_chunk_n_cols ); 3162const __fp16 * next_weight_chunk = weight_group + nc_next * params -> weight_stride ; 3163 3164dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,next_weight_chunk ), 3165fp16_row_bytes ,weight_row_bytes ,fp16_row_bytes ,n_cols_next ); 3166 } 3167 } 3168 3169// Reuse the interleaved weight for every q_head in this GQA group 3170for (int g = 0 ;g < group_size ;++ g ) { 3171 { 3172const __fp16 * vtcm_act_g = vtcm_f16_act + (size_t )g * L .act_head_stride ; 3173hmx_matmul_job_init (& job ,vtcm_output ,vtcm_act_g ,vtcm_weight ,vtcm_scales ,n_row_tiles ,n_col_tiles ,params -> k /32 ); 3174hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job )); 3175hmx_queue_pop (ctx -> hmx_queue ); 3176 } 3177 3178 { 3179float * output = hmx_mm_dst_batch_ptr (params ,b2_base + g ,b3 )+ mr * params -> dst_stride + nc ; 3180const float * src2_chunk = params -> src2 ? (hmx_mm_src2_batch_ptr (params ,b2_base + g ,b3 )+ mr * params -> src2_stride + nc ) :NULL ; 3181int chunk_dst_cols = params -> n - (int )nc ; 3182if (chunk_dst_cols > 0 ) { 3183transfer_output_chunk_threaded (ctx ,output ,src2_chunk ,vtcm_output , (int )n_rows , (int )n_cols , 3184params -> dst_stride ,params -> src2_stride ,chunk_dst_cols ,n_threads ); 3185 } 3186 } 3187 } 3188 } 3189 } 3190 } 3191 } 3192 3193return 0 ; 3194} 3195 3196static void transfer_activation_chunk_gathered_threaded ( 3197struct htp_context * ctx , 3198__fp16 * dst , 3199const float * src , 3200int start_row , 3201int n_rows , 3202int k_block , 3203const struct mmid_row_mapping * matrix_rows , 3204int cur_a , 3205int mapping_stride , 3206int ne11 , 3207size_t nb11 , 3208size_t nb12 , 3209int cne1 , 3210int n_threads , 3211int k_valid ) { 3212if (n_rows <=0 )return ; 3213int chunks_per_thread = hmx_ceil_div (n_rows ,n_threads ); 3214chunks_per_thread = hex_align_up (chunks_per_thread ,2 ); 3215 3216int actual_threads = hmx_ceil_div (n_rows ,chunks_per_thread ); 3217 3218activation_transfer_gathered_task_state_t state = { 3219 .dst = dst , 3220 .src = src , 3221 .n_tasks = actual_threads , 3222 .n_tot_chunks = n_rows , 3223 .n_chunks_per_task = chunks_per_thread , 3224 .k_block = k_block , 3225 .matrix_rows = matrix_rows , 3226 .cur_a = cur_a , 3227 .mapping_stride = mapping_stride , 3228 .ne11 = ne11 , 3229 .ne11_div = ne11 > 1 ?init_fastdiv_values (ne11 ) : (struct fastdiv_values ){0 ,0 }, 3230 .nb11 = nb11 , 3231 .nb12 = nb12 , 3232 .start_row = start_row , 3233 .cne1 = cne1 , 3234 .k_valid = k_valid , 3235 .traces = ctx -> trace , 3236 }; 3237 3238worker_callback_t worker_fn = ne11 == 1 ?transfer_activation_chunk_gathered_worker_flat_fn : 3239transfer_activation_chunk_gathered_worker_fn ; 3240 3241if (actual_threads <=1 ) { 3242worker_fn (1 ,0 ,& state ); 3243 }else { 3244worker_pool_run_func (ctx -> worker_pool ,worker_fn ,& state ,actual_threads ); 3245 } 3246} 3247 3248static void transfer_output_chunk_scattered_threaded ( 3249struct htp_context * ctx , 3250float * dst , 3251const __fp16 * vtcm_src , 3252int start_row , 3253int n_rows , 3254int n_cols , 3255const struct mmid_row_mapping * matrix_rows , 3256int cur_a , 3257int mapping_stride , 3258size_t dst_nb1 , 3259size_t dst_nb2 , 3260int cne1 , 3261int n_threads ) { 3262if (n_rows <=0 )return ; 3263int chunks_per_thread = hmx_ceil_div (n_rows ,n_threads ); 3264chunks_per_thread = hex_align_up (chunks_per_thread ,2 ); 3265 3266int actual_threads = hmx_ceil_div (n_rows ,chunks_per_thread ); 3267 3268output_transfer_scattered_task_state_t state = { 3269 .vtcm_src = vtcm_src , 3270 .dst = dst , 3271 .n_tasks = actual_threads , 3272 .n_tot_chunks = n_rows , 3273 .n_chunks_per_task = chunks_per_thread , 3274 .n_cols = n_cols , 3275 .matrix_rows = matrix_rows , 3276 .cur_a = cur_a , 3277 .mapping_stride = mapping_stride , 3278 .dst_nb1 = dst_nb1 , 3279 .dst_nb2 = dst_nb2 , 3280 .start_row = start_row , 3281 .cne1 = cne1 , 3282 .traces = ctx -> trace , 3283 }; 3284 3285if (actual_threads <=1 ) { 3286transfer_output_chunk_scattered_worker_fn (1 ,0 ,& state ); 3287 }else { 3288worker_pool_run_func (ctx -> worker_pool ,transfer_output_chunk_scattered_worker_fn ,& state ,actual_threads ); 3289 } 3290} 3291 3292static int hmx_mm_id_2d_f32 (struct htp_context * ctx , 3293float * restrictdst , 3294const float * activation , 3295const uint8_t * weight , 3296int m ,int k ,int n , 3297int k_valid , 3298int ne11 , 3299size_t act_nb1 ,size_t act_nb2 , 3300size_t dst_nb1 ,size_t dst_nb2 , 3301int weight_stride , 3302int weight_type , 3303const struct mmid_row_mapping * matrix_rows , 3304int cur_a , 3305int mapping_stride , 3306int m_start , 3307int m_end , 3308int n_threads ) { 3309struct htp_thread_trace * tr = & ctx -> trace [0 ]; 3310htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 3311 3312const int cne1 = m ; 3313const int m_padded = hex_align_up (m ,32 ); 3314 3315if (k %32 != 0 || n %32 != 0 ) {return -1 ; } 3316if (!hex_is_aligned (dst ,VLEN )|| !hex_is_aligned (activation ,VLEN )) {return -1 ; } 3317 3318size_t row_stride = htp_mm_get_tiled_row_stride (weight_type ,k ); 3319if (row_stride == 0 ) { 3320return -1 ; 3321 } 3322 3323worker_callback_t dequant_worker_fn = NULL ; 3324switch (weight_type ) { 3325case HTP_TYPE_Q4_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_0 ;break ; 3326case HTP_TYPE_IQ4_NL :dequant_worker_fn = dequantize_tiled_worker_loop_iq4_nl ;break ; 3327case HTP_TYPE_Q4_1 :dequant_worker_fn = dequantize_tiled_worker_loop_q4_1 ;break ; 3328case HTP_TYPE_MXFP4 :dequant_worker_fn = dequantize_tiled_worker_loop_mxfp4 ;break ; 3329case HTP_TYPE_Q8_0 :dequant_worker_fn = dequantize_tiled_worker_loop_q8_0 ;break ; 3330case HTP_TYPE_F16 :dequant_worker_fn = convert_f16_worker_loop ;break ; 3331case HTP_TYPE_F32 :dequant_worker_fn = quantize_f32_worker_loop ;break ; 3332default : 3333return -1 ; 3334 } 3335 3336const int n_k_tiles = k /HTP_MM_HMX_TILE_N_COLS ; 3337const struct fastdiv_values n_k_tiles_div = init_fastdiv_values (n_k_tiles ); 3338 3339const bool is_quant = (weight_type != HTP_TYPE_F16 && weight_type != HTP_TYPE_F32 ); 3340 3341const size_t vec_dot_size = k * sizeof (__fp16 ); 3342const size_t vtcm_budget = ctx -> vtcm_size ; 3343size_t vtcm_used = 0 ; 3344 3345int tile_size = htp_mm_get_weight_tile_size (weight_type ); 3346int aligned_tile_size = htp_mm_get_weight_aligned_tile_size (weight_type ); 3347 3348const uint32_t dma_dst_stride = is_quant ?aligned_tile_size :row_stride ; 3349const uint32_t dma_src_stride = is_quant ?tile_size :weight_stride ; 3350const uint32_t dma_width_bytes = is_quant ?tile_size :row_stride ; 3351 3352const size_t qweight_row_stride = is_quant ? (size_t )(n_k_tiles * aligned_tile_size ) /32 :0 ; 3353const size_t weight_row_stride = is_quant ?qweight_row_stride :row_stride ; 3354 3355size_t size_per_n = 0 ,size_per_m = 0 ,size_per_mn = 0 ; 3356htp_mm_hmx_get_2d_chunk_costs (weight_type ,k ,/*pipeline=*/ false,aligned_tile_size , 3357& size_per_n ,& size_per_m ,& size_per_mn ); 3358 3359const size_t overhead = htp_mm_hmx_get_2d_overhead (/*pipeline=*/ false,/*is_matmul_id=*/ true); 3360size_t m_chunk_n_rows = 0 ,n_chunk_n_cols = 0 ; 3361if (htp_mm_hmx_compute_chunks (vtcm_budget ,overhead ,size_per_n ,size_per_m ,size_per_mn , 3362m_padded ,n , 3363/*m_block_cost=*/ (size_t )n * HTP_MM_HMX_COST_W_DEQUANT , 3364/*n_block_cost=*/ (size_t )m_padded * HTP_MM_HMX_COST_A_CONVERT ,& m_chunk_n_rows ,& n_chunk_n_cols ,& vtcm_used )) { 3365FARF (ERROR ,"hmx-mm-id-2d: VTCM too small : m %d k %d n %d budget %zu" ,m_padded ,k ,n ,vtcm_budget ); 3366return -1 ; 3367 } 3368 3369const size_t weight_area_size = hex_align_up (n_chunk_n_cols * weight_row_stride ,HTP_MM_HMX_TILE_SIZE ); 3370const size_t act_area_size = hex_align_up (m_chunk_n_rows * vec_dot_size ,HTP_MM_HMX_TILE_SIZE ); 3371const size_t output_area_size = hex_align_up (m_chunk_n_rows * n_chunk_n_cols * sizeof (__fp16 ),HTP_MM_HMX_TILE_SIZE ); 3372 3373size_t scratch0_size = hex_align_up (n_chunk_n_cols * vec_dot_size ,HTP_MM_HMX_TILE_SIZE ); 3374 3375uint8_t * vtcm_ptr = (uint8_t * )ctx -> vtcm_base ; 3376__fp16 * vtcm_weight = weight_area_size ? (__fp16 * )vtcm_seq_alloc (& vtcm_ptr ,weight_area_size ) :NULL ; 3377__fp16 * vtcm_f16_act = (__fp16 * )vtcm_seq_alloc (& vtcm_ptr ,act_area_size ); 3378__fp16 * vtcm_output = (__fp16 * )vtcm_seq_alloc (& vtcm_ptr ,output_area_size ); 3379void * vtcm_scratch0 = vtcm_seq_alloc (& vtcm_ptr ,scratch0_size ); 3380__fp16 * vtcm_scales = (__fp16 * )vtcm_seq_alloc (& vtcm_ptr ,256 ); 3381 3382vtcm_used = vtcm_ptr - (uint8_t * )ctx -> vtcm_base ; 3383if (vtcm_used > vtcm_budget ) { 3384FARF (ERROR ,"hmx-mm-id-2d: VTCM overflow: used %zu budget %zu" ,vtcm_used ,vtcm_budget ); 3385return -1 ; 3386 } 3387 3388hmx_init_column_scales (vtcm_scales ,Q6_V_vsplat_R (0x3c00 )); 3389 3390htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3391 3392hmx_matmul_job_t job ; 3393 3394for (size_t mr = (size_t )m_start ;mr < (size_t )m_end ;mr += m_chunk_n_rows ) { 3395const size_t n_rows = hex_smin ((size_t )m_end - mr ,m_chunk_n_rows ); 3396const size_t n_row_tiles = hmx_ceil_div (n_rows ,HTP_MM_HMX_TILE_N_ROWS ); 3397 3398transfer_activation_chunk_gathered_threaded ( 3399ctx ,vtcm_f16_act ,activation , (int )mr , (int )n_rows ,k , 3400matrix_rows ,cur_a ,mapping_stride ,ne11 ,act_nb1 ,act_nb2 ,cne1 ,n_threads ,k_valid ); 3401 3402// A0: Pre-fetch the first weight chunk (nc = 0) 3403if (n > 0 ) { 3404const size_t n_cols = hex_smin ((size_t )n ,n_chunk_n_cols ); 3405const uint32_t height = is_quant ? (n_cols /32 )* n_k_tiles :n_cols ; 3406dma_queue_push (ctx -> dma [0 ],dma_make_ptr (vtcm_weight ,weight ), 3407dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height ); 3408 } 3409 3410for (size_t nc = 0 ;nc < (size_t )n ;nc += n_chunk_n_cols ) { 3411const size_t n_cols = hex_smin ((size_t )n - nc ,n_chunk_n_cols ); 3412const size_t n_col_tiles = hmx_ceil_div (n_cols ,HTP_MM_HMX_TILE_N_COLS ); 3413 3414// A: Wait for weight DMA 3415void * curr_raw = dma_queue_pop (ctx -> dma [0 ]).dst ; 3416 3417// B: Weight Dequantize (Threaded) 3418dequantize_tiled_weight_chunk_to_fp16_tiles ( 3419ctx ,vtcm_scratch0 ,curr_raw , 3420n_cols ,k ,row_stride ,weight_type , 3421n_k_tiles ,n_k_tiles_div ,dequant_worker_fn ,n_threads 3422 ); 3423 3424// Start weight DMA for the next chunk early 3425const size_t nc_next = nc + n_chunk_n_cols ; 3426if (nc_next < (size_t )n ) { 3427const size_t n_cols_next = hex_smin ((size_t )n - nc_next ,n_chunk_n_cols ); 3428const uint32_t height_next = is_quant ? (n_cols_next /32 )* n_k_tiles :n_cols_next ; 3429dma_queue_push (ctx -> dma [0 ],dma_make_ptr (curr_raw ,weight + nc_next * weight_stride ), 3430dma_dst_stride ,dma_src_stride ,dma_width_bytes ,height_next ); 3431 } 3432 3433// C: HMX Compute (Queue-based) 3434hmx_matmul_job_init (& job ,vtcm_output ,vtcm_f16_act ,vtcm_scratch0 ,vtcm_scales ,n_row_tiles ,n_col_tiles ,k /HTP_MM_HMX_TILE_N_ROWS ); 3435hmx_queue_push (ctx -> hmx_queue ,hmx_queue_make_desc (hmx_matmul_worker_fn ,& job )); 3436hmx_queue_pop (ctx -> hmx_queue ); 3437 3438// D: Output Store 3439transfer_output_chunk_scattered_threaded ( 3440ctx ,dst + nc ,vtcm_output , (int )mr , (int )n_rows , (int )n_cols , 3441matrix_rows ,cur_a ,mapping_stride ,dst_nb1 ,dst_nb2 ,cne1 ,n_threads ); 3442 } 3443 } 3444 3445return 0 ; 3446} 3447 3448// --- Dispatchers and Public Entry Points --- 3449 3450static int hmx_mm_op_matmul (struct htp_ops_context * octx ,const struct htp_mm_kernel_params * kparams ) { 3451htp_matmul_tensors_preamble ; 3452 3453int k = (int )src0 -> ne [0 ]; 3454int n = (int )src0 -> ne [1 ]; 3455const int m_total = (int )src1 -> ne [1 ]; 3456const int act_stride = (int )(src1 -> nb [1 ] /sizeof (float )); 3457const int wgt_stride = (int )(src0 -> nb [1 ] /sizeof (__fp16 )); 3458 3459int m_start = 0 ; 3460int m_rows = m_total ; 3461if (octx -> ctx -> mdev .count > 1 ) { 3462const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 3463const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition ((uint32_t )m_total ,can_split ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 3464m_start = (int )range .start ; 3465m_rows = (int )range .count ; 3466 } 3467 3468if (m_rows == 0 ) { 3469return HTP_STATUS_OK ; 3470 } 3471 3472const float * src2_ptr = NULL ; 3473uint32_t src2_stride = 0 ; 3474size_t src2_nb2 = 0 ; 3475size_t src2_nb3 = 0 ; 3476if (src2 ) { 3477src2_stride = (src2 -> ne [1 ]== 1 ) ?0 : (uint32_t ) (src2 -> nb [1 ] /sizeof (float )); 3478src2_ptr = (const float * )src2 -> data + m_start * src2_stride ; 3479src2_nb2 = (src2 -> ne [2 ]== 1 ) ?0 :src2 -> nb [2 ]; 3480src2_nb3 = (src2 -> ne [3 ]== 1 ) ?0 :src2 -> nb [3 ]; 3481 } 3482 3483const int dst_stride = (int )(dst -> nb [1 ] /sizeof (float )); 3484float * dst_ptr = (float * )dst -> data + m_start * dst_stride ; 3485const float * act_ptr = (const float * )src1 -> data + m_start * act_stride ; 3486 3487int ret = -1 ; 3488const int n_threads = kparams -> n_threads ; 3489if (kparams -> kernel_type == HTP_MM_KERNEL_HMX_F16_BATCHED ) { 3490hmx_mm_f16_f32_batched_params_t batch_params = { 3491 .dst = dst_ptr , 3492 .src2 = src2_ptr , 3493 .activation = act_ptr , 3494 .weight = (const __fp16 * )src0 -> data , 3495 .m = m_rows , 3496 .k = k , 3497 .n = n , 3498 .act_stride = act_stride , 3499 .weight_stride = wgt_stride , 3500 .dst_stride = dst_stride , 3501 .src2_stride = src2_stride , 3502 .ne02 = ne02 , 3503 .ne03 = ne03 , 3504 .ne12 = ne12 , 3505 .ne13 = ne13 , 3506 .src0_nb2 = src0 -> nb [2 ], 3507 .src0_nb3 = src0 -> nb [3 ], 3508 .src1_nb2 = src1 -> nb [2 ], 3509 .src1_nb3 = src1 -> nb [3 ], 3510 .dst_nb2 = dst -> nb [2 ], 3511 .dst_nb3 = dst -> nb [3 ], 3512 .src2_nb2 = src2_nb2 , 3513 .src2_nb3 = src2_nb3 , 3514 .r2 = (ne02 > 0 ) ? (ne12 /ne02 ) :1 , 3515 .r3 = (ne03 > 0 ) ? (ne13 /ne03 ) :1 , 3516 .div_r2 = kparams -> div_r2 , 3517 .div_r3 = kparams -> div_r3 , 3518 }; 3519ret = hmx_mm_f16_f32_batched (octx -> ctx ,& batch_params , 3520kparams -> m_chunk ,kparams -> n_chunk , 3521kparams -> pipeline ,n_threads , 3522kparams -> n_act_threads , 3523& kparams -> div_n_act_threads , 3524& kparams -> div_ne00_padded , 3525kparams -> vtcm_size ); 3526 }else { 3527ret = hmx_mm_2d_f32 ( 3528octx -> ctx ,dst_ptr ,src2_ptr ,act_ptr , (const uint8_t * )src0 -> data , 3529m_rows ,k ,n ,act_stride , (int )src0 -> nb [1 ], (int )src0 -> type , (int )src1 -> ne [0 ], 3530dst_stride ,src2_stride , (int )dst -> ne [0 ], 3531kparams -> m_chunk ,kparams -> n_chunk ,kparams -> pipeline ,n_threads , 3532kparams -> n_act_threads , 3533& kparams -> div_n_act_threads , 3534& kparams -> div_ne00_padded , 3535kparams -> tile_size ,kparams -> aligned_tile_size ,kparams -> vtcm_size 3536 ); 3537 } 3538 3539if (ret != 0 ) { 3540FARF (ERROR ,"HMX matmul failed (ret=%d)\n" ,ret ); 3541return HTP_STATUS_INTERNAL_ERR ; 3542 } 3543return HTP_STATUS_OK ; 3544} 3545 3546int op_matmul (struct htp_ops_context * octx ) { 3547const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3548 3549const int status = htp_mm_init_context (octx ,kparams ); 3550if (status != HTP_STATUS_OK ) { 3551return status ; 3552 } 3553 3554if (kparams -> n_hmx ) { 3555return hmx_mm_op_matmul (octx ,kparams ); 3556 } 3557 3558return hvx_mm_matmul (octx ); 3559} 3560 3561static int hmx_mm_op_matmul_id ( 3562struct htp_ops_context * octx , 3563struct htp_mm_context * mmctx 3564) { 3565const uint32_t * matrix_row_counts = mmctx -> matrix_row_counts ; 3566const struct mmid_row_mapping * matrix_rows = mmctx -> matrix_rows ; 3567htp_matmul_tensors_preamble ; 3568const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3569const int n_ids = octx -> src [2 ]-> ne [0 ]; 3570const int n_as = ne02 ; 3571 3572for (uint32_t cur_a = 0 ;cur_a < n_as ;++ cur_a ) { 3573const int32_t cne1 = matrix_row_counts [cur_a ]; 3574if (cne1 == 0 )continue ; 3575 3576const int m_padded = hex_align_up (cne1 ,32 ); 3577int m_start = 0 ,m_end = m_padded ; 3578if (octx -> ctx -> mdev .count > 1 ) { 3579const bool can_split = htp_tensor_mdev_data_aligned (dst )&& (uint32_t )cne1 >=octx -> ctx -> mdev .count ; 3580const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition ((uint32_t )m_padded ,can_split ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 3581m_start = (int )range .start ; 3582m_end = (int ) (range .start + range .count ); 3583 } 3584if (m_start >=m_end )continue ; 3585 3586int ret = hmx_mm_id_2d_f32 (octx -> ctx , (float * )dst -> data , (float * )src1 -> data , 3587 (const uint8_t * )src0 -> data + cur_a * nb02 , 3588cne1 ,ne00 ,ne01 , 3589ne10 , 3590ne11 , 3591nb11 ,nb12 , 3592nb1 ,nb2 , 3593 (int )src0 -> nb [1 ], (int )src0 -> type , 3594matrix_rows ,cur_a ,mmctx -> mapping_stride , 3595m_start ,m_end , (int )octx -> n_threads ); 3596if (ret != 0 ) { 3597FARF (ERROR ,"HMX matmul failed for expert %u, error %d\n" ,cur_a ,ret ); 3598return HTP_STATUS_NO_SUPPORT ; 3599 } 3600 } 3601 3602return HTP_STATUS_OK ; 3603} 3604 3605static int hvx_mm_matmul_id ( 3606struct htp_ops_context * octx , 3607struct htp_mm_context * mmctx , 3608work_queue_func_t hvx_mmid_task_func 3609) { 3610htp_matmul_tensors_preamble ; 3611const uint32_t src0_row_size_padded = mmctx -> src0_row_size_padded ; 3612const uint32_t src1_nrows = mmctx -> src1_nrows ; 3613 3614struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 3615htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 3616 3617const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3618const struct htp_tensor * restrictids = octx -> src [2 ]; 3619const size_t src0_row_size = nb01 ; 3620 3621const uint32_t qk = QK_Q8_0_TILED ; 3622const uint32_t nb = (ne10 + qk - 1 ) /qk ; 3623const uint32_t total_nb = src1_nrows * nb ; 3624 3625work_queue_func_t quant_task_func ; 3626uint32_t n_quant_tasks = 1 ; 3627if (src1_nrows < octx -> n_threads ) { 3628n_quant_tasks = MIN (total_nb ,octx -> n_threads ); 3629quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled_block :quantize_f32_q8_0_tiled_block ; 3630for (uint32_t ith = 0 ;ith < n_quant_tasks ;++ ith ) { 3631uint32_t ib_first = (total_nb * ith ) /n_quant_tasks ; 3632uint32_t ib_last = (total_nb * (ith + 1 )) /n_quant_tasks ; 3633mmctx -> quant_ib_first [ith ]= ib_first ; 3634mmctx -> quant_ib_last [ith ]= ib_last ; 3635mmctx -> quant_r [ith ]= ib_first /nb ; 3636mmctx -> quant_c [ith ]= ib_first %nb ; 3637 } 3638 }else { 3639n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 3640quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled :quantize_f32_q8_0_tiled ; 3641 } 3642size_t src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (ne10 ) :htp_mm_q8_0_tiled_row_size (ne10 ); 3643 3644struct htp_mm_hvx_vtcm_layout L ; 3645htp_mm_hvx_vtcm_layout_build (& L ,kparams -> kernel_type ,src0 -> type ,ne10 ,src1_nrows ,octx -> n_threads , 36460 ,src0_row_size ,src1_row_size ,0 ,kparams -> n_prefetch , true, false); 3647 3648const size_t vtcm_size = L .total_bytes ; 3649 3650FARF (HIGH ,"matmul-id-%s : src0-spad-size %zu src1-spad-size %zu src2-spad-size 0 dst-spad-size %zu (%zu)\n" ,mmctx -> type , 3651L .src0_bytes ,L .src1_bytes ,L .dst_bytes ,vtcm_size ); 3652 3653FARF (HIGH ,"matmul-id-%s : %ux%ux%ux%u * %ux%ux%ux%u (%ux%ux%ux%u) -> %ux%ux%ux%u (0x%p, 0x%p, 0x%p)\n" ,mmctx -> type , 3654src0 -> ne [0 ],src0 -> ne [1 ],src0 -> ne [2 ],src0 -> ne [3 ],src1 -> ne [0 ],src1 -> ne [1 ],src1 -> ne [2 ],src1 -> ne [3 ], 3655ids -> ne [0 ],ids -> ne [1 ],ids -> ne [2 ],ids -> ne [3 ],dst -> ne [0 ],dst -> ne [1 ],dst -> ne [2 ],dst -> ne [3 ],src0 -> data , 3656src1 -> data ,dst -> data ); 3657 3658// Make sure the reserved vtcm size is sufficient 3659if (octx -> ctx -> vtcm_size < vtcm_size ) { 3660FARF (ERROR ,"matmul-id-%s : current VTCM reservation %zu is too small, needed %zu\n" ,mmctx -> type ,octx -> ctx -> vtcm_size ,vtcm_size ); 3661return HTP_STATUS_VTCM_TOO_SMALL ; 3662 } 3663 3664uint8_t * const base = (uint8_t * )octx -> ctx -> vtcm_base ; 3665mmctx -> vtcm_src1 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src1 ); 3666mmctx -> vtcm_src0 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src0 ); 3667mmctx -> vtcm_src2 = NULL ; 3668mmctx -> vtcm_dst = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_dst ); 3669 3670octx -> src1_spad .src = NULL ; 3671octx -> src0_spad .src = NULL ; 3672octx -> src2_spad .src = NULL ; 3673octx -> dst_spad .src = NULL ; 3674 3675mmctx -> vtcm_src0_stride = src0_row_size_padded ; 3676mmctx -> vtcm_src1_stride = src1_row_size ; 3677 3678mmctx -> vtcm_src0_size_per_thread = fastdiv (L .src0_bytes ,& octx -> n_threads_div ); 3679mmctx -> vtcm_src1_size_per_thread = L .src1_bytes ; 3680mmctx -> vtcm_src2_size_per_thread = 0 ; 3681mmctx -> vtcm_dst_size_per_thread = fastdiv (L .dst_bytes ,& octx -> n_threads_div ); 3682 3683mmctx -> n_quant_rows_per_thread = (src1_nrows + n_quant_tasks - 1 ) /n_quant_tasks ; 3684mmctx -> quant_task_func = quant_task_func ; 3685mmctx -> n_quant_tasks = n_quant_tasks ; 3686atomic_init (& mmctx -> quant_barrier ,n_quant_tasks ); 3687 3688htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3689 3690worker_pool_run_func (octx -> ctx -> worker_pool ,hvx_mmid_task_func ,mmctx ,octx -> n_threads ); 3691 3692return HTP_STATUS_OK ; 3693} 3694 3695static int hmx_mm_op_matmul_id_nx ( 3696struct htp_ops_context * octx , 3697struct htp_mm_context * mmctx 3698) { 3699const uint32_t * matrix_row_counts = mmctx -> matrix_row_counts ; 3700const struct mmid_row_mapping * matrix_rows = mmctx -> matrix_rows ; 3701const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3702const uint32_t n_weights = kparams -> n_weights ; 3703const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 3704const struct htp_tensor * restrictact = octx -> src [n_weights ]; 3705const int n_as = src0 -> ne [2 ]; 3706 3707for (uint32_t cur_a = 0 ;cur_a < (uint32_t )n_as ;++ cur_a ) { 3708const int32_t cne1 = matrix_row_counts [cur_a ]; 3709if (cne1 == 0 )continue ; 3710 3711const int m_padded = hex_align_up (cne1 ,32 ); 3712int m_start = 0 ,m_end = m_padded ; 3713if (octx -> ctx -> mdev .count > 1 ) { 3714bool can_split = (uint32_t )cne1 >=octx -> ctx -> mdev .count ; 3715for (uint32_t p = 0 ;p < n_weights && can_split ;++ p ) { 3716const struct htp_tensor * restrictdst = octx -> dsts [p ]; 3717can_split = !dst || htp_tensor_mdev_data_aligned (dst ); 3718 } 3719const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition ((uint32_t )m_padded ,can_split ?1 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 3720m_start = (int )range .start ; 3721m_end = (int ) (range .start + range .count ); 3722 } 3723if (m_start >=m_end )continue ; 3724 3725for (uint32_t p = 0 ;p < n_weights ;++ p ) { 3726const struct htp_tensor * restrictsrc_w = octx -> src [p ]; 3727const struct htp_tensor * restrictdst = octx -> dsts [p ]; 3728if (!src_w || !dst )continue ; 3729 3730int ret = hmx_mm_id_2d_f32 (octx -> ctx , (float * )dst -> data , (float * )act -> data , 3731 (const uint8_t * )src_w -> data + cur_a * src_w -> nb [2 ], 3732cne1 ,src_w -> ne [0 ],src_w -> ne [1 ], 3733act -> ne [0 ], 3734act -> ne [1 ], 3735act -> nb [1 ],act -> nb [2 ], 3736dst -> nb [1 ],dst -> nb [2 ], 3737 (int )src_w -> nb [1 ], (int )src_w -> type , 3738matrix_rows ,cur_a ,mmctx -> mapping_stride , 3739m_start ,m_end , (int )octx -> n_threads ); 3740if (ret != 0 ) { 3741FARF (ERROR ,"HMX matmul ID NX failed for expert %u weight %u, error %d\n" ,cur_a ,p ,ret ); 3742return HTP_STATUS_NO_SUPPORT ; 3743 } 3744 } 3745 } 3746 3747return HTP_STATUS_OK ; 3748} 3749 3750static int hvx_mm_matmul_id_nx ( 3751struct htp_ops_context * octx , 3752struct htp_mm_context * mmctx , 3753work_queue_func_t hvx_mmid_task_func 3754) { 3755const uint32_t src0_row_size_padded = mmctx -> src0_row_size_padded ; 3756const uint32_t src1_nrows = mmctx -> src1_nrows ; 3757 3758struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 3759htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 3760 3761const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3762const uint32_t n_weights = kparams -> n_weights ; 3763const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 3764const struct htp_tensor * restrictact = octx -> src [n_weights ]; 3765const struct htp_tensor * restrictids = octx -> src [n_weights + 1 ]; 3766const size_t src0_row_size = src0 -> nb [1 ]; 3767 3768const uint32_t qk = QK_Q8_0_TILED ; 3769const uint32_t nb = (act -> ne [0 ]+ qk - 1 ) /qk ; 3770const uint32_t total_nb = src1_nrows * nb ; 3771 3772work_queue_func_t quant_task_func ; 3773uint32_t n_quant_tasks = 1 ; 3774if (src1_nrows < octx -> n_threads ) { 3775n_quant_tasks = MIN (total_nb ,octx -> n_threads ); 3776quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled_block :quantize_f32_q8_0_tiled_block ; 3777for (uint32_t ith = 0 ;ith < n_quant_tasks ;++ ith ) { 3778uint32_t ib_first = (total_nb * ith ) /n_quant_tasks ; 3779uint32_t ib_last = (total_nb * (ith + 1 )) /n_quant_tasks ; 3780mmctx -> quant_ib_first [ith ]= ib_first ; 3781mmctx -> quant_ib_last [ith ]= ib_last ; 3782mmctx -> quant_r [ith ]= ib_first /nb ; 3783mmctx -> quant_c [ith ]= ib_first %nb ; 3784 } 3785 }else { 3786n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 3787quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled :quantize_f32_q8_0_tiled ; 3788 } 3789size_t src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (act -> ne [0 ]) :htp_mm_q8_0_tiled_row_size (act -> ne [0 ]); 3790 3791struct htp_mm_hvx_vtcm_layout L ; 3792htp_mm_hvx_vtcm_layout_build (& L ,kparams -> kernel_type ,src0 -> type ,act -> ne [0 ],src1_nrows ,octx -> n_threads , 37930 ,src0_row_size ,src1_row_size ,0 ,kparams -> n_prefetch , true, false); 3794 3795const size_t vtcm_size = L .total_bytes ; 3796 3797if (octx -> ctx -> vtcm_size < vtcm_size ) { 3798FARF (ERROR ,"matmul-id-nx: current VTCM reservation %zu is too small, needed %zu\n" , 3799octx -> ctx -> vtcm_size ,vtcm_size ); 3800return HTP_STATUS_VTCM_TOO_SMALL ; 3801 } 3802 3803uint8_t * const base = (uint8_t * )octx -> ctx -> vtcm_base ; 3804mmctx -> vtcm_src0 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src0 ); 3805mmctx -> vtcm_src1 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src1 ); 3806mmctx -> vtcm_dst = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_dst ); 3807 3808octx -> src0_spad .src = NULL ; 3809octx -> src1_spad .src = NULL ; 3810octx -> src2_spad .src = NULL ; 3811octx -> src3_spad .src = NULL ; 3812octx -> dst_spad .src = NULL ; 3813 3814mmctx -> vtcm_src0_stride = 0 ; 3815mmctx -> vtcm_src1_stride = src1_row_size ; 3816 3817mmctx -> vtcm_src0_size_per_thread = fastdiv (L .src0_bytes ,& octx -> n_threads_div ); 3818mmctx -> vtcm_src1_size_per_thread = L .src1_bytes ; 3819mmctx -> vtcm_dst_size_per_thread = fastdiv (L .dst_bytes ,& octx -> n_threads_div ); 3820 3821mmctx -> n_quant_rows_per_thread = (src1_nrows + n_quant_tasks - 1 ) /n_quant_tasks ; 3822mmctx -> quant_task_func = quant_task_func ; 3823mmctx -> n_quant_tasks = n_quant_tasks ; 3824atomic_init (& mmctx -> quant_barrier ,n_quant_tasks ); 3825 3826FARF (HIGH ,"matmul-id-nx: src0 %d:%d:%d type %s nrows %u, src1 %d:%d:%d nrows %u, vtcm %zu/%zu, threads %d\n" , 3827src0 -> ne [0 ],src0 -> ne [1 ],src0 -> ne [2 ],mmctx -> type ,src0 -> ne [1 ], 3828act -> ne [0 ],act -> ne [1 ],act -> ne [2 ],src1_nrows , 3829L .total_bytes ,octx -> ctx -> vtcm_size ,octx -> n_threads ); 3830 3831htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3832 3833worker_pool_run_func (octx -> ctx -> worker_pool ,hvx_mmid_task_func ,mmctx ,octx -> n_threads ); 3834 3835return HTP_STATUS_OK ; 3836} 3837 3838static inline void scan_expert_ids_n ( 3839const struct htp_tensor * ids , 3840const uint32_t n_ids , 3841uint32_t n_as , 3842uint32_t * counts , 3843struct mmid_row_mapping * matrix_rows , 3844uint32_t mapping_stride 3845) { 3846const size_t ids_nb1 = ids -> nb [1 ]; 3847const uint8_t * ids_data = (const uint8_t * )ids -> data ; 3848 3849for (uint32_t iid1 = 0 ;iid1 < ids -> ne [1 ];++ iid1 ) { 3850const int32_t * row_ptr = (const int32_t * ) (ids_data + iid1 * ids_nb1 ); 3851for (uint32_t id = 0 ;id < n_ids ;++ id ) { 3852const int32_t i02 = row_ptr [id ]; 3853if (i02 < 0 ) { 3854continue ; 3855 } 3856assert (i02 < n_as ); 3857 3858if (matrix_rows ) { 3859matrix_rows [i02 * mapping_stride + counts [i02 ]]= (struct mmid_row_mapping ) {id ,iid1 }; 3860 } 3861counts [i02 ]+= 1 ; 3862 } 3863 } 3864} 3865 3866static inline void scan_expert_ids ( 3867const struct htp_tensor * ids , 3868uint32_t n_ids , 3869uint32_t n_as , 3870uint32_t * counts , 3871struct mmid_row_mapping * matrix_rows , 3872uint32_t mapping_stride 3873) { 3874const size_t ids_nb0 = ids -> nb [0 ]; 3875 3876if (ids_nb0 == 4 ) { 3877switch (n_ids ) { 3878case 8 :scan_expert_ids_n (ids ,8 ,n_as ,counts ,matrix_rows ,mapping_stride );break ; 3879case 4 :scan_expert_ids_n (ids ,4 ,n_as ,counts ,matrix_rows ,mapping_stride );break ; 3880case 2 :scan_expert_ids_n (ids ,2 ,n_as ,counts ,matrix_rows ,mapping_stride );break ; 3881default :scan_expert_ids_n (ids ,n_ids ,n_as ,counts ,matrix_rows ,mapping_stride );break ; 3882 } 3883 }else { 3884// Strided fallback 3885const size_t ids_nb1 = ids -> nb [1 ]; 3886const uint8_t * ids_data = (const uint8_t * )ids -> data ; 3887for (uint32_t iid1 = 0 ;iid1 < ids -> ne [1 ];++ iid1 ) { 3888const int32_t * row_ptr = (const int32_t * ) (ids_data + iid1 * ids_nb1 ); 3889for (uint32_t id = 0 ;id < n_ids ;++ id ) { 3890const int32_t i02 = * (const int32_t * ) ((const uint8_t * )row_ptr + id * ids_nb0 ); 3891if (i02 < 0 ) { 3892continue ; 3893 } 3894assert (i02 < n_as ); 3895 3896if (matrix_rows ) { 3897matrix_rows [i02 * mapping_stride + counts [i02 ]]= (struct mmid_row_mapping ) {id ,iid1 }; 3898 } 3899counts [i02 ]+= 1 ; 3900 } 3901 } 3902 } 3903} 3904 3905int op_matmul_id (struct htp_ops_context * octx ) { 3906htp_matmul_tensors_preamble ; 3907 3908const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 3909struct htp_mm_context mmctx_struct = {0 }; 3910struct htp_mm_context * mmctx = & mmctx_struct ; 3911 3912const int status = htp_mm_init_context (octx ,kparams ); 3913if (status != HTP_STATUS_OK ) { 3914return status ; 3915 } 3916 3917struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 3918htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 3919 3920mmctx -> octx = octx ; 3921mmctx -> act = src1 ; 3922 3923const struct htp_tensor * restrictids = octx -> src [2 ]; 3924 3925const size_t src0_row_size = nb01 ; 3926const size_t dst_row_size = nb1 ; 3927 3928const size_t src0_row_size_padded = hex_round_up (src0_row_size ,128 ); 3929 3930const uint32_t src0_nrows = ne01 ;// per expert 3931const uint32_t src1_nrows = ne11 * ne12 * ne13 ; 3932 3933// row groups 3934const int n_ids = ids -> ne [0 ];// n_expert_used 3935const int n_as = ne02 ;// n_expert 3936 3937uint8_t * mapping_buf = octx -> ctx -> ddr_spad_base ; 3938uint32_t mapping_stride = 1 ; 3939uint32_t * matrix_row_counts = (uint32_t * )mapping_buf ; 3940struct mmid_row_mapping * matrix_rows = NULL ; 3941 3942if (src1_nrows > 1 ) { 3943const size_t matrix_row_counts_size = n_as * sizeof (uint32_t ); 3944assert (octx -> ctx -> ddr_spad_size >=matrix_row_counts_size ); 3945 3946hex_l2fetch_block ((const void * )ids -> data ,ids -> ne [1 ]* ids -> nb [1 ]); 3947 3948memset (matrix_row_counts ,0 ,matrix_row_counts_size ); 3949scan_expert_ids (ids ,n_ids ,n_as ,matrix_row_counts ,NULL ,0 ); 3950 3951uint32_t max_count = hvx_reduce_max_i32 ((const uint8_t * )matrix_row_counts ,n_as ); 3952mapping_stride = max_count > 0 ?max_count :1 ; 3953 3954size_t matrix_row_map_size = n_as * mapping_stride * sizeof (struct mmid_row_mapping ); 3955const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size ; 3956 3957if (total_map_size > octx -> ctx -> ddr_spad_size ) { 3958mapping_buf = memalign (128 ,total_map_size ); 3959if (!mapping_buf ) { 3960return HTP_STATUS_INTERNAL_ERR ; 3961 } 3962 } 3963 3964matrix_row_counts = (uint32_t * )mapping_buf ; 3965matrix_rows = (struct mmid_row_mapping * ) (mapping_buf + matrix_row_counts_size ); 3966 3967memset (matrix_row_counts ,0 ,n_as * sizeof (uint32_t )); 3968scan_expert_ids (ids ,n_ids ,n_as ,matrix_row_counts ,matrix_rows ,mapping_stride ); 3969 } 3970 3971mmctx -> matrix_row_counts = matrix_row_counts ; 3972mmctx -> matrix_rows = matrix_rows ; 3973mmctx -> mapping_stride = mapping_stride ; 3974mmctx -> mm_div_ne11 = kparams -> div_ne11 ; 3975mmctx -> src0_row_size_padded = src0_row_size_padded ; 3976mmctx -> src1_nrows = src1_nrows ; 3977 3978htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 3979 3980int s ; 3981if (kparams -> n_hmx ) { 3982s = hmx_mm_op_matmul_id (octx ,mmctx ); 3983 }else { 3984uint32_t src0_row_start = 0 ; 3985uint32_t src0_row_end = src0_nrows ; 3986if (octx -> ctx -> mdev .count > 1 ) { 3987const bool can_split = htp_tensor_can_row_partition (dst ,sizeof (float )); 3988const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition (src0_nrows ,can_split ?32 :0 ,octx -> ctx -> mdev .idx ,octx -> ctx -> mdev .count ,& octx -> ctx -> mdev .count_div ); 3989src0_row_start = range .start ; 3990src0_row_end = range .start + range .count ; 3991 } 3992 3993if (src0_row_start >=src0_row_end ) { 3994if (mapping_buf != octx -> ctx -> ddr_spad_base ) { 3995free (mapping_buf ); 3996 } 3997return HTP_STATUS_OK ; 3998 } 3999 4000const uint32_t nrows = src0_row_end - src0_row_start ; 4001mmctx -> src0_row_start = src0_row_start ; 4002mmctx -> src0_row_end = src0_row_end ; 4003 4004mmctx -> src0_nrows_per_thread = fastdiv (nrows + octx -> n_threads - 1 ,& octx -> n_threads_div ); 4005mmctx -> src0_nrows_per_thread = hex_round_up (mmctx -> src0_nrows_per_thread ,32 ); 4006 4007if (hvx_mm_init_vec_dot (mmctx ,src0 -> type )== 0 ) { 4008s = hvx_mm_matmul_id (octx ,mmctx ,src1_nrows > 1 ?hvx_mm_id :hvx_mv_id ); 4009 }else { 4010s = HTP_STATUS_NO_SUPPORT ; 4011 } 4012 } 4013 4014if (mapping_buf != octx -> ctx -> ddr_spad_base ) { 4015free (mapping_buf ); 4016 } 4017 4018return s ; 4019} 4020 4021int op_matmul_id_nx (struct htp_ops_context * octx ) { 4022const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 4023struct htp_mm_context mmctx_struct = {0 }; 4024struct htp_mm_context * mmctx = & mmctx_struct ; 4025 4026const int status = htp_mm_init_context (octx ,kparams ); 4027if (status != HTP_STATUS_OK ) { 4028return status ; 4029 } 4030 4031struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 4032htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 4033 4034mmctx -> octx = octx ; 4035const uint32_t n_weights = kparams -> n_weights ; 4036const struct htp_tensor * restrictsrc0 = octx -> src [0 ]; 4037const struct htp_tensor * restrictact = octx -> src [n_weights ]; 4038const struct htp_tensor * restrictids = octx -> src [n_weights + 1 ]; 4039 4040mmctx -> act = act ; 4041 4042const size_t src0_row_size = src0 -> nb [1 ]; 4043const size_t src0_row_size_padded = hex_round_up (src0_row_size ,128 ); 4044 4045const uint32_t src1_nrows = act -> ne [1 ]* act -> ne [2 ]* act -> ne [3 ]; 4046 4047const int n_ids = ids -> ne [0 ]; 4048const int n_as = src0 -> ne [2 ]; 4049 4050uint8_t * mapping_buf = octx -> ctx -> ddr_spad_base ; 4051uint32_t mapping_stride = 1 ; 4052uint32_t * matrix_row_counts = (uint32_t * )mapping_buf ; 4053struct mmid_row_mapping * matrix_rows = NULL ; 4054 4055if (src1_nrows > 1 ) { 4056const size_t matrix_row_counts_size = n_as * sizeof (uint32_t ); 4057assert (octx -> ctx -> ddr_spad_size >=matrix_row_counts_size ); 4058 4059hex_l2fetch_block ((const void * )ids -> data ,ids -> ne [1 ]* ids -> nb [1 ]); 4060 4061memset (matrix_row_counts ,0 ,matrix_row_counts_size ); 4062scan_expert_ids (ids ,n_ids ,n_as ,matrix_row_counts ,NULL ,0 ); 4063 4064uint32_t max_count = hvx_reduce_max_i32 ((const uint8_t * )matrix_row_counts ,n_as ); 4065mapping_stride = max_count > 0 ?max_count :1 ; 4066 4067size_t matrix_row_map_size = n_as * mapping_stride * sizeof (struct mmid_row_mapping ); 4068const size_t total_map_size = matrix_row_counts_size + matrix_row_map_size ; 4069 4070if (total_map_size > octx -> ctx -> ddr_spad_size ) { 4071mapping_buf = memalign (128 ,total_map_size ); 4072if (!mapping_buf ) { 4073return HTP_STATUS_INTERNAL_ERR ; 4074 } 4075 } 4076 4077matrix_row_counts = (uint32_t * )mapping_buf ; 4078matrix_rows = (struct mmid_row_mapping * ) (mapping_buf + matrix_row_counts_size ); 4079 4080memset (matrix_row_counts ,0 ,n_as * sizeof (uint32_t )); 4081scan_expert_ids (ids ,n_ids ,n_as ,matrix_row_counts ,matrix_rows ,mapping_stride ); 4082 } 4083 4084mmctx -> matrix_row_counts = matrix_row_counts ; 4085mmctx -> matrix_rows = matrix_rows ; 4086mmctx -> mapping_stride = mapping_stride ; 4087mmctx -> mm_div_ne11 = kparams -> div_ne11 ; 4088mmctx -> src0_row_size_padded = src0_row_size_padded ; 4089mmctx -> src1_nrows = src1_nrows ; 4090 4091htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 4092 4093int s ; 4094if (kparams -> n_hmx ) { 4095s = hmx_mm_op_matmul_id_nx (octx ,mmctx ); 4096 }else { 4097if (hvx_mm_init_vec_dot (mmctx ,src0 -> type )== 0 ) { 4098s = hvx_mm_matmul_id_nx (octx ,mmctx ,src1_nrows > 1 ?hvx_mm_id_nx :hvx_mv_id_nx ); 4099 }else { 4100s = HTP_STATUS_NO_SUPPORT ; 4101 } 4102 } 4103 4104if (mapping_buf != octx -> ctx -> ddr_spad_base ) { 4105free (mapping_buf ); 4106 } 4107 4108return s ; 4109} 4110int op_matmul_nx (struct htp_ops_context * octx ) { 4111const struct htp_mm_kernel_params * kparams = (const struct htp_mm_kernel_params * )octx -> kernel_params ; 4112 4113const int status = htp_mm_init_context (octx ,kparams ); 4114if (status != HTP_STATUS_OK ) { 4115return status ; 4116 } 4117 4118if (kparams -> n_hmx ) { 4119return hmx_mm_nx_2d_f32 (octx ,kparams ); 4120 } 4121 4122struct htp_thread_trace * tr = & octx -> ctx -> trace [0 ]; 4123htp_trace_event_start (tr ,HTP_TRACE_EVT_INIT ,0 ); 4124 4125const uint32_t n_weights = kparams -> n_weights ; 4126 4127const struct htp_tensor * restrictsrc0 = octx -> src [0 ];// first weight 4128const struct htp_tensor * restrictact = octx -> src [n_weights ];// activation x 4129 4130bool is_repacked = (src0 -> type == HTP_TYPE_Q4_0 || src0 -> type == HTP_TYPE_Q4_1 || 4131src0 -> type == HTP_TYPE_Q8_0 || src0 -> type == HTP_TYPE_IQ4_NL || 4132src0 -> type == HTP_TYPE_MXFP4 ); 4133 4134struct htp_mm_context mmctx_struct = {0 }; 4135struct htp_mm_context * mmctx = & mmctx_struct ; 4136mmctx -> octx = octx ; 4137mmctx -> act = act ; 4138 4139const uint32_t src1_nrows = act -> ne [1 ]* act -> ne [2 ]* act -> ne [3 ]; 4140 4141const size_t src0_row_size = src0 -> nb [1 ]; 4142const size_t src0_row_size_padded = hex_round_up (src0_row_size ,128 ); 4143 4144if (hvx_mm_init_vec_dot (mmctx ,src0 -> type )!= 0 ) { 4145return HTP_STATUS_NO_SUPPORT ; 4146 } 4147 4148const uint32_t qk = QK_Q8_0_TILED ; 4149const uint32_t nb = (act -> ne [0 ]+ qk - 1 ) /qk ; 4150const uint32_t total_nb = src1_nrows * nb ; 4151 4152worker_callback_t quant_task_func ; 4153uint32_t n_quant_tasks = 1 ; 4154if (kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ) { 4155n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 4156quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_flat :quantize_f32_q8_0_flat ; 4157 }else if (src1_nrows < octx -> n_threads ) { 4158n_quant_tasks = MIN (total_nb ,octx -> n_threads ); 4159quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled_block :quantize_f32_q8_0_tiled_block ; 4160for (uint32_t ith = 0 ;ith < n_quant_tasks ;++ ith ) { 4161uint32_t ib_first = (total_nb * ith ) /n_quant_tasks ; 4162uint32_t ib_last = (total_nb * (ith + 1 )) /n_quant_tasks ; 4163mmctx -> quant_ib_first [ith ]= ib_first ; 4164mmctx -> quant_ib_last [ith ]= ib_last ; 4165mmctx -> quant_r [ith ]= ib_first /nb ; 4166mmctx -> quant_c [ith ]= ib_first %nb ; 4167 } 4168 }else { 4169n_quant_tasks = MIN (src1_nrows ,octx -> n_threads ); 4170quant_task_func = (src0 -> type == HTP_TYPE_Q4_1 ) ?quantize_f32_q8_1_tiled :quantize_f32_q8_0_tiled ; 4171 } 4172 4173size_t src1_row_size ; 4174if (kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ) { 4175src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_flat_row_size (act -> ne [0 ]) :htp_mm_q8_0_flat_row_size (act -> ne [0 ]); 4176 }else { 4177src1_row_size = (src0 -> type == HTP_TYPE_Q4_1 ) ?htp_mm_q8_1_tiled_row_size (act -> ne [0 ]) :htp_mm_q8_0_tiled_row_size (act -> ne [0 ]); 4178 } 4179 4180struct htp_mm_hvx_vtcm_layout L ; 4181htp_mm_hvx_vtcm_layout_build (& L ,kparams -> kernel_type ,src0 -> type ,act -> ne [0 ],src1_nrows ,octx -> n_threads , 41820 ,src0_row_size ,src1_row_size ,0 ,kparams -> n_prefetch , false, true); 4183 4184const size_t vtcm_size = L .total_bytes ; 4185 4186if (octx -> ctx -> vtcm_size < vtcm_size ) { 4187FARF (ERROR ,"matmul-nx: current VTCM reservation %zu is too small, needed %zu\n" , 4188octx -> ctx -> vtcm_size ,vtcm_size ); 4189return HTP_STATUS_VTCM_TOO_SMALL ; 4190 } 4191 4192uint8_t * const base = (uint8_t * )octx -> ctx -> vtcm_base ; 4193mmctx -> vtcm_src0 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src0 ); 4194mmctx -> vtcm_src1 = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_src1 ); 4195mmctx -> vtcm_dst = VTCM_LAYOUT_PTR (uint8_t ,base ,L .off_dst ); 4196 4197octx -> src0_spad .src = NULL ; 4198octx -> src1_spad .src = NULL ; 4199octx -> src2_spad .src = NULL ; 4200octx -> src3_spad .src = NULL ; 4201octx -> dst_spad .src = NULL ; 4202 4203mmctx -> vtcm_src0_stride = is_repacked ?0 :src0_row_size_padded ; 4204mmctx -> vtcm_src1_stride = src1_row_size ; 4205 4206mmctx -> vtcm_src0_size_per_thread = fastdiv (L .src0_bytes ,& octx -> n_threads_div ); 4207mmctx -> vtcm_src1_size_per_thread = L .src1_bytes ; 4208mmctx -> vtcm_dst_size_per_thread = fastdiv (L .dst_bytes ,& octx -> n_threads_div ); 4209 4210mmctx -> n_quant_rows_per_thread = (src1_nrows + n_quant_tasks - 1 ) /n_quant_tasks ; 4211mmctx -> quant_task_func = quant_task_func ; 4212mmctx -> n_quant_tasks = n_quant_tasks ; 4213atomic_init (& mmctx -> quant_barrier ,n_quant_tasks ); 4214 4215// Run fused matmul 4216const uint32_t n_matmul_jobs = octx -> n_threads ; 4217worker_callback_t matmul_job_func ; 4218if (is_repacked ) { 4219if (kparams -> kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW_FLAT ) { 4220switch (src0 -> type ) { 4221case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mm_nx_2d_repacked_q4_0_flat ;break ; 4222case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mm_nx_2d_repacked_q4_1_flat ;break ; 4223case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mm_nx_2d_repacked_q8_0_flat ;break ; 4224case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mm_nx_2d_repacked_iq4nl_flat ;break ; 4225case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mm_nx_2d_repacked_mxfp4_flat ;break ; 4226default :return HTP_STATUS_NO_SUPPORT ; 4227 } 4228 }else { 4229switch (src0 -> type ) { 4230case HTP_TYPE_Q4_0 :matmul_job_func = hvx_mm_nx_2d_repacked_q4_0 ;break ; 4231case HTP_TYPE_Q4_1 :matmul_job_func = hvx_mm_nx_2d_repacked_q4_1 ;break ; 4232case HTP_TYPE_Q8_0 :matmul_job_func = hvx_mm_nx_2d_repacked_q8_0 ;break ; 4233case HTP_TYPE_IQ4_NL :matmul_job_func = hvx_mm_nx_2d_repacked_iq4nl ;break ; 4234case HTP_TYPE_MXFP4 :matmul_job_func = hvx_mm_nx_2d_repacked_mxfp4 ;break ; 4235default :return HTP_STATUS_NO_SUPPORT ; 4236 } 4237 } 4238 }else { 4239matmul_job_func = hvx_mm_nx_2d ; 4240 } 4241 4242htp_trace_event_stop (tr ,HTP_TRACE_EVT_INIT ,0 ); 4243 4244worker_pool_run_func (octx -> ctx -> worker_pool ,matmul_job_func ,mmctx ,n_matmul_jobs ); 4245 4246return HTP_STATUS_OK ; 4247}