journal 2026-05-03

P41 lands the FreeRTOS port (compile)

p41freertosport

P40 made a clean foundation; P41 builds on it. FreeRTOS-Kernel V11.1.0 now links cleanly against our chip via a small port adapter.

The compile-only rung was deliberate: FreeRTOS bring-up has a chain of “did the port work?” questions and we want each rung to fail in exactly one place if it fails. P41 covers “does it build and link?”; P42 (next) covers “does scheduling actually work?”.

Three places the port had to deviate from the upstream RISC-V port:

  1. 32-bit mtime/mtimecmp. The upstream vPortSetupTimerInterrupt writes a 64-bit *pullMachineTimerCompareRegister. Our DUT only has a 32-bit mtimecmp at 0x10000004. A 64-bit store would step on a non-existent 0x10000008. Disabled the upstream default via configMTIMECMP_BASE_ADDRESS = 0 and provided ours.

  2. freertos_risc_v_application_interrupt_handler. With portasmHAS_MTIME = 0, the upstream port routes all interrupts through this user hook. Mine dispatches on mcause: machine timer updates the deadline and calls xTaskIncrementTick/ vTaskSwitchContext; anything else halts loudly.

  3. Freestanding libc. The Arch riscv64-elf-gcc doesn’t ship newlib - just freestanding headers. FreeRTOS uses <stdlib.h> (for size_t) and <string.h> (memset/memcpy/etc). Header shims under port/freestanding-shims/ plus byte-loop implementations in port/p41_libc.c.

ELF: 13 KiB text, 12 B data, 16 KiB bss (mostly the FreeRTOS heap). About 30 KiB total. Comfortable in our 256 KiB external memory.

The trap-frame work in P40 paid off here. FreeRTOS’s portContext.h expects a 32-word context with mcause/mepc at the end - which is exactly the shape P40 built. The IRQ save/restore primitives I wrote for P40 satisfy what FreeRTOS’s portmacro.h wants. None of this is coincidence - P40’s trap frame was designed reading FreeRTOS’s port, so this part should drop in cleanly.

It does. P42 next will tell us whether the scheduler actually schedules.