Foundry/tt/tests/ft_tt_phase2_l1_layout.h

150 lines
7.1 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file ft_tt_phase2_l1_layout.h
* @brief Where the Phase 2 bring-up tests put things in one Tensix's L1 — DERIVED at run
* time from where the firmware images actually landed, not hand-chosen.
*
* One header rather than per-test constants, because the tests have to agree:
* test_phase2_images.c checks that no loaded kernel reaches the allocation floor,
* test_phase2_launch.c plants runtime args here, and test_phase2_vector_add.c puts the
* circular buffers and the data buffers here.
*
* The base used to be the literal 0x14000, picked by hand to clear the firmware `.data`
* staging and the bank tables. ft_tt_image_placement_l1_floor() exists to compute
* exactly that number, so the base now comes from
* ft_tt_phase2_l1_layout_derive(): the highest address the five FIRMWARE images
* reached, or the end of the regions the stock memory map reserves above MEM_MAP_END,
* whichever is higher. Everything else is a fixed offset from it.
*
* Constraints the offsets satisfy, all from docs/tt_launch_notes.md §2:
* - the base is at or above FT_TT_L1_USABLE_BASE (0x9F80), the allocator floor;
* - and above the firmware `.data` staging, NCRISC's IRAM staging and the two bank
* tables, which the usable-base macro does NOT cover — see
* FT_TT_PHASE2_RESERVED_L1_END;
* - the CB-config array and every runtime-arg block sit within 64 KiB of the base,
* because those wire fields are 16-bit;
* - the base is 16-byte aligned (FT_TT_MAX_NOC_WRITE_ALIGNMENT), and so is every
* offset, because a CB FIFO address is shifted by 4 bits on a TRISC.
*
* Kernel TEXT is not in this map. It is linked at a fixed low address just after its
* processor's firmware, not allocated from here — see the notes' §2.
*
* @status Used by the on-card Phase 2 tests.
* @issues None
* @todo None
*/
#ifndef FT_TT_PHASE2_L1_LAYOUT_H
#define FT_TT_PHASE2_L1_LAYOUT_H
#include <stdint.h>
#include "ft_tt_launch.h"
#include "ft_tt_memmap.h"
/**
* First L1 address above every region the stock memory map stages something into.
*
* FT_TT_L1_USABLE_BASE is MEM_MAP_END, and it is NOT this number: the five per-processor
* `.data` staging regions, NCRISC's IRAM staging and the two bank tables all live ABOVE
* it (ft_tt_memmap.h). A floor derived from the loaded firmware images alone would sit
* under the bank tables, because those are written by firmware and are in no ELF.
*/
#define FT_TT_PHASE2_RESERVED_L1_END \
(FT_TT_MEM_LOGICAL_TO_VIRTUAL_SCRATCH + FT_TT_MEM_LOGICAL_TO_VIRTUAL_SIZE)
/**
* What the derived base is rounded up to — the DRAM alignment, 64 B, NOT L1's 16 B.
*
* **This cost a debugging session and is the non-obvious part of this file.** A circular
* buffer in this map is the LOCAL endpoint of the reader's `noc_async_read` from DRAM,
* and the binding alignment for a transfer is the stricter of the two ends. The
* hand-chosen 0x14000 satisfied it by accident; the first derived base, 0x137A0, is
* 16 B aligned and 32 B aligned but not 64, and every tile came back with its LAST 64
* BYTES wrong — a silent, partial, byte-exact-until-element-992 corruption, not a
* refusal. Every offset below is a multiple of 64, so aligning the base is enough to
* align the whole map.
*/
#define FT_TT_PHASE2_LAYOUT_ALIGNMENT FT_TT_DRAM_ALIGNMENT
/* Offsets from the derived base. Kept as macros so the relationship between them is
* readable in one place; the addresses themselves are struct fields below. */
#define FT_TT_PHASE2_CB_CONFIG_OFFSET 0x0000u /**< 64 CBs x 4 words */
#define FT_TT_PHASE2_RTA_OFFSET 0x0400u
#define FT_TT_PHASE2_RTA_STRIDE 0x0080u
#define FT_TT_PHASE2_PROBE_OFFSET 0x2000u
#define FT_TT_PHASE2_CB_IN0_OFFSET 0x4000u
#define FT_TT_PHASE2_CB_IN1_OFFSET 0x5000u
#define FT_TT_PHASE2_CB_OUT_OFFSET 0x6000u
#define FT_TT_PHASE2_SOURCE0_OFFSET 0xC000u
#define FT_TT_PHASE2_SOURCE1_OFFSET 0xE000u
#define FT_TT_PHASE2_DESTINATION_OFFSET 0x10000u
/** Scratch stride for the runtime-arg probe kernel, one word per TRISC. */
#define FT_TT_PHASE2_PROBE_STRIDE 4u
/** A 32x32 Float16_b tile, the default tile shape the generated CB descriptors describe
* (build_tensix_kernel.sh emits Float16_b / 32x32 / 4 faces). */
#define FT_TT_PHASE2_TILE_BYTES 2048u
/** CB indices. These MUST match the compile-time args the kernels were built with —
* vector_add is built 0,1 in / 16 out. */
#define FT_TT_PHASE2_CB_IN0 0u
#define FT_TT_PHASE2_CB_IN1 1u
#define FT_TT_PHASE2_CB_OUT 16u
/** Two pages per CB: enough for the FIFO to be a ring rather than a single slot, small
* enough that a wrong page count shows up as a wrap bug rather than silence. */
#define FT_TT_PHASE2_CB_PAGE_COUNT 2u
#define FT_TT_PHASE2_CB_BYTES (FT_TT_PHASE2_CB_PAGE_COUNT * FT_TT_PHASE2_TILE_BYTES)
/** One core's Phase 2 L1 map. Every address is the derived base plus a fixed offset. */
typedef struct ft_tt_phase2_l1_layout {
uint32_t kernel_config_base; /**< everything the launch message's offsets are relative to */
uint32_t cb_config_address;
uint32_t runtime_args_base;
uint32_t probe_address;
uint32_t cb_in0_address;
uint32_t cb_in1_address;
uint32_t cb_out_address;
uint32_t source0_address;
uint32_t source1_address;
uint32_t destination_address;
/** No loaded kernel image may reach this address. Same as kernel_config_base; named
* separately because that is the question test_phase2_images.c asks. */
uint32_t allocation_floor;
} ft_tt_phase2_l1_layout;
/**
* Build the map from where the five FIRMWARE images landed.
*
* The base is max(ft_tt_image_placement_l1_floor(placements, count),
* FT_TT_PHASE2_RESERVED_L1_END), which is the whole point: the first term is what the
* loader measured on this core with this firmware build, the second is what the memory
* map reserves whether or not an ELF covers it.
*
* @param placements firmware placements from ft_tt_core_boot_firmware(); entries with
* bytes_written == 0 are ignored. NULL is allowed with count 0, which
* yields the reserved-region floor alone
* @param count number of entries
* @param out_layout written only on success
* @return FT_TT_OK; FT_TT_EINVAL for a NULL out_layout, a floor
* ft_tt_image_placement_l1_floor() refuses, or a base whose highest
* offset would leave L1
*
* Thread-safe: yes Reentrant: yes Signal-safe: no (logs on failure)
*/
int ft_tt_phase2_l1_layout_derive(const ft_tt_image_placement *placements, unsigned count,
ft_tt_phase2_l1_layout *out_layout);
/**
* Runtime-arg block address for one processor: the base plus FT_TT_PHASE2_RTA_OFFSET plus
* `processor` strides. Returns 0 for a NULL layout or an out-of-range processor, which is
* the "this processor has no args" value the launch encoder already understands.
*/
uint32_t ft_tt_phase2_runtime_args_address(const ft_tt_phase2_l1_layout *layout,
unsigned processor);
#endif /* FT_TT_PHASE2_L1_LAYOUT_H */