No. 29 / project of 147 on the ladder

RV32I MMIO platform

introduces — Memory-mapped timer and UART registers on the external-memory shell

harden statelast run2026-05-01

P29 turns P28’s timer shortcut into a small memory-mapped platform. Program RAM still lives on the external bus and is still loaded through the UART loader, but platform registers now sit at 0x1000_0000.

Status: RTL pass. Top-level directed MMIO tests pass, and the scoped ACT4/Sail rv32i/I batch still passes with PASS=39 FAIL=0 NOT RUN=0. Hardening and official privileged tests are NOT RUN for this rung.

The Result

Run it:

make -C projects/29_rv32i_mmio_platform/test

Result: PASS

testresult
external RAM plus MMIO UARTPASS
MMIO timer interrupt and mretPASS
external interrupt through MMIO platformPASS

ACT4/Sail rv32i/I: PASS=39 FAIL=0 NOT RUN=0

Harden status: NOT RUN

Memory Map

addressbehavior
0x0000_0000external RAM bus
0x1000_000032-bit mtime
0x1000_000432-bit mtimecmp
0x1000_1000UART status
0x1000_1004UART data

This is intentionally small. It is not a full platform spec and it is not a CLINT implementation. The important improvement is that software now uses ordinary lw and sw instructions to reach timer and UART registers.

What Changed

P28 had custom local CSRs at 0x7c0 and 0x7c1 for timer test control. P29 removes those from the core. The core now receives timer_irq and external_irq as platform signals and keeps only the machine CSR/trap/interrupt state.

The top-level wrapper now arbitrates CPU memory requests:

  • loader-owned traffic goes to external RAM;
  • CPU low-address traffic goes to external RAM;
  • CPU 0x10xx_xxxx traffic goes to the internal MMIO block;
  • MMIO timer pending feeds the machine timer interrupt bit in mip;
  • UART data writes latch a byte and pulse a simple transmit-valid signal.

What This Proves

The directed testbench boots small programs through the UART loader, then lets the CPU execute them from external RAM. Those programs:

  1. store and load a word through external RAM;
  2. write a byte to the MMIO UART data register;
  3. read the MMIO UART status register;
  4. program mtime and mtimecmp through stores;
  5. take a machine timer interrupt and return through mret;
  6. take a machine external interrupt through the same CSR path.

The scoped ACT4/Sail run matters because P29 touches the top-level bus around instruction fetch and data access. The base integer batch still passes: PASS=39 FAIL=0 NOT RUN=0.

Scope

Still unsupported: supervisor mode, vectored mtvec, delegation CSRs, satp, an MMU, 64-bit mtime / mtimecmp, a real interrupt controller, and official privileged architecture tests.

So the honest label is narrow: PASS for directed MMIO RTL tests, PASS for scoped ACT4/Sail rv32i/I, NOT RUN for hardening, and NOT RUN for privileged compliance.