#!/usr/bin/env python3 # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (C) 2026 David (Corey) Herrera """ make_program_blob_fixtures.py Build the synthetic Tensix program directories tests/test_program_blob.c gates the embedded-program blob against. WHY SYNTHETIC. Real program directories are built by build_tensix_program.sh, which needs SFPI, which exists only on the card host. So the blob mechanism is gated here against directories this script writes: minimal but genuinely valid ELF32/LE/ET_EXEC/EM_RISCV images that ft_tt_elf_parse accepts, laid out with the file names, the .src sidecars and the fw_*.elf set that build_tensix_program.sh produces. WHAT THAT DOES AND DOES NOT PROVE, stated so it is not overread: it proves the blob format, the generator's refusals, the lookup, the integrity checks and the override behaviour. It proves NOTHING about SFPI output. A real regeneration is untested and needs a bank session. Usage: make_program_blob_fixtures.py Writes: /kernels/ the "source tree" the .src sidecars name /programs/prog_alpha/ five processors, firmware set A /programs/prog_beta/ DM0+DM1 only, firmware set A (dedup + partial mask) /programs/prog_gamma/ five processors, firmware set B (a DIFFERENT set) /bad// one directory per refusal the generator must make Two of those cases are about PROVENANCE (tools/tensix_image_provenance.py): `no_provenance/prog_unvouched` records nothing, and `stale_provenance/prog_drifted` has sources edited after its records were written WITHOUT their mtimes moving — the case the mtime check is structurally unable to see. Status: Runs on the Mac; no card, no toolchain. Issues: None Todo: None """ import os import struct import sys import time sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tools")) from tensix_image_provenance import build_record, hash_file # noqa: E402 PROCESSOR_FILE_NAMES = ["brisc.elf", "ncrisc.elf", "trisc0.elf", "trisc1.elf", "trisc2.elf"] FIRMWARE_FILE_NAMES = [ "fw_brisc.elf", "fw_ncrisc.elf", "fw_trisc0.elf", "fw_trisc1.elf", "fw_trisc2.elf", ] EHDR_SIZE = 52 PHDR_SIZE = 32 TEXT_VADDR = 0x00008000 DATA_VADDR = 0xFFB00820 DEFAULT_TEXT_BYTES = 512 DEFAULT_DATA_BYTES = 64 def deterministic_bytes(seed, count): """A distinct, reproducible payload per (program, processor), so a lookup that returns the WRONG image is visible as different bytes rather than as a passing test.""" payload = bytearray(count) state = 0 for character in seed.encode("utf-8"): state = (state * 131 + character) & 0xFFFFFFFF for index in range(count): state = (state * 1103515245 + 12345) & 0xFFFFFFFF payload[index] = (state >> 16) & 0xFF return bytes(payload) def build_elf(seed, text_bytes=DEFAULT_TEXT_BYTES, data_bytes=DEFAULT_DATA_BYTES): """A two-PT_LOAD Tensix-shaped ELF32: text at its own vaddr with e_entry on its first byte, and a data segment whose VMA (local memory) differs from its LMA (staged in L1) exactly as a real kernel's does.""" phdr_count = 2 if data_bytes else 1 text_offset = EHDR_SIZE + PHDR_SIZE * phdr_count assert text_offset % 4 == 0 data_offset = text_offset + text_bytes header = bytearray() header += b"\x7fELF" + bytes([1, 1, 1, 0]) + bytes(8) # e_ident: ELF32, LE, v1 header += struct.pack("") root = os.path.abspath(argv[0]) kernels = os.path.join(root, "kernels") os.makedirs(kernels, exist_ok=True) build_good_programs(root, kernels) newer_source = build_bad_programs(root, kernels) age_tree(root, 600) os.utime(newer_source, None) print("fixtures under %s" % root) return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))