126 lines
4.8 KiB
C
126 lines
4.8 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/**
|
|
* @file dispatch_gate.c
|
|
* @brief PHASE D's gate: a ONE-SHOT dispatcher on BRISC that issues a recorded list of resident
|
|
* descriptors into ANOTHER core's queue over the NoC and awaits each one there — the
|
|
* on-card half of the host's issue + await, so its per-descriptor cost can be priced
|
|
* against the host round trip before the full dispatcher is built.
|
|
*
|
|
* @status Gate kernel. Driven by tests/test_phase5_resident.c stage 7.
|
|
* @issues None
|
|
* @todo None
|
|
*
|
|
* WHAT IT REPRODUCES, AND THE TWO RULES IT MUST KEEP (tt_ornith_optimized_spec.md PHASE D):
|
|
* per entry — write the 32 B descriptor into the target's ring slot (token pre-stamped by
|
|
* the host, which owns the target's run nonce), barrier, THEN bump the target's HEAD with one
|
|
* 4 B inline write (descriptors before head), barrier, then poll the target's words 4..7
|
|
* (stop, completed count, completed token, state) with a 16 B NoC READ until the count
|
|
* reaches the entry, and check the token and the state the host would check.
|
|
* It NEVER posts into a triad writer's path (the `ftq_group_post` wedge suspect); its only
|
|
* writes are the ring slot and the head word of the ONE target the table names.
|
|
*
|
|
* THE TABLE (runtime arg 0 = its L1 address, 64-byte aligned), words:
|
|
* 0 magic 0x47505344 "DSPG" 1 entries 2 target NoC x 3 target NoC y
|
|
* 4 target control address 5 target ring 6 capacity mask 7 first queue index
|
|
* 8 poll spin bound 16..19 poll buffer (16 B, kernel-owned)
|
|
* 24 state (kernel: 1 running, 2 done, 3 failed) 25 entries completed 26 error
|
|
* 27 last count seen 28 last token seen 29 last state seen
|
|
* 32.. entries, 16 words each: 0..7 the descriptor (token at word 1), 8 expected token.
|
|
* Errors: 1 bad magic, 2 poll bound exhausted, 3 token mismatch, 4 target state poison/error.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "../fw/ft_bh_regs.h"
|
|
#include "ft_tensix_dataflow.h"
|
|
#include "ft_tensix_noc.h"
|
|
|
|
#define DSPG_MAGIC 0x47505344u
|
|
#define DSPG_ENTRY_WORDS 16u
|
|
#define DSPG_FIRST_ENTRY 32u
|
|
#define QUEUE_HEAD_WORD 2u
|
|
#define QUEUE_WINDOW_OFFSET 16u /* words 4..7 */
|
|
#define QUEUE_STATE_POISON 0x7B1B1B1Bu
|
|
|
|
static void dispatch_fail(volatile uint32_t *table, uint32_t error, uint32_t done)
|
|
{
|
|
table[25] = done;
|
|
table[26] = error;
|
|
table[24] = 3u;
|
|
}
|
|
|
|
void kernel_main(void)
|
|
{
|
|
volatile uint32_t *table = (volatile uint32_t *)(uintptr_t)ft_tensix_runtime_arg(0);
|
|
volatile uint32_t *poll;
|
|
uint32_t entries, x, y, control, ring, mask, first, bound, entry;
|
|
|
|
table[24] = 1u;
|
|
if (table[0] != DSPG_MAGIC) {
|
|
dispatch_fail(table, 1u, 0u);
|
|
return;
|
|
}
|
|
entries = table[1];
|
|
x = table[2];
|
|
y = table[3];
|
|
control = table[4];
|
|
ring = table[5];
|
|
mask = table[6];
|
|
first = table[7];
|
|
bound = table[8];
|
|
poll = table + 16;
|
|
|
|
for (entry = 0; entry < entries; entry++) {
|
|
const uint32_t index = first + entry;
|
|
volatile uint32_t *record = table + DSPG_FIRST_ENTRY + entry * DSPG_ENTRY_WORDS;
|
|
uint32_t spins = 0u;
|
|
uint32_t count, token, state;
|
|
|
|
/* 1. THE DESCRIPTOR, then a barrier: it must have landed before the head says so. */
|
|
ft_tensix_noc_write((uint32_t)(uintptr_t)record,
|
|
ft_tensix_noc_address(x, y, ring + (index & mask) * 32u), 32u);
|
|
ft_tensix_noc_write_barrier();
|
|
/* 2. THE HEAD — one 4 B inline write, the host's own publish. */
|
|
if (ft_tensix_noc_inline_dword_write_l1(
|
|
ft_tensix_noc_address(x, y, control + QUEUE_HEAD_WORD * 4u), index + 1u) !=
|
|
FT_TENSIX_NOC_ISSUED) {
|
|
dispatch_fail(table, 4u, entry);
|
|
return;
|
|
}
|
|
ft_tensix_noc_write_barrier();
|
|
/* 3. THE AWAIT — a 16 B read of the target's completion window, as the host's `line`. */
|
|
for (;;) {
|
|
ft_tensix_noc_read(ft_tensix_noc_address(x, y, control + QUEUE_WINDOW_OFFSET),
|
|
(uint32_t)(uintptr_t)poll, 16u);
|
|
ft_tensix_noc_read_barrier();
|
|
ft_bh_l1_cache_invalidate();
|
|
count = poll[1];
|
|
if (count >= index + 1u)
|
|
break;
|
|
if (++spins >= bound) {
|
|
table[27] = count;
|
|
table[29] = poll[3];
|
|
dispatch_fail(table, 2u, entry);
|
|
return;
|
|
}
|
|
}
|
|
token = poll[2];
|
|
state = poll[3];
|
|
table[27] = count;
|
|
table[28] = token;
|
|
table[29] = state;
|
|
if (state == QUEUE_STATE_POISON) {
|
|
dispatch_fail(table, 4u, entry);
|
|
return;
|
|
}
|
|
if (token != record[8]) {
|
|
dispatch_fail(table, 3u, entry);
|
|
return;
|
|
}
|
|
}
|
|
table[25] = entries;
|
|
table[26] = 0u;
|
|
table[24] = 2u;
|
|
}
|