232 lines
8.5 KiB
C
232 lines
8.5 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/**
|
|
* test_phase2_boot.c — checklist steps 1-4 from docs/tt_launch_notes.md §6.
|
|
*
|
|
* This stops at the offset proof and does not run a kernel. Step 4 is the gate:
|
|
* after loading firmware and deasserting BRISC, we read the mailbox back and look
|
|
* for values *the firmware itself wrote*. Seeing them at the offsets we predicted
|
|
* is the only real evidence that the transliterated mailbox layout matches the
|
|
* binary — everything after it writes to addresses this step validates.
|
|
*
|
|
* Nothing here guesses a coordinate: the target Tensix comes from ft_tt_coord
|
|
* against live ARC telemetry.
|
|
*
|
|
* Build:
|
|
* cc -std=c99 -O1 -Wall -Wextra -Werror -DFT_TT_PHASE1B_NOC_ENABLED -I.. \
|
|
* ../ft_tt_kmd.c ../ft_tt_device.c ../ft_tt_tlb.c ../ft_tt_arc_telemetry.c \
|
|
* ../ft_tt_coord.c ../ft_tt_elf.c ../ft_tt_kernel_blob.c ../ft_tt_launch.c \
|
|
* test_phase2_boot.c -o build/test_phase2_boot
|
|
*/
|
|
|
|
#define _POSIX_C_SOURCE 199309L
|
|
|
|
#include "ft_tt_arc_telemetry.h"
|
|
#include "ft_tt_coord.h"
|
|
#include "ft_tt_device.h"
|
|
#include "ft_tt_hardware_session.h"
|
|
#include "ft_tt_launch.h"
|
|
#include "ft_tt_memmap.h"
|
|
#include "ft_tt_tlb.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define WINDOW_BYTES (2u * 1024u * 1024u)
|
|
|
|
static int failures;
|
|
|
|
static void check(const char *what, bool ok, const char *detail)
|
|
{
|
|
printf(" %-4s %-44s %s\n", ok ? "ok" : "FAIL", what, detail ? detail : "");
|
|
if (!ok) {
|
|
failures++;
|
|
}
|
|
}
|
|
|
|
/** Slurp an ELF. Returns NULL and reports on failure. */
|
|
static unsigned char *load_file(const char *path, size_t *out_size)
|
|
{
|
|
FILE *f = fopen(path, "rb");
|
|
if (f == NULL) {
|
|
fprintf(stderr, " cannot open %s\n", path);
|
|
return NULL;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
long size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
unsigned char *buf = malloc((size_t)size);
|
|
if (buf == NULL || fread(buf, 1, (size_t)size, f) != (size_t)size) {
|
|
fclose(f);
|
|
free(buf);
|
|
return NULL;
|
|
}
|
|
fclose(f);
|
|
*out_size = (size_t)size;
|
|
return buf;
|
|
}
|
|
|
|
/** File names of the five firmware images, indexed by FT_TT_PROC_*. The DIRECTORY comes
|
|
* from ft_tt_firmware_directory() so two workers can point at their own build. */
|
|
static const char *const FW_FILE_NAMES[FT_TT_PROC_COUNT] = {
|
|
"fw_brisc.elf", "fw_ncrisc.elf", "fw_trisc0.elf", "fw_trisc1.elf", "fw_trisc2.elf"
|
|
};
|
|
|
|
int main(void)
|
|
{
|
|
ft_tt_ctx *ctx = ft_tt_init_device(0);
|
|
if (ctx == NULL) {
|
|
fprintf(stderr, "could not open device 0\n");
|
|
return 1;
|
|
}
|
|
|
|
ft_tt_grid_info grid;
|
|
if (ft_tt_arc_telemetry_read_grid(ctx, &grid) != FT_TT_OK ||
|
|
ft_tt_coord_check_grid(&grid) != FT_TT_OK) {
|
|
fprintf(stderr, "grid not sound; refusing to touch a core\n");
|
|
ft_tt_ctx_free(ctx);
|
|
return 1;
|
|
}
|
|
|
|
uint32_t tensix_index = 0;
|
|
ft_tt_noc_coord core;
|
|
if (ft_tt_tensix_index_from_environment(&tensix_index) != FT_TT_OK ||
|
|
ft_tt_coord_tensix_by_index(&grid, tensix_index, FT_TT_COORD_TRANSLATED, &core) !=
|
|
FT_TT_OK) {
|
|
fprintf(stderr, "could not derive Tensix %u\n", tensix_index);
|
|
ft_tt_ctx_free(ctx);
|
|
return 1;
|
|
}
|
|
printf("target Tensix %u at translated (%u, %u) [%s]\n\n", tensix_index, core.x, core.y,
|
|
FT_TT_TENSIX_INDEX_ENVIRONMENT);
|
|
|
|
ft_tt_tlb_window *window = NULL;
|
|
if (ft_tt_tlb_window_alloc(ctx, WINDOW_BYTES, FT_TT_KMD_CACHE_UC, &window) != FT_TT_OK) {
|
|
ft_tt_ctx_free(ctx);
|
|
return 1;
|
|
}
|
|
|
|
ft_tt_noc_window_binding binding;
|
|
ft_tt_noc_access noc;
|
|
int rc = ft_tt_launch_noc_over_window(ctx, window, &binding, &noc);
|
|
if (rc != FT_TT_OK) {
|
|
fprintf(stderr, "noc_over_window failed: %d\n", rc);
|
|
(void)ft_tt_tlb_window_free(ctx, window);
|
|
ft_tt_ctx_free(ctx);
|
|
return 1;
|
|
}
|
|
|
|
char detail[200];
|
|
uint16_t x = (uint16_t)core.x;
|
|
uint16_t y = (uint16_t)core.y;
|
|
|
|
/* --- step 2: read the reset register, do not write it ------------------ */
|
|
printf("step 2 — read the soft-reset register (first non-L1 address)\n");
|
|
uint32_t reset = 0;
|
|
rc = ft_tt_launch_read_reset(&noc, x, y, &reset);
|
|
snprintf(detail, sizeof(detail), "0x%08x (rc=%d)", reset, rc);
|
|
check("reset register readable", rc == FT_TT_OK, detail);
|
|
check("all five RISC-Vs are held in reset",
|
|
(reset & FT_TT_SOFT_RESET_ALL_RISCS) == FT_TT_SOFT_RESET_ALL_RISCS, detail);
|
|
if (failures != 0) {
|
|
goto done;
|
|
}
|
|
|
|
/* --- step 3: assert reset, clear the mailbox, read back zero ----------- */
|
|
printf("\nstep 3 — assert reset and clear the mailbox\n");
|
|
rc = ft_tt_launch_assert_reset(&noc, x, y, FT_TT_SOFT_RESET_ALL_RISCS);
|
|
snprintf(detail, sizeof(detail), "rc=%d", rc);
|
|
check("reset asserted", rc == FT_TT_OK, detail);
|
|
|
|
rc = ft_tt_launch_clear_mailbox(&noc, x, y);
|
|
snprintf(detail, sizeof(detail), "rc=%d", rc);
|
|
check("mailbox cleared", rc == FT_TT_OK, detail);
|
|
|
|
ft_tt_launch_state state;
|
|
rc = ft_tt_launch_read_state(&noc, x, y, &state);
|
|
snprintf(detail, sizeof(detail), "go[0].signal=0x%02x sync=%02x%02x%02x%02x rd_ptr=%u",
|
|
state.go_messages[0].signal, state.subordinate_sync[0], state.subordinate_sync[1],
|
|
state.subordinate_sync[2], state.subordinate_sync[3], state.launch_msg_rd_ptr);
|
|
check("mailbox reads back clear", rc == FT_TT_OK && state.go_messages[0].signal == 0u,
|
|
detail);
|
|
if (failures != 0) {
|
|
goto done;
|
|
}
|
|
|
|
/* --- step 4: load firmware, prime, deassert BRISC, THE OFFSET PROOF ---- */
|
|
printf("\nstep 4 — load firmware, prime, deassert BRISC (the offset proof)\n");
|
|
if (ft_tt_firmware_directory() == NULL) {
|
|
check("firmware directory named (FT_TT_FIRMWARE_DIR)", false, "unset");
|
|
goto done;
|
|
}
|
|
for (unsigned p = 0; p < FT_TT_PROC_COUNT; p++) {
|
|
char path[512];
|
|
size_t size = 0;
|
|
unsigned char *elf;
|
|
|
|
(void)snprintf(path, sizeof(path), "%s/%s", ft_tt_firmware_directory(), FW_FILE_NAMES[p]);
|
|
elf = load_file(path, &size);
|
|
if (elf == NULL) {
|
|
check("firmware image loaded", false, path);
|
|
goto done;
|
|
}
|
|
rc = ft_tt_launch_load_image(&noc, x, y, p, FT_TT_IMAGE_FIRMWARE, elf, size, NULL);
|
|
snprintf(detail, sizeof(detail), "proc %u, %zu bytes, rc=%d", p, size, rc);
|
|
check("firmware image loaded", rc == FT_TT_OK, detail);
|
|
free(elf);
|
|
if (failures != 0) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
rc = ft_tt_launch_prime_firmware_handshake(&noc, x, y);
|
|
snprintf(detail, sizeof(detail), "rc=%d — RUN_MSG_INIT, not DONE(=0)", rc);
|
|
check("handshake primed", rc == FT_TT_OK, detail);
|
|
|
|
rc = ft_tt_launch_deassert_reset(&noc, x, y, FT_TT_SOFT_RESET_BRISC, false);
|
|
snprintf(detail, sizeof(detail), "rc=%d — BRISC only; it releases the others", rc);
|
|
check("BRISC deasserted", rc == FT_TT_OK, detail);
|
|
|
|
rc = ft_tt_launch_wait_firmware_ready(&noc, x, y, 2000);
|
|
snprintf(detail, sizeof(detail), "rc=%d", rc);
|
|
check("firmware reported ready within 2 s", rc == FT_TT_OK, detail);
|
|
|
|
rc = ft_tt_launch_read_state(&noc, x, y, &state);
|
|
if (rc != FT_TT_OK) {
|
|
check("state readable after boot", false, "read failed");
|
|
goto done;
|
|
}
|
|
|
|
snprintf(detail, sizeof(detail), "0x%02x (BRISC firmware writes DONE at the end of boot)",
|
|
state.go_messages[0].signal);
|
|
check("go signal went 0x40 -> 0x00", state.go_messages[0].signal == 0u, detail);
|
|
|
|
bool sync_clear = true;
|
|
for (unsigned i = 0; i < 4u; i++) {
|
|
if (state.subordinate_sync[i] != 0u) {
|
|
sync_clear = false;
|
|
}
|
|
}
|
|
snprintf(detail, sizeof(detail), "%02x %02x %02x %02x — all four subordinates done",
|
|
state.subordinate_sync[0], state.subordinate_sync[1], state.subordinate_sync[2],
|
|
state.subordinate_sync[3]);
|
|
check("subordinate_sync reads 00 00 00 00", sync_clear, detail);
|
|
|
|
snprintf(detail, sizeof(detail), "%u (BRISC firmware zeroes it at boot)", state.launch_msg_rd_ptr);
|
|
check("launch_msg_rd_ptr reads 0", state.launch_msg_rd_ptr == 0u, detail);
|
|
|
|
if (failures != 0) {
|
|
printf("\n--- state dump ---\n");
|
|
ft_tt_launch_dump_state(&state, x, y);
|
|
}
|
|
|
|
done:
|
|
/* Leave the core parked in reset rather than running. */
|
|
(void)ft_tt_launch_assert_reset(&noc, x, y, FT_TT_SOFT_RESET_ALL_RISCS);
|
|
(void)ft_tt_tlb_window_free(ctx, window);
|
|
printf("\n%d checks failed\n", failures);
|
|
ft_tt_ctx_free(ctx);
|
|
return failures == 0 ? 0 : 1;
|
|
}
|