journal 2026-05-01

P31 gets to freestanding C

p31riscvruntimec

P31 is the first rung where the runtime body is C instead of assembly.

The reset path still lives in runtime/start.S: set the stack pointer, set mtvec, clear .bss, call main(), and copy the return value into x5 before halting. The actual probe lives in runtime/main.c.

Commands:

make -C projects/31_rv32i_c_runtime_probe/test
make -C projects/31_rv32i_c_runtime_probe/test act4

Results:

checkresult
Compiled C runtimePASS
Runtime UART sequence CTEPASS
Runtime timer interrupt visible to CPASS
Runtime external interrupt visible to CPASS
ACT4/Sail rv32i/IPASS=39 FAIL=0 NOT RUN=0
HardeningNOT RUN
Official privileged testsNOT RUN

One useful cleanup: the linker script now gives text and data separate program headers, so GNU ld no longer warns about a single RWX load segment. That does not add memory protection to the tiny platform. It just makes the ELF shape less sloppy.

The current generated binary is 540 bytes. Small is good here. We are proving that the core can run compiler output with calls, globals, volatile MMIO, and trap-updated C flags before asking it to run anything that looks like real firmware.