170 lines
6.7 KiB
C
170 lines
6.7 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/**
|
|
* @file resident_gdngate_writer.c
|
|
* @brief Writer half of the RESIDENT GDN GATE pair: puts each block the reader assembled at
|
|
* its slot in DRAM and publishes the descriptor's completion. resident_gdngate_protocol.h has
|
|
* the layout; resident_gdngate_reader.c the arithmetic.
|
|
*
|
|
* The writer walks the SAME (band, sub) sequence as the reader from the same parameter words
|
|
* (`ftgg_sub_live_rows()`), so the n-th page it pops is the n-th block the reader pushed.
|
|
*
|
|
* THIS PROCESSOR OWNS `completed_count` AND `completed_token` (resident_queue_protocol.h
|
|
* rule 1), and publishes only after the reader's `tail` passed the descriptor.
|
|
*
|
|
* Compile-time args:
|
|
* 0: CB_OUT index
|
|
* 1: wait mode -- 0 upstream, 1 stoppable (ships), 2 bound-only
|
|
* Runtime args: the ten of resident_queue_protocol.h
|
|
*
|
|
* Build (NCRISC, NoC 1):
|
|
* CTARGS=16,1 NOC=1 KERNEL_TEXT_BASE=0x18000 bash \
|
|
* foundry/tt/build_tensix_dataflow_kernel.sh \
|
|
* foundry/tt/kernels/resident_gdngate_writer.c ncrisc <out>
|
|
*
|
|
* @status Written PM 7 (2026-10-08). Unexecuted.
|
|
* @issues None
|
|
* @todo None
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "ft_tensix_dataflow.h"
|
|
#include "resident_gdngate_protocol.h"
|
|
#include "resident_queue_protocol.h"
|
|
|
|
#define CB_OUT ft_tensix_compile_time_arg(0)
|
|
#define WAIT_MODE ft_tensix_compile_time_arg(1)
|
|
|
|
/**
|
|
* The writer half of one GDN_GATE descriptor.
|
|
* @return true if every block was written; false if refused or a wait was abandoned
|
|
*/
|
|
static bool write_one_gate_op(ftq_context *ctx, volatile FTQ_L1_PTR uint32_t *parameters)
|
|
{
|
|
const uint32_t rows = parameters[kGgRows];
|
|
const uint32_t band_count = parameters[kGgBandCount];
|
|
const uint32_t sub_rows = parameters[kGgSubRows];
|
|
const uint32_t heads = parameters[kGgHeads];
|
|
const uint32_t dst_x = parameters[kGgDstXY] & 0xFFFFu;
|
|
const uint32_t dst_y = parameters[kGgDstXY] >> 16;
|
|
const uint32_t dst_base = parameters[kGgDstBase];
|
|
const uint32_t block_stride = parameters[kGgBlockStride];
|
|
const uint32_t block_bytes = ftgg_block_bytes(heads);
|
|
|
|
const uint32_t flags = parameters[kGgFlags];
|
|
|
|
// ITEM 6 FLAT: one page; this slice's two runs (decay, beta) of every row.
|
|
if ((flags & kGgFlagFlat) != 0u) {
|
|
const uint32_t first = parameters[kGgSubRows], total = parameters[kGgBandStride];
|
|
const uint32_t run = heads * 4u;
|
|
|
|
if (rows == 0u || band_count != 1u || heads == 0u || total > kGgMaxHeads ||
|
|
first + heads > total || block_stride < 2u * total * 4u ||
|
|
(block_stride % 16u) != 0u || (dst_base % 16u) != 0u ||
|
|
ftgg_flat_bytes(rows, total, block_stride) > ftgg_block_bytes(kGgMaxHeads) ||
|
|
!ft_tensix_cb_pop_grid_is_safe(CB_OUT, 1u)) {
|
|
ftq_abort(ctx, kErrBadParameters);
|
|
return false;
|
|
}
|
|
if (ftq_cb_wait_front_stoppable(ctx, (int32_t)CB_OUT, 1, WAIT_MODE) && !ctx->aborting) {
|
|
const uint32_t page = ft_tensix_cb_read_pointer(CB_OUT);
|
|
|
|
for (uint32_t row = 0; row < rows; row++) {
|
|
const uint32_t decay = row * block_stride + first * 4u;
|
|
const uint32_t beta = row * block_stride + (total + first) * 4u;
|
|
|
|
ft_tensix_noc_write(page + decay, ft_tensix_noc_address(dst_x, dst_y,
|
|
dst_base + decay), run);
|
|
ft_tensix_noc_write(page + beta, ft_tensix_noc_address(dst_x, dst_y,
|
|
dst_base + beta), run);
|
|
}
|
|
ft_tensix_noc_write_barrier();
|
|
}
|
|
ft_tensix_cb_pop_front(CB_OUT, 1u);
|
|
return !ctx->aborting;
|
|
}
|
|
// The reader refuses the same shapes; the writer must not wait for pages it never gets.
|
|
if (rows == 0u || band_count == 0u || band_count > kGgMaxBands ||
|
|
(rows + kGgTileRows - 1u) / kGgTileRows != band_count ||
|
|
(sub_rows != 16u && sub_rows != 32u) || heads == 0u || heads > kGgMaxHeads ||
|
|
block_stride < block_bytes || (block_stride % 64u) != 0u || (dst_base % 64u) != 0u ||
|
|
!ft_tensix_cb_pop_grid_is_safe(CB_OUT, 1u)) {
|
|
ftq_abort(ctx, kErrBadParameters);
|
|
return false;
|
|
}
|
|
for (uint32_t band = 0; band < band_count; band++) {
|
|
const uint32_t done = band * kGgTileRows;
|
|
const uint32_t band_rows = rows - done < kGgTileRows ? rows - done : kGgTileRows;
|
|
|
|
for (uint32_t sub = 0; sub < 2u; sub++) {
|
|
if (ftgg_sub_live_rows(band_rows, sub_rows, sub) == 0u)
|
|
continue;
|
|
if (ftq_cb_wait_front_stoppable(ctx, (int32_t)CB_OUT, 1, WAIT_MODE) &&
|
|
!ctx->aborting) {
|
|
const uint32_t slot = dst_base + (2u * band + sub) * block_stride;
|
|
|
|
ft_tensix_noc_write(ft_tensix_cb_read_pointer(CB_OUT),
|
|
ft_tensix_noc_address(dst_x, dst_y, slot), block_bytes);
|
|
ft_tensix_noc_write_barrier();
|
|
}
|
|
ft_tensix_cb_pop_front(CB_OUT, 1u);
|
|
}
|
|
}
|
|
return !ctx->aborting;
|
|
}
|
|
|
|
void kernel_main(void)
|
|
{
|
|
ftq_context ctx;
|
|
uint32_t completed;
|
|
bool running = true;
|
|
|
|
ftq_context_init(&ctx);
|
|
ftq_status_set(&ctx, kStateBoot);
|
|
|
|
if (!ftq_check_control(&ctx)) {
|
|
ftq_status_set(&ctx, kStateExit);
|
|
return;
|
|
}
|
|
completed = ctx.control[kFieldCompletedCount];
|
|
ctx.cursor = completed;
|
|
|
|
while (running) {
|
|
if (!ftq_wait_for_work(&ctx, kFieldHead))
|
|
break;
|
|
|
|
ctx.aborting = false;
|
|
ftq_status_set(&ctx, kStateExec);
|
|
|
|
volatile FTQ_L1_PTR uint32_t *descriptor = ftq_descriptor(&ctx);
|
|
const uint32_t opcode = descriptor[kDescOpcode];
|
|
const uint32_t token = descriptor[kDescToken];
|
|
const uint32_t group_sequence = descriptor[kDescA5];
|
|
bool done = true;
|
|
|
|
ftq_status_opcode(&ctx, opcode);
|
|
if (opcode == kOpGdnGate) {
|
|
ftq_status_set(&ctx, kStateCbWaitFront);
|
|
done = write_one_gate_op(&ctx, ftq_parameters(descriptor));
|
|
ftq_status_set(&ctx, kStateExec);
|
|
} else if (opcode == kOpStop) {
|
|
running = false;
|
|
}
|
|
if (!done) {
|
|
running = false;
|
|
continue;
|
|
}
|
|
// The reader must have finished this descriptor before its completion is published.
|
|
if (!ftq_wait_until_reaches(&ctx, kFieldTail, ctx.cursor + 1u)) {
|
|
running = false;
|
|
continue;
|
|
}
|
|
ctx.cursor++;
|
|
ctx.control[kFieldCompletedToken] = token;
|
|
ctx.control[kFieldCompletedCount] = ++completed;
|
|
ftq_group_post(&ctx, group_sequence, token);
|
|
}
|
|
ftq_status_set(&ctx, kStateExit);
|
|
}
|