77
88#include "hex-fastdiv.h"
99#include "hex-common.h"
10+ #include "htp-vtcm.h"
1011
1112#ifdef __cplusplus
1213extern "C" {
1314#endif
1415
1516// Tile constants (mirrored from hmx-utils.h for use on host side if needed)
17+ #define HTP_FA_HMX_TILE_SIZE 2048
18+ #define HMX_FP16_TILE_SIZE 2048
1619#define HMX_FP16_TILE_N_ROWS 32
1720#define HMX_FP16_TILE_N_COLS 32
1821#define HMX_FP16_TILE_N_ELMS 1024
19- #define HMX_FP16_TILE_SIZE 2048
22+
2023#define HVX_FA_DMA_CACHE_SIZE 128
2124#define HMX_FA_DMA_CACHE_SIZE 4
2225
26+
2327#define HTP_FA_M_INITIAL_VAL -10000.0f
2428
2529enum htp_fa_kernel_type {
@@ -54,6 +58,11 @@ struct htp_fa_kernel_params {
5458 struct fastdiv_values src3_div2 ;
5559 struct fastdiv_values src3_div3 ;
5660
61+ struct fastdiv_values broadcast_rk2 ;
62+ struct fastdiv_values broadcast_rk3 ;
63+ struct fastdiv_values broadcast_rv2 ;
64+ struct fastdiv_values broadcast_rv3 ;
65+
5766 union {
5867 struct {
5968 uint32_t g_br ;
@@ -69,10 +78,6 @@ struct htp_fa_kernel_params {
6978 uint32_t size_v_row_padded ;
7079 struct fastdiv_values src0_div21 ;
7180 struct fastdiv_values src0_div1 ;
72- struct fastdiv_values broadcast_rk2 ;
73- struct fastdiv_values broadcast_rk3 ;
74- struct fastdiv_values broadcast_rv2 ;
75- struct fastdiv_values broadcast_rv3 ;
7681 } hvx ;
7782 } u ;
7883};
@@ -81,39 +86,124 @@ struct htp_fa_kernel_params {
8186static_assert (sizeof (struct htp_fa_kernel_params ) <= 128 , "htp_fa_kernel_params is too large for kernel_params blob" );
8287#endif
8388
84- // Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
85- // g_br = hex_align_up(gqa_factor * Br, 32) replaces Br for all Q/O/S/P/D dimensions.
86- // Layout: Q + O_ping + O_pong + K_dma*2 + V_dma*2 + K_tile + V_tile + S + P + D + vectors + scales
87- // Mask is DMA'd into a VTCM buffer (Br rows per KV block) to avoid DDR reads in softmax.
88- static inline size_t hmx_fa_compute_vtcm_usage (size_t gqa_factor , size_t DK , size_t DV , size_t Br , size_t Bc , size_t n_threads , bool pipeline ) {
89+ // VTCM region layout for the HMX flash-attention kernel.
90+ //
91+ // Single source of truth for both the host (which needs the total size to pick a
92+ // (Br, Bc) tiling that fits the VTCM budget) and the device (which needs the actual
93+ // byte offsets to place each scratch buffer). Building the layout once and reading
94+ // offsets/total from it makes host estimate and device allocation impossible to
95+ // desync -- previously they were duplicated formulas in two files and drifted.
96+ //
97+ // All fields are byte offsets / byte sizes -- no HVX_Vector type is named here so the
98+ // header stays host-includable. The device casts (base + off_*) to the proper type.
99+ // An offset of 0 marks a region that is not allocated for this configuration (only
100+ // off_v_tiles[1], which exists only when pipelining); the device sets such pointers NULL.
101+ struct hmx_fa_vtcm_layout {
102+ // Byte offsets from vtcm_base for each region.
103+ size_t off_q_tiles ;
104+ size_t off_o_tiles [2 ];
105+ size_t off_k_fp16 [2 ];
106+ size_t off_v_fp16 [2 ];
107+ size_t off_k_tiles ;
108+ size_t off_v_tiles [2 ]; // [1] allocated only when pipeline, else 0
109+ size_t off_s_tiles ;
110+ size_t off_p_tiles ;
111+ size_t off_d_tiles ;
112+ size_t off_m_vec ;
113+ size_t off_l_vec ;
114+ size_t off_s_rowmax ;
115+ size_t off_p_rowsum ;
116+ size_t off_row_bufs ;
117+ size_t off_hmx_scales_id ;
118+ size_t off_hmx_scales_qk ;
119+ size_t off_mask_buf ;
120+ size_t off_slopes ;
121+
122+ // Region byte sizes reused by the device at runtime (not just for allocation).
123+ size_t q_tile_bytes ;
124+ size_t o_tile_bytes ;
125+ size_t s_tile_bytes ; // S and P tiles (same size)
126+ size_t d_tile_bytes ;
127+ size_t m_line_bytes ; // one mask row
128+ size_t m_buf_slot_bytes ; // one dma_cache slot = align_up(Br * m_line_bytes, 4096)
129+ size_t col_vec_bytes ;
130+
131+ // Derived strides.
132+ size_t row_buf_stride ; // HVX vectors (128B) per row buffer
133+ size_t mask_buf_row_stride ; // __fp16 elements per row in the mask buffer
134+
135+ bool pipeline ;
136+ size_t total_bytes ;
137+ };
138+
139+ // Build the VTCM layout.
140+
141+ static inline void hmx_fa_vtcm_layout_build (struct hmx_fa_vtcm_layout * L ,
142+ size_t gqa_factor , size_t DK , size_t DV ,
143+ size_t Br , size_t Bc , size_t n_threads , bool pipeline ) {
89144 const size_t g_br = hex_align_up (gqa_factor * Br , HMX_FP16_TILE_N_ROWS );
90- const size_t q_tile_size = hex_align_up (g_br * DK * sizeof (__fp16 ), 4096 ); // Q: [g_br, DK]
91- const size_t o_tile_size = hex_align_up (g_br * DV * sizeof (__fp16 ), 4096 ); // O: [g_br, DV] x2 ping-pong
92- const size_t k_dma_size = hex_align_up (Bc * hex_round_up (DK * sizeof (__fp16 ), 128 ), 4096 ); // K DMA: [Bc, DK] x2 double-buf
93- const size_t v_dma_size = hex_align_up (Bc * hex_round_up (DV * sizeof (__fp16 ), 128 ), 4096 ); // V DMA: [Bc, DV] x2 double-buf
94- const size_t k_tile_size = hex_align_up (Bc * DK * sizeof (__fp16 ), 4096 ); // K tiles: [Bc, DK] interleaved
95- const size_t v_tile_size = hex_align_up (Bc * DV * sizeof (__fp16 ), 4096 ); // V tiles: [Bc, DV] interleaved
96- const size_t s_tile_size = hex_align_up (g_br * Bc * sizeof (__fp16 ), 4096 ); // S/P:[g_br, Bc]
97- const size_t d_tile_size = hex_align_up (g_br * g_br * sizeof (__fp16 ), 4096 ); // D: [g_br, g_br]
98- const size_t col_vec_size = hex_align_up (g_br * sizeof (float ), 256 ); // m, l, etc.
99- const size_t row_vec_size = hex_align_up (Bc * sizeof (__fp16 ), 256 );
100- const size_t m_line_size = hex_align_up (Bc * sizeof (__fp16 ), 128 );
101- const size_t m_buf_size = hex_align_up (Br * m_line_size , 4096 ) * HMX_FA_DMA_CACHE_SIZE ;
145+ const size_t q_tile_size = hex_align_up (g_br * DK * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
146+ const size_t o_tile_size = hex_align_up (g_br * DV * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
147+ const size_t k_tile_size = hex_align_up (Bc * DK * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
148+ const size_t v_tile_size = hex_align_up (Bc * DV * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
149+ const size_t s_tile_size = hex_align_up (g_br * Bc * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
150+ const size_t d_tile_size = hex_align_up (g_br * g_br * sizeof (__fp16 ), HTP_FA_HMX_TILE_SIZE );
151+
152+ const size_t k_dma_size = hex_align_up (Bc * hex_round_up (DK * sizeof (__fp16 ), 128 ), 128 );
153+ const size_t v_dma_size = hex_align_up (Bc * hex_round_up (DV * sizeof (__fp16 ), 128 ), 128 );
154+ const size_t col_vec_size = hex_align_up (g_br * sizeof (float ), 256 );
155+ const size_t row_vec_size = hex_align_up (Bc * sizeof (__fp16 ), 256 );
156+ const size_t m_line_size = hex_align_up (Bc * sizeof (__fp16 ), 128 );
157+ const size_t m_buf_slot = hex_align_up (Br * m_line_size , 256 );
158+ const size_t m_buf_size = m_buf_slot * HMX_FA_DMA_CACHE_SIZE ;
102159 const size_t slopes_size = hex_align_up (g_br * sizeof (__fp16 ), 128 );
103160
104- return q_tile_size * 1 // Q tiles
105- + o_tile_size * 2 // O ping-pong
106- + k_dma_size * 2 // K DMA x2
107- + v_dma_size * 2 // V DMA x2
108- + k_tile_size * 1 // K tiles
109- + v_tile_size * (pipeline ? 2 : 1 ) // V tiles (double-buffered if pipelining)
110- + s_tile_size * 2 // S + P
111- + d_tile_size * 1 // D (diagonal matrix)
112- + col_vec_size * 4 // m_vec, l_vec, s_rowmax, p_rowsum
113- + row_vec_size * 2 * n_threads // per-thread softmax row scratch
114- + m_buf_size * 1 // mask VTCM buffer [Br rows]
115- + slopes_size // Slopes
116- + 256 * 2 ; // HMX scales (id + qk)
161+ size_t off = 0 ;
162+
163+ // Section 1: HMX Tiled Buffers (FA_HMX_TILE_SIZE = 2KB Aligned)
164+ VTCM_LAYOUT_ALLOC (off , off_q_tiles , q_tile_size );
165+ VTCM_LAYOUT_ALLOC (off , off_o_tiles [0 ], o_tile_size );
166+ VTCM_LAYOUT_ALLOC (off , off_o_tiles [1 ], o_tile_size );
167+ VTCM_LAYOUT_ALLOC (off , off_k_tiles , k_tile_size );
168+ VTCM_LAYOUT_ALLOC (off , off_v_tiles [0 ], v_tile_size );
169+ VTCM_LAYOUT_ALLOC_OPTIONAL (off , off_v_tiles [1 ], v_tile_size , pipeline );
170+ VTCM_LAYOUT_ALLOC (off , off_s_tiles , s_tile_size );
171+ VTCM_LAYOUT_ALLOC (off , off_p_tiles , s_tile_size );
172+ VTCM_LAYOUT_ALLOC (off , off_d_tiles , d_tile_size );
173+
174+ // Section 2: HVX/DMA flat and vector buffers (128B / 256B Aligned)
175+ VTCM_LAYOUT_ALLOC (off , off_k_fp16 [0 ], k_dma_size );
176+ VTCM_LAYOUT_ALLOC (off , off_k_fp16 [1 ], k_dma_size );
177+ VTCM_LAYOUT_ALLOC (off , off_v_fp16 [0 ], v_dma_size );
178+ VTCM_LAYOUT_ALLOC (off , off_v_fp16 [1 ], v_dma_size );
179+ VTCM_LAYOUT_ALLOC (off , off_m_vec , col_vec_size );
180+ VTCM_LAYOUT_ALLOC (off , off_l_vec , col_vec_size );
181+ VTCM_LAYOUT_ALLOC (off , off_s_rowmax , col_vec_size );
182+ VTCM_LAYOUT_ALLOC (off , off_p_rowsum , col_vec_size );
183+ VTCM_LAYOUT_ALLOC (off , off_row_bufs , row_vec_size * 2 * n_threads );
184+ VTCM_LAYOUT_ALLOC (off , off_hmx_scales_id , 256 );
185+ VTCM_LAYOUT_ALLOC (off , off_hmx_scales_qk , 256 );
186+ VTCM_LAYOUT_ALLOC (off , off_mask_buf , m_buf_size );
187+ VTCM_LAYOUT_ALLOC (off , off_slopes , slopes_size );
188+
189+ L -> q_tile_bytes = q_tile_size ;
190+ L -> o_tile_bytes = o_tile_size ;
191+ L -> col_vec_bytes = col_vec_size ;
192+ L -> s_tile_bytes = s_tile_size ;
193+ L -> d_tile_bytes = d_tile_size ;
194+ L -> m_line_bytes = m_line_size ;
195+ L -> m_buf_slot_bytes = m_buf_slot ;
196+ L -> row_buf_stride = row_vec_size / 128 ;
197+ L -> mask_buf_row_stride = m_line_size / sizeof (__fp16 );
198+ L -> pipeline = pipeline ;
199+ L -> total_bytes = off ;
200+ }
201+
202+ // Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
203+ static inline size_t hmx_fa_compute_vtcm_usage (size_t gqa_factor , size_t DK , size_t DV , size_t Br , size_t Bc , size_t n_threads , bool pipeline ) {
204+ struct hmx_fa_vtcm_layout L ;
205+ hmx_fa_vtcm_layout_build (& L , gqa_factor , DK , DV , Br , Bc , n_threads , pipeline );
206+ return L .total_bytes ;
117207}
118208
119209#define FA_HVX_BLOCK_SIZE 64
@@ -153,23 +243,8 @@ static inline int hmx_fa_find_chunk_size(size_t * Br_out,
153243 const size_t T = HMX_FP16_TILE_N_ROWS ; // 32
154244 const size_t br_unit = hmx_ceil_div (T , gqa_factor );
155245 const size_t bc_unit = HMX_FP16_TILE_N_COLS * 2 ; // 64
156- const size_t fp16 = sizeof (__fp16 );
157246 const bool can_pipeline = (kv_len >= FA_MIN_KV_BLOCKS * bc_unit && n_threads >= 2 );
158247
159- // Approximate per-unit VTCM costs (without per-buffer alignment padding).
160- const size_t per_gbr = (DK + 2 * DV ) * fp16 + 4 * sizeof (float ); // Q + O*2 + 4 col vectors
161- const size_t per_gbr2 = fp16 ; // D diagonal matrix
162- const size_t per_bc =
163- 3 * DK * fp16 + (can_pipeline ? 4 : 3 ) * DV * fp16 + 2 * n_threads * fp16 ; // K/V DMA x2 + tiles + row bufs
164- const size_t per_gbr_bc = 2 * fp16 ; // S + P
165-
166- const size_t overhead = 256 * 2 + 13 * 4096 ;
167-
168- if (vtcm_budget <= overhead ) {
169- return -1 ;
170- }
171- const size_t usable = vtcm_budget - overhead ;
172-
173248 // Br_max: largest Br aligned to br_unit that does not exceed qo_len.
174249 const size_t Br_max = qo_len >= br_unit ? hex_align_down (qo_len , br_unit ) : br_unit ;
175250
@@ -185,59 +260,34 @@ static inline int hmx_fa_find_chunk_size(size_t * Br_out,
185260 size_t best_Br = 0 , best_Bc = 0 ;
186261
187262 for (size_t Br = Br_max ; Br >= br_unit ; Br -= br_unit ) {
188- const size_t g_br = hex_align_up (gqa_factor * Br , T );
189-
190- // g_br-dependent VTCM cost: g_br * per_gbr + g_br*g_br * per_gbr2
191- const size_t gbr_cost = g_br * per_gbr + g_br * g_br * per_gbr2 ;
192- if (gbr_cost >= usable ) {
193- if (Br == br_unit ) {
194- break ;
195- }
196- continue ;
197- }
198-
199- // Analytically solve for max Bc:
200- // remain >= Bc * (per_bc + g_br * per_gbr_bc + Br * fp16 * HMX_FA_DMA_CACHE_SIZE)
201- // The Br * fp16 term accounts for the VTCM mask buffer [Br * Bc].
202- const size_t remain = usable - gbr_cost ;
203- const size_t bc_denom = per_bc + g_br * per_gbr_bc + Br * fp16 * HMX_FA_DMA_CACHE_SIZE ;
204- size_t Bc = hex_smin (hex_align_down (remain / bc_denom , bc_unit ), Bc_limit );
205- if (Bc < bc_unit ) {
206- if (Br == br_unit ) {
263+ // Try all Bc candidates from Bc_limit down to bc_unit
264+ for (size_t Bc = Bc_limit ; Bc >= bc_unit ; Bc -= bc_unit ) {
265+ size_t vtcm_needed = hmx_fa_compute_vtcm_usage (gqa_factor , DK , DV , Br , Bc , n_threads , can_pipeline );
266+ if (vtcm_needed <= vtcm_budget ) {
267+ // This Bc fits for this Br!
268+ const size_t q_blocks = (qo_len + Br - 1 ) / Br ;
269+ const size_t kv_blocks = (kv_len + Bc - 1 ) / Bc ;
270+ const size_t cost = q_blocks * (c_q_fixed + kv_blocks * c_iter_fixed );
271+ const size_t mn = Br * Bc ;
272+
273+ if (cost < best_cost || (cost == best_cost && mn > best_mn )) {
274+ best_cost = cost ;
275+ best_mn = mn ;
276+ best_Br = Br ;
277+ best_Bc = Bc ;
278+ }
279+ // Since we iterate Bc from largest to smallest, this is the largest Bc that fits
280+ // for this Br. We can break to the next Br.
207281 break ;
208282 }
209- continue ;
210- }
211-
212- // Exact VTCM verification (alignment padding may push over budget)
213- while (Bc >= bc_unit && hmx_fa_compute_vtcm_usage (gqa_factor , DK , DV , Br , Bc , n_threads , can_pipeline ) > vtcm_budget ) {
214- Bc -= bc_unit ;
215- }
216- if (Bc < bc_unit ) {
217- if (Br == br_unit ) {
218- break ;
219- }
220- continue ;
221- }
222-
223- const size_t q_blocks = (qo_len + Br - 1 ) / Br ;
224- const size_t kv_blocks = (kv_len + Bc - 1 ) / Bc ;
225- const size_t cost = q_blocks * (c_q_fixed + kv_blocks * c_iter_fixed );
226- const size_t mn = Br * Bc ;
227-
228- if (cost < best_cost || (cost == best_cost && mn > best_mn )) {
229- best_cost = cost ;
230- best_mn = mn ;
231- best_Br = Br ;
232- best_Bc = Bc ;
233283 }
234284
235285 if (Br == br_unit ) {
236286 break ;
237287 }
238288 }
239289
240- if (best_Br == 0 ) {
290+ if (best_Br == 0 || best_Bc == 0 ) {
241291 return -1 ;
242292 }
243293
0 commit comments