Foundry/tt/kernels/vector_add_reader.c

76 lines
3.1 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file vector_add_reader.c
* @brief Reader half of Foundry's vector-add triad. Streams tile pairs from two source
* buffers into the two input circular buffers the compute kernel consumes.
*
* C99, on Foundry's own `ft_tensix_dataflow.h` — no tt-metal include (plan Phase 6.6). The
* C++ predecessor's `.text` is the acceptance baseline: this must emit the same bytes.
*
* Deliberately NOT built on TensorAccessor. Upstream's examples derive NoC addresses from a
* compile-time buffer layout appended host-side by TensorAccessorArgs
* (docs/tt_kernel_model.md §6), which drags tt-metal's buffer abstraction into Foundry's
* host code. Foundry already knows exactly where it put every buffer, so the addresses
* arrive as runtime args and the kernel builds the NoC address itself.
*
* Compile-time args:
* 0: cb_in0 index
* 1: cb_in1 index
* 2: tile size in bytes (2048 for a 32x32 Float16_b tile)
* Runtime args:
* 0: number of tiles
* 1: source-0 NoC x 2: source-0 NoC y 3: source-0 base address
* 4: source-1 NoC x 5: source-1 NoC y 6: source-1 base address
*
* Build (BRISC, NoC 0):
* CTARGS=0,1,2048 NOC=0 bash \
* foundry/tt/build_tensix_dataflow_kernel.sh \
* foundry/tt/kernels/vector_add_reader.c brisc /tmp/ttk_vector_add
*
* NOC_INDEX is fixed at compile time, but the firmware picks BRISC's NoC at run time from
* the launch message's brisc_noc_id. The host must pass the same value this
* was built with, or the kernel issues on a NoC whose command buffers the firmware never
* initialised.
*
* @status None
* @issues None
* @todo None
*/
#include <stdint.h>
#include "ft_tensix_dataflow.h"
void kernel_main(void)
{
const uint32_t cb_in0 = ft_tensix_compile_time_arg(0);
const uint32_t cb_in1 = ft_tensix_compile_time_arg(1);
const uint32_t tile_bytes = ft_tensix_compile_time_arg(2);
const uint32_t tile_count = ft_tensix_runtime_arg(0);
const uint32_t source0_x = ft_tensix_runtime_arg(1);
const uint32_t source0_y = ft_tensix_runtime_arg(2);
const uint32_t source0_base = ft_tensix_runtime_arg(3);
const uint32_t source1_x = ft_tensix_runtime_arg(4);
const uint32_t source1_y = ft_tensix_runtime_arg(5);
const uint32_t source1_base = ft_tensix_runtime_arg(6);
for (uint32_t tile = 0; tile < tile_count; tile++) {
const uint32_t byte_offset = tile * tile_bytes;
ft_tensix_cb_reserve_back(cb_in0, 1);
ft_tensix_cb_reserve_back(cb_in1, 1);
ft_tensix_noc_read(ft_tensix_noc_address(source0_x, source0_y, source0_base + byte_offset),
ft_tensix_cb_write_pointer(cb_in0), tile_bytes);
ft_tensix_noc_read(ft_tensix_noc_address(source1_x, source1_y, source1_base + byte_offset),
ft_tensix_cb_write_pointer(cb_in1), tile_bytes);
/* One barrier for both reads: it is per-core, not per-transaction. */
ft_tensix_noc_read_barrier();
ft_tensix_cb_push_back(cb_in0, 1);
ft_tensix_cb_push_back(cb_in1, 1);
}
}