meteor-p150 / code /tt_meteor /tt /kernels /maxpool_writer.cpp
changh95's picture
tt-model push meteor-p150 (container)
8eeb550 verified
Raw History Blame Contribute Delete
4.52 kB
// SPDX-License-Identifier: Apache-2.0
// STEM_POOL writer (meteor-p150 OPT round 3 R3.2, tt/pool.py): the horizontal half of the 3x3 / s2 max pool and the
// output. Per unit (work split as the reader), the WT vertical-max tiles of CB 16 hold one input row of 32 * WT
// columns w (tile tx = w / 32, tile row w % 32) for one channel tile. Output column ox takes max(v[2ox-1], v[2ox],
// v[2ox+1]); column -1 is the zero padding (+0.0; W = 2 * OW, so 2ox+1 always exists). The OWT = WT / 2 output tiles
// are built in a scratch CB (24) on this RISC and written to the TILE fp32 output [1, 1, N*OH*OW, C].
// Compare: the fp32 bits as int32. The inputs are post-ReLU (>= +0.0, or -0.0), where the int32 order is the float
// order with -0.0 < +0.0 (that of the SFPU min / max swap); fp32 is never rounded here.
// Tile element (row r, column c): face ((r / 16) * 2 + c / 16) * 256 + (r % 16) * 16 + c % 16.
// CT args: [0] NU, [1] NC, [2] GX, [3] WT, [4] CT, [5] OH, then the TensorAccessorArgs of the output.
// Common RT args: [out_addr].
#include <cstdint>
#include "api/dataflow/dataflow_api.h"
constexpr uint32_t NU = get_compile_time_arg_val(0);
constexpr uint32_t NC = get_compile_time_arg_val(1);
constexpr uint32_t GX = get_compile_time_arg_val(2);
constexpr uint32_t WT = get_compile_time_arg_val(3);
constexpr uint32_t CT = get_compile_time_arg_val(4);
constexpr uint32_t OH = get_compile_time_arg_val(5);
constexpr auto out_args = TensorAccessorArgs<6>();
constexpr uint32_t OWT = WT / 2;
constexpr uint32_t TB = 4096;
constexpr uint32_t cb_v = 16, cb_s = 24;
static inline uint32_t row_off(uint32_t r) { return (r >> 4) * 512 + (r & 15) * 16; } // + (c / 16) * 256 + c % 16
void kernel_main() {
const uint32_t core = get_absolute_logical_y() * GX + get_absolute_logical_x();
if (core >= NC) {
return;
}
constexpr uint32_t B = NU / NC, E = NU % NC;
const uint32_t u0 = core * B + (core < E ? core : E);
const uint32_t nu = B + (core < E ? 1 : 0);
const auto out = TensorAccessor(out_args, get_common_arg_val<uint32_t>(0), TB);
volatile tt_l1_ptr int32_t* s = reinterpret_cast<volatile tt_l1_ptr int32_t*>(get_write_ptr(cb_s));
for (uint32_t u = u0; u < u0 + nu; ++u) {
const uint32_t ct = u % CT;
const uint32_t q = u / CT; // n * OH + oy
cb_wait_front(cb_v, WT);
volatile tt_l1_ptr int32_t* v = reinterpret_cast<volatile tt_l1_ptr int32_t*>(get_read_ptr(cb_v));
noc_async_write_barrier(); // the scratch tiles of the previous unit are out
for (uint32_t ot = 0; ot < OWT; ++ot) {
volatile tt_l1_ptr int32_t* o = s + ot * 1024;
for (uint32_t r = 0; r < 32; ++r) {
const uint32_t w1 = (ot * 32 + r) * 2; // 2 ox
const uint32_t w2 = w1 + 1;
volatile tt_l1_ptr int32_t* p1 = v + (w1 >> 5) * 1024 + row_off(w1 & 31);
volatile tt_l1_ptr int32_t* p2 = v + (w2 >> 5) * 1024 + row_off(w2 & 31);
volatile tt_l1_ptr int32_t* q0 = o + row_off(r);
if (w1 == 0) { // column -1: the +0.0 padding
for (uint32_t f = 0; f < 2; ++f) {
for (uint32_t c = 0; c < 16; ++c) {
int32_t m = p1[f * 256 + c];
const int32_t b = p2[f * 256 + c];
m = b > m ? b : m;
q0[f * 256 + c] = m > 0 ? m : 0;
}
}
} else {
const uint32_t w0 = w1 - 1;
volatile tt_l1_ptr int32_t* p0 = v + (w0 >> 5) * 1024 + row_off(w0 & 31);
for (uint32_t f = 0; f < 2; ++f) {
for (uint32_t c = 0; c < 16; ++c) {
int32_t m = p0[f * 256 + c];
const int32_t a = p1[f * 256 + c];
const int32_t b = p2[f * 256 + c];
m = a > m ? a : m;
q0[f * 256 + c] = b > m ? b : m;
}
}
}
}
}
cb_pop_front(cb_v, WT);
const uint32_t base = (q * OWT) * CT + ct;
for (uint32_t ot = 0; ot < OWT; ++ot) {
noc_async_write(reinterpret_cast<uint32_t>(s + ot * 1024), out.get_noc_addr(base + ot * CT), TB);
}
}
noc_async_write_barrier();
}