File size: 8,150 Bytes
58ba6df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// SPDX-FileCopyrightText: © 2026 Tenstorrent Inc.
// SPDX-License-Identifier: Apache-2.0
// hgemv compute, round 6 (see reader.cpp).  One output tile per core.
//   HG_LN (K = 1024: the whole vector is ONE dense tile): mean = sum(x) / K, d = x - mean, rstd = rsqrt(sum(d^2) / K
//          + eps), h = d * rstd (scalar broadcasts; gamma / beta are folded into W / b on the host) -> c_hd (bf16),
//          which the writer spreads into the row-form operand c_hrow.
//   y = sum_k hrow_k @ W_k (fp32 DST)  (+ bias)  [GELU, the model's erf form]  [+ residual]  -> c_out
// compile-time: KT, inv_k bits, eps bits
#include <cstdint>
#include "api/compute/matmul.h"
#include "api/compute/reduce.h"
#include "api/compute/bcast.h"
#include "api/compute/eltwise_binary.h"
#include "api/compute/eltwise_binary_sfpu.h"
#include "api/compute/tile_move_copy.h"
#include "api/compute/eltwise_unary/eltwise_unary.h"
#include "api/compute/eltwise_unary/rsqrt.h"
#include "api/compute/eltwise_unary/binop_with_scalar.h"
#include "api/compute/compute_kernel_hw_startup.h"
#include "api/compute/reconfig_data_format.h"
#ifdef HG_PROF
#include "tools/profiler/kernel_profiler.hpp"
#define PZ(n) DeviceZoneScopedN(n)
#else
#define PZ(n)
#endif
#ifdef HG_GELU
#include "api/compute/common.h"
#include "api/compute/compute_kernel_api.h"
#ifdef TRISC_MATH
#include "sfpi.h"
namespace ckernel::sfpu {
// the erf-form GELU of kernels/eltwise/gelu_compute.cpp (degree-8 CDF polynomial, fp32 SFPU)
inline void hg_gelu_face() {
    for (size_t i = 0; i < 8; i += 2) {
        sfpi::vFloat x0 = sfpi::dst_reg[i];
        sfpi::vFloat x1 = sfpi::dst_reg[i + 1];
        sfpi::vFloat u0 = sfpi::min(x0 * x0, 16.0f);
        sfpi::vFloat u1 = sfpi::min(x1 * x1, 16.0f);
        sfpi::vFloat a0, a1;
        { const sfpi::vFloat c8 = 8.040229593e-11f; const sfpi::vFloat c7 = -6.988785106e-09f;
        a0 = u0 * c8 + c7;
        a1 = u1 * c8 + c7; }
        { const sfpi::vFloat c = 2.712364733e-07f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = -6.289864814e-06f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = 9.886907355e-05f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = -1.133757411e-03f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = 9.877118282e-03f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = -6.641025841e-02f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        { const sfpi::vFloat c = 3.989226222e-01f;
        a0 = a0 * u0 + c;
        a1 = a1 * u1 + c; }
        sfpi::vFloat p0 = sfpi::clamp(x0 * a0 + 0.5f, 0.0f, 1.0f);
        sfpi::vFloat p1 = sfpi::clamp(x1 * a1 + 0.5f, 0.0f, 1.0f);
        sfpi::dst_reg[i] = x0 * p0;
        sfpi::dst_reg[i + 1] = x1 * p1;
    }
}
}  // namespace ckernel::sfpu
#endif
inline void hg_gelu_tile(uint32_t idst) {
    MATH(SFPU_UNARY_CALL_NO_TEMPLATE_ARGS(DST_SYNC_MODE, DST_ACCUM_MODE, hg_gelu_face, idst, VectorMode::RC));
}
#endif

#ifdef HG_LN
// scalar statistic of the dense tile in c_src: sum * inv_k [+ eps -> rsqrt], packed (only [0, 0] is used)
template <bool RSTD>
FORCE_INLINE void scalar_stat(uint32_t c_src, uint32_t c_scal, uint32_t c_dst, uint32_t inv_k, uint32_t eps) {
    reconfig_data_format(c_src, c_scal);  // REDUCE_SCALAR: data -> SrcA, scaler -> SrcB (not swapped)
    pack_reconfig_data_format(c_dst);
    reduce_init<PoolType::SUM, ReduceDim::REDUCE_SCALAR>(c_src, c_scal, c_dst);
    cb_reserve_back(c_dst, 1);
    tile_regs_acquire();
    reduce_tile<PoolType::SUM, ReduceDim::REDUCE_SCALAR>(c_src, c_scal, 0, 0, 0);
    reduce_uninit();
    binop_with_scalar_tile_init();
    mul_unary_tile(0, inv_k);
    if constexpr (RSTD) {
        add_unary_tile(0, eps);
        rsqrt_tile_init();
        rsqrt_tile(0);
    }
    tile_regs_commit();
    tile_regs_wait();
    pack_tile(0, c_dst);
    tile_regs_release();
    cb_push_back(c_dst, 1);
}
#endif

void kernel_main() {
    constexpr uint32_t KT = get_compile_time_arg_val(0);
    constexpr uint32_t INV_K_BITS = get_compile_time_arg_val(1);
    constexpr uint32_t EPS_BITS = get_compile_time_arg_val(2);
    constexpr uint32_t c_xd = 0, c_w = 1, c_bias = 2, c_scal = 3, c_res = 4, c_hrow = 5, c_hd = 6, c_out = 16;
    constexpr uint32_t c_mean = 24, c_d = 25, c_sq = 26, c_rstd = 27, c_f32 = 28;  // c_f32: fp32 format reference
#ifdef HG_LN
    compute_kernel_hw_startup(c_xd, c_scal, c_mean);
    {PZ("WX");
    cb_wait_front(c_xd, 1);}
    {PZ("LN");
    cb_wait_front(c_scal, 1);
    scalar_stat<false>(c_xd, c_scal, c_mean, INV_K_BITS, EPS_BITS);
    // d = x - mean
    cb_wait_front(c_mean, 1);
    reconfig_data_format(c_xd, c_mean);
    pack_reconfig_data_format(c_d);
    sub_bcast_scalar_init(c_xd, c_mean);
    cb_reserve_back(c_d, 1);
    tile_regs_acquire();
    sub_tiles_bcast_scalar(c_xd, c_mean, 0, 0, 0);
    tile_regs_commit();
    tile_regs_wait();
    pack_tile(0, c_d);
    tile_regs_release();
    cb_push_back(c_d, 1);
    cb_pop_front(c_mean, 1);
    cb_pop_front(c_xd, 1);
    // d^2
    cb_wait_front(c_d, 1);
    reconfig_data_format(c_d, c_d);
    pack_reconfig_data_format(c_sq);
    mul_init(c_d, c_d);
    cb_reserve_back(c_sq, 1);
    tile_regs_acquire();
    mul_tiles(c_d, c_d, 0, 0, 0);
    tile_regs_commit();
    tile_regs_wait();
    pack_tile(0, c_sq);
    tile_regs_release();
    cb_push_back(c_sq, 1);
    cb_wait_front(c_sq, 1);
    scalar_stat<true>(c_sq, c_scal, c_rstd, INV_K_BITS, EPS_BITS);
    cb_pop_front(c_sq, 1);
    // h = d * rstd -> bf16 dense tile for the writer
    cb_wait_front(c_rstd, 1);
    reconfig_data_format(c_d, c_rstd);
    pack_reconfig_data_format(c_hd);
    mul_bcast_scalar_init(c_d, c_rstd);
    cb_reserve_back(c_hd, 1);
    tile_regs_acquire();
    mul_tiles_bcast_scalar(c_d, c_rstd, 0, 0, 0);
    tile_regs_commit();
    tile_regs_wait();
    pack_tile(0, c_hd);
    tile_regs_release();
    cb_push_back(c_hd, 1);
#ifdef HG_TINY
    // round 10: the row-major dense LN output IS the matmul operand: c_hrow = KT [1, 32] tiny tiles over c_hd's memory
    // (no spread by the writer)
    cb_reserve_back(c_hrow, KT);
    cb_push_back(c_hrow, KT);
#endif
    cb_pop_front(c_rstd, 1);
    cb_pop_front(c_d, 1);
    cb_pop_front(c_scal, 1);
    }
#else
    compute_kernel_hw_startup(c_hrow, c_w, c_out);
#endif
    // y = sum_k hrow_k @ W_k
    {PZ("WH");
    cb_wait_front(c_hrow, KT);}
    {PZ("WW");
    cb_wait_front(c_w, KT);}
    PZ("MM");
#ifdef HG_TINY
    // round 10: [1, 32] tiny in0 / output tiles -- reprogram the tile / face geometry, not only the formats
    reconfig_full_operand(c_w, c_hrow);  // srcA = in1 = W, srcB = in0 = h
    pack_reconfig_data_format<true>(c_out);
#else
    reconfig_data_format(c_w, c_hrow);  // srcA = in1 = W, srcB = in0 = h
    pack_reconfig_data_format(c_out);
#endif
    matmul_init(c_hrow, c_w);
    tile_regs_acquire();
    for (uint32_t k = 0; k < KT; ++k) {
        matmul_tiles(c_hrow, c_w, k, k, 0);
    }
    // + bias [GELU] [+ residual]: SFPU fp32 adds in DST (operands copied in with unpack-to-dest fp32, so the matmul
    // result never goes through a 19-bit source register)
    cb_wait_front(c_bias, 1);
#ifdef HG_TINY
    reconfig_full_operand_srca(c_bias);
    copy_tile_to_dst_init_short(c_bias);
#else
    copy_tile_to_dst_init_short_with_dt(c_w, c_bias);
#endif
    copy_tile(c_bias, 0, 1);
    add_binary_tile_init();
    add_binary_tile(0, 1, 0);
#ifdef HG_GELU
    hg_gelu_tile(0);
#endif
#ifdef HG_RES
    cb_wait_front(c_res, 1);
#ifdef HG_TINY
    reconfig_full_operand_srca(c_res);
    copy_tile_to_dst_init_short(c_res);
#else
    copy_tile_to_dst_init_short_with_dt(c_bias, c_res);
#endif
    copy_tile(c_res, 0, 1);
    add_binary_tile_init();
    add_binary_tile(0, 1, 0);
#endif
    tile_regs_commit();
    cb_reserve_back(c_out, 1);
    tile_regs_wait();
    pack_tile(0, c_out);
    tile_regs_release();
    cb_push_back(c_out, 1);
    cb_pop_front(c_hrow, KT);
    cb_pop_front(c_w, KT);
    cb_pop_front(c_bias, 1);
#ifdef HG_RES
    cb_pop_front(c_res, 1);
#endif
}