# 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= [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/