No. 28 / project of 147 on the ladder

RV32I machine-mode interrupts

introduces — Machine timer interrupt, external IRQ, mie/mip, and mstatus interrupt stack

harden statelast run2026-05-01

P28 gives the P27 trap core something asynchronous to do. It adds a machine timer source, a top-level external interrupt input, mie/mip, and the mstatus.MIE / mstatus.MPIE stack needed for interrupt entry and mret.

Status: RTL pass. Directed machine-mode interrupt 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/28_rv32i_machine_interrupts/test

Result: PASS

testresult
CSR read/write and immediate setPASS
ecall trap and mret resumePASS
illegal-instruction trap and mret resumePASS
ebreak trap and mret resumePASS
misaligned load trapPASS
misaligned store trapPASS
masked timer interrupt stays pendingPASS
timer interrupt and mret resumePASS
external interrupt handlerPASS

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

Harden status: NOT RUN

What Changed

The new interrupt substrate is intentionally small:

piecebehavior
miewritable MTIE and MEIE bits
mipread-only MTIP and MEIP pending bits
mstatusMIE clears on trap entry, old MIE saves into MPIE; mret restores it
timerlocal 32-bit counter plus compare register
external IRQone synchronized top-level interrupt input
trap causemachine timer 0x8000_0007, machine external 0x8000_000b

The timer controls live at custom CSR addresses 0x7c0 and 0x7c1. That is a teaching shortcut, not a standard RISC-V platform model. In real systems, mtime and mtimecmp are normally memory-mapped registers. P29 should move this toward a normal MMIO map.

What This Proves

P27 proved that software can handle exceptions. P28 proves that hardware event sources can enter the same trap path:

  1. a pending timer interrupt remains visible in mip while globally masked;
  2. enabling mstatus.MIE and mie.MTIE vectors to mtvec;
  3. trap entry records the interrupt mcause;
  4. mstatus stacks MIE into MPIE;
  5. a handler can disable the timer, rewrite mepc, and return with mret;
  6. a top-level external IRQ can trigger the machine external interrupt path.

The scoped ACT4/Sail run matters because these changes touch fetch and trap entry. 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, standard memory-mapped CLINT-style timer registers, nested interrupt policy beyond the simple MIE/MPIE stack, and official privileged architecture tests.

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