143 lines
6.4 KiB
Makefile
143 lines
6.4 KiB
Makefile
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
# Copyright (C) 2026 David (Corey) Herrera
|
|
# Foundry Blackhole backend — the EMBEDDED PROGRAM BLOB's off-device suite.
|
|
#
|
|
# make -f Makefile.programblob check # fixtures, build, run both gates
|
|
# make -f Makefile.programblob red # clean first, then the above
|
|
# make -f Makefile.programblob gates # the whole mutation battery (slow, thorough)
|
|
#
|
|
# NO DEVICE. Nothing here calls ft_tt_runtime_open(), ft_tt_runtime_boot_core() or anything
|
|
# else that reaches a card, and the build does NOT define FT_TT_PHASE1B_NOC_ENABLED, so the
|
|
# NoC fence is closed: every device call in the linked backend returns FT_TT_EPERM before
|
|
# issuing a transaction. ft_tt_runtime.c is linked because the OVERRIDE arm calls its two
|
|
# file loaders, and linking it drags the backend in with it — the same arrangement
|
|
# test_launch already uses to run off-card.
|
|
#
|
|
# WHOLE-PROGRAM LINKS, NO OBJECT FILES. Apple ships GNU make 3.81, whose one-second mtime
|
|
# granularity silently skips a rebuild of a source edited in the same second as its object,
|
|
# so a mutation loop over incremental objects can be testing the PREVIOUS binary
|
|
# (coding-best-practices.md §9). There are no .o files to go stale here, and `red` deletes
|
|
# the binaries as well.
|
|
#
|
|
# WHY THERE ARE TWO BINARIES. test_program_blob is compiled against a GENERATED FIXTURE
|
|
# blob and checks the mechanism. test_program_blob_empty checks that the EMPTY state refuses
|
|
# loudly instead
|
|
# of passing over zero programs. Each asserts WHICH header it got, so neither can quietly
|
|
# become a re-run of the other.
|
|
|
|
CC ?= cc
|
|
SRC_DIR := ..
|
|
TOOLS := ../tools
|
|
BUILD := build-programblob
|
|
|
|
# Where make_program_blob_fixtures.py writes, and where the generated fixture header lands.
|
|
FIXTURES ?= /tmp/ft_tt_program_blob_fixtures
|
|
FIXTURE_HEADER := $(FIXTURES)/ft_tt_programs_fixture.h
|
|
|
|
CFLAGS := -std=c99 -O1 -g \
|
|
-Wall -Wextra -Werror -pedantic \
|
|
-Wshadow -Wconversion -Wstrict-prototypes -Wformat=2 \
|
|
-fstack-protector-strong \
|
|
-I$(SRC_DIR) -I.
|
|
|
|
# ft_tt_runtime.c is the reason for most of this list: it is the file the override arm
|
|
# calls, and it references the launch, NoC, TLB and coordinate layers. Since 2026-08-17 it
|
|
# also dispatches to the USERSPACE DRIVER, so ft_tt_runtime_udrv.c and the ft_tt_udrv_*
|
|
# modules under it come with it. None of them is reached here — no card is opened — but they
|
|
# must LINK, and that is itself a gate: a seam that only builds where the card is, is a seam
|
|
# nobody finds broken until the card has moved.
|
|
BACKEND := $(SRC_DIR)/ft_tt_kmd.c \
|
|
$(SRC_DIR)/ft_tt_arch.c \
|
|
$(SRC_DIR)/ft_tt_arch_blackhole.c \
|
|
$(SRC_DIR)/ft_tt_device.c \
|
|
$(SRC_DIR)/ft_tt_tlb.c \
|
|
$(SRC_DIR)/ft_tt_coord.c \
|
|
$(SRC_DIR)/ft_tt_arc_telemetry.c \
|
|
$(SRC_DIR)/ft_tt_noc.c \
|
|
$(SRC_DIR)/ft_tt_buffer.c \
|
|
$(SRC_DIR)/ft_tt_elf.c \
|
|
$(SRC_DIR)/ft_tt_kernel_blob.c \
|
|
$(SRC_DIR)/ft_tt_udrv_claim.c \
|
|
$(SRC_DIR)/ft_tt_udrv_pci_sysfs.c \
|
|
$(SRC_DIR)/ft_tt_udrv_tlb_encode.c \
|
|
$(SRC_DIR)/ft_tt_udrv_noc.c \
|
|
$(SRC_DIR)/ft_tt_udrv_noc_bank.c \
|
|
$(SRC_DIR)/ft_tt_udrv_arc_msg.c \
|
|
$(SRC_DIR)/ft_tt_udrv_arc_probe.c \
|
|
$(SRC_DIR)/ft_tt_tensix_claim.c \
|
|
$(SRC_DIR)/ft_tt_launch.c \
|
|
$(SRC_DIR)/ft_tt_runtime.c \
|
|
$(SRC_DIR)/ft_tt_runtime_udrv.c \
|
|
$(SRC_DIR)/ft_tt_udrv_reset.c \
|
|
$(SRC_DIR)/ft_tt_program_blob.c \
|
|
$(SRC_DIR)/ft_tt_program_resolve.c \
|
|
ft_tt_test_report.c
|
|
|
|
FIXTURE_TARGET := $(BUILD)/test_program_blob
|
|
EMPTY_TARGET := $(BUILD)/test_program_blob_empty
|
|
|
|
.PHONY: all check red gates fixtures clean
|
|
|
|
all: $(FIXTURE_TARGET) $(EMPTY_TARGET)
|
|
|
|
$(BUILD):
|
|
mkdir -p $(BUILD)
|
|
|
|
# The fixtures and the fixture blob are ONE step: a generated header without the directories
|
|
# it was generated from cannot be byte-compared against anything.
|
|
fixtures: $(FIXTURE_HEADER)
|
|
|
|
$(FIXTURE_HEADER): make_program_blob_fixtures.py $(TOOLS)/emit_tensix_program_header.py
|
|
rm -rf $(FIXTURES)
|
|
python3 make_program_blob_fixtures.py $(FIXTURES)
|
|
python3 $(TOOLS)/emit_tensix_program_header.py -o $@ \
|
|
--kernels $(FIXTURES)/kernels --toolchain fixture-1.0 \
|
|
--recipe 'make_program_blob_fixtures.py' \
|
|
--program $(FIXTURES)/programs/prog_alpha \
|
|
--program $(FIXTURES)/programs/prog_beta \
|
|
--program $(FIXTURES)/programs/prog_gamma
|
|
|
|
$(FIXTURE_TARGET): $(FIXTURE_HEADER) test_program_blob.c $(BACKEND) | $(BUILD)
|
|
rm -f $@
|
|
$(CC) $(CFLAGS) -I$(FIXTURES) \
|
|
-DFT_TT_PROGRAMS_HEADER='"ft_tt_programs_fixture.h"' \
|
|
test_program_blob.c $(BACKEND) -o $@
|
|
|
|
# The empty arm builds against its OWN empty header, not the shipping one. It used to compile
|
|
# against $(SRC_DIR)/ft_tt_programs.h and pass only because that file was still the placeholder
|
|
# -- so the moment a real blob was generated (2026-08-17) its six checks failed, having asserted
|
|
# a property of the artifact rather than of the code. An empty-state gate that can only pass
|
|
# before the work is done is backwards.
|
|
$(BUILD)/ft_tt_programs_empty.h: | $(BUILD)
|
|
printf '%s\n' \
|
|
'/* GENERATED for test_program_blob_empty -- the empty-blob arm needs an empty blob' \
|
|
' * regardless of what the shipping header holds. Not the placeholder; a fixture. */' \
|
|
'#ifndef FT_TT_PROGRAMS_H' \
|
|
'#define FT_TT_PROGRAMS_H' \
|
|
'static const ft_tt_program_blob_entry *const ft_tt_program_blob_table_storage[1] = { 0 };' \
|
|
'#define ft_tt_program_blob_table ft_tt_program_blob_table_storage' \
|
|
'#define FT_TT_PROGRAM_BLOB_TABLE_COUNT 0u' \
|
|
'#endif' > $@
|
|
|
|
$(EMPTY_TARGET): test_program_blob_empty.c $(BACKEND) $(BUILD)/ft_tt_programs_empty.h | $(BUILD)
|
|
rm -f $@
|
|
$(CC) $(CFLAGS) -I$(BUILD) -DFT_TT_PROGRAMS_HEADER='"ft_tt_programs_empty.h"' \
|
|
test_program_blob_empty.c $(BACKEND) -o $@
|
|
|
|
check: $(FIXTURE_TARGET) $(EMPTY_TARGET)
|
|
@rc=0; \
|
|
echo "=== $(FIXTURE_TARGET) ==="; ./$(FIXTURE_TARGET) $(FIXTURES) || rc=1; \
|
|
echo "=== $(EMPTY_TARGET) ==="; ./$(EMPTY_TARGET) $(FIXTURES) || rc=1; \
|
|
exit $$rc
|
|
|
|
# The battery. Every gate above is re-run against a header that has been damaged in one
|
|
# specific way, and each damaged build MUST fail for the stated reason — a red for some
|
|
# other reason proves nothing about the check it was aimed at.
|
|
gates: fixtures
|
|
bash run_program_blob_gates.sh
|
|
|
|
red: clean check
|
|
|
|
clean:
|
|
rm -rf $(BUILD) $(FIXTURES)
|