C6-Reticulum-ASM/Makefile
2026-05-03 15:58:57 -06:00

158 lines
6.2 KiB
Makefile

# Top-level Makefile.
#
# Targets (host-side):
# make test run the host-side test suite (pytest)
# make registry cross-check FUNCTIONS.md against src/
# make spec FILE=… validate one spec block
# make verify FN=… dispatch one function's tests + verifier
# make verify-all dispatch every registered function
# make ci equivalent to: registry + test
#
# Targets (target-side, requires riscv64-elf-binutils + qemu, see ADR-0008):
# make build [TARGET=qemu-virt|c6] assemble + link → build/firmware.elf
# make emu run firmware.elf in qemu-system-riscv32
# make image TARGET=c6 only — wraps ELF in ESP image format
# make flash TARGET=c6 only — esptool write_flash 0x0
# make monitor open serial monitor on the C6 USB-Serial/JTAG
# make clean rm -rf build/
PYTHON := python3
# Use := (not ?=) so make's built-in AS=as default does not override us.
# Override on the command line: `make build AS=path/to/as`.
AS := riscv64-elf-as
LD := riscv64-elf-ld
OBJCOPY := riscv64-elf-objcopy
OBJDUMP := riscv64-elf-objdump
QEMU := qemu-system-riscv32
# Default to the qemu-virt target. The C6 target lands once real-hardware
# bring-up starts (and toolchain/c6.ld exists).
TARGET ?= qemu-virt
ARCH_FLAGS := -march=rv32imac -mabi=ilp32
ifeq ($(TARGET),qemu-virt)
ASDEFS := --defsym TARGET_QEMU_VIRT=1
LDSCRIPT := toolchain/qemu-virt.ld
else ifeq ($(TARGET),c6)
ASDEFS := --defsym TARGET_C6=1
LDSCRIPT := toolchain/c6.ld
else
$(error TARGET must be qemu-virt or c6, got '$(TARGET)')
endif
BUILD_DIR := build/$(TARGET)
SRC_DIRS := src/boot src/clock src/uart src/log src/kiss src/packet src/state src/crypto/sha256 src/crypto/sha512 src/crypto/hmac src/crypto/hkdf src/crypto/aes src/crypto/x25519 src/crypto/rng src/crypto/ed25519 src/identity src/destination src/announce src/transport src/link src/resource src/lxmf src/flash src/interface/gpio src/interface/spi src/interface/lora
SRCS := $(wildcard $(addsuffix /*.S,$(SRC_DIRS)))
OBJS := $(patsubst src/%.S,$(BUILD_DIR)/%.o,$(SRCS))
ELF := $(BUILD_DIR)/firmware.elf
BIN := $(BUILD_DIR)/firmware.bin
ASFLAGS := $(ARCH_FLAGS) $(ASDEFS) -I src/include
LDFLAGS := -nostdlib -static --no-warn-rwx-segments
.PHONY: help test tools-test registry spec verify verify-all ci build emu image flash monitor clean
# C6 hardware bring-up. Override on the command line if your board enumerates
# at a different /dev/cu.* path or you want a faster flashing baud.
ESPTOOL ?= esptool.py
ESP_PORT ?= /dev/cu.usbmodem4101
ESP_BAUD ?= 460800
ESP_CHIP ?= esp32c6
IMAGE_BIN := build/c6/firmware.image.bin
help:
@echo "Targets:"
@echo " test run pytest over tests/"
@echo " registry check FUNCTIONS.md <-> src/ agreement"
@echo " stack worst-case stack depth from _reset"
@echo " spec FILE=… validate one spec block (any path)"
@echo " verify FN=… dispatch one function's tests + verifier [JSON=1]"
@echo " verify-all dispatch every registered function"
@echo " ci equivalent to: registry + test"
@echo " build [TARGET=qemu-virt|c6] assemble + link"
@echo " emu run firmware.elf in qemu-system-riscv32"
@echo " image (TARGET=c6) wrap ELF in ESP image header"
@echo " flash (TARGET=c6) esptool write_flash 0x0 \$$IMAGE_BIN"
@echo " monitor open the C6 USB Serial/JTAG (115200, ESP_PORT=$(ESP_PORT))"
@echo " clean remove build/"
test:
$(PYTHON) -m pytest tests/ -q
tools-test:
$(PYTHON) -m pytest tests/tools/ -q
registry:
$(PYTHON) tools/check_registry.py
stack:
$(PYTHON) tools/check_stack.py
spec:
@if [ -z "$(FILE)" ]; then echo "usage: make spec FILE=path/to/file.S"; exit 2; fi
$(PYTHON) tools/parse_spec.py "$(FILE)"
verify:
@if [ -z "$(FN)" ]; then echo "usage: make verify FN=<function_name> [JSON=1]"; exit 2; fi
$(PYTHON) verify_cli.py $(if $(JSON),--json) "$(FN)"
verify-all:
$(PYTHON) verify_cli.py --all $(if $(JSON),--json)
ci: registry stack test
# --- target-side build ----------------------------------------------------
build: $(ELF) $(BIN)
$(BUILD_DIR)/%.o: src/%.S | $(BUILD_DIR)
@mkdir -p $(dir $@)
$(AS) $(ASFLAGS) -o $@ $<
$(ELF): $(OBJS) $(LDSCRIPT)
$(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(OBJS)
$(BIN): $(ELF)
$(OBJCOPY) -O binary $< $@
$(BUILD_DIR):
@mkdir -p $@
emu: build
$(QEMU) -machine virt -cpu rv32 -bios none -kernel $(ELF) \
-nographic -no-reboot -d guest_errors
# --- C6 hardware bring-up -------------------------------------------------
# `image` produces an ESP image-format binary that the C6 mask-ROM second-stage
# loader can consume from flash offset 0x0. esptool's elf2image walks PT_LOAD
# program headers, segregates IRAM vs DRAM segments, prepends the image header
# (magic 0xE9, segment count, entry-point address), and appends the SHA-256
# integrity hash. We pass --flash_size 4MB to match the ESP32-C6FH4 part on
# the Adafruit Feather (4 MB embedded NOR flash).
image: build
ifneq ($(TARGET),c6)
$(error make image only works with TARGET=c6, got '$(TARGET)')
endif
# DIO + 40 MHz are the conservative defaults that match the Adafruit
# Feather ESP32-C6's factory flash configuration. QIO at boot returns
# garbage data on this board — empirically the ROM's segment-data XOR
# walk reads 0x00 throughout, producing calculated == seed (0xef) and
# a checksum mismatch. Higher speeds / QIO can be re-enabled once the
# bootloader-equivalent code reconfigures the SPI controller.
$(ESPTOOL) --chip $(ESP_CHIP) elf2image --flash-size 4MB \
--flash-mode dio --flash-freq 40m \
--output $(IMAGE_BIN) $(ELF)
flash: image
$(ESPTOOL) --chip $(ESP_CHIP) --port $(ESP_PORT) --baud $(ESP_BAUD) \
write_flash 0x0 $(IMAGE_BIN)
# Convenience: open a serial monitor. Uses miniterm.py (ships with pyserial,
# which is an esptool dependency, so it is always available alongside esptool).
# Ctrl-] to exit. The C6's USB Serial/JTAG ignores baud rate; 115200 matches
# what the host driver advertises.
monitor:
$(PYTHON) -m serial.tools.miniterm --raw --eol LF $(ESP_PORT) 115200
clean:
rm -rf build/