P50 wires medeleg from P48’s M-readable storage into actual
trap routing. When the chip takes an exception in S-mode and the
corresponding medeleg bit is set, the trap goes to S — uses
stvec, writes sepc/scause/stval, captures
cur_priv into mstatus.SPP. Adds sret so S-mode software
can return.
Status: RTL pass. Delegation probe drives M→S→trap-to-S→ sret→S→trap-to-M→mret→M and verifies cause/SPP/MPP captures at every step. PASS at 5,102,703 clocks; FreeRTOS demo runs to halt cleanly afterwards.
What changed
enter_trap decides M vs S routing
delegate_to_s = (cur_priv != M)
&& ((!is_interrupt && medeleg[cause_idx]) ||
( is_interrupt && mideleg[cause_idx]));
If delegating, write sepc/scause/stval, capture
cur_priv into mstatus.SPP, save SIE→SPIE, set
cur_priv=S, jump to stvec. Else the existing M-mode path.
sret instruction
0x10200073. Restores cur_priv from mstatus.SPP, restores
SIE from SPIE, sets SPP back to U=0 (per spec — least-priv
supported on sret), jumps to csr_sepc. Behavioral dual of mret.
sstatus is a subset view of mstatus
The P47 standalone storage register is gone. Reads return
mstatus & SSTATUS_MASK (SIE bit 1 | SPIE bit 5 | SPP
bit 8). Writes update only those bits in mstatus. Single
architectural register, M-only and S-visible projections.
What’s tested
trap_delegation_probe (error codes 110-115):
- Save mtvec/stvec/mstatus/medeleg, install probe-local M and S trap handlers.
medeleg[9] = 1,mretwithMPP=Sto a label.ecallfrom S — delegated. Chip routes tostvec, capturescur_priv=Sintosstatus.SPP.- S handler captures
scause(=9) +sstatus, sets SPP=1,sretkeeps us in S. - Clear
medeleg,ecallfrom S — not delegated. Chip routes tomtvec, capturescur_priv=Sintomstatus.MPP. - M handler captures,
mretback to M.
End-to-end the cause codes, captured priv values, and handler hit-counts all match expectations.
What still doesn’t delegate
Interrupts. The routing decision consults mideleg correctly,
but our interrupt fast paths (timer/external) haven’t been
exercised from S-mode and sip/sie are still P47 standalone
storage (not subset views of mip/mie). Next rung.
M-only CSR access from S isn’t privilege-checked either. The
spec says accessing mstatus/medeleg/mtvec/etc. from S
should illegal-instr trap; our chip allows it. Separable rung.
Files
src/top.sv- delegation routing + sret + sstatus subsetapp/main.c- trap_delegation_probe
Harden
NOT RUN. Estimated +60 cells over P49 (sret decode, delegation
mux on trap entry, sstatus subset projection logic).
What just happened?
The chip can now take a trap in one priv mode and run the handler in another. That’s the first piece of real privilege machinery — every prior rung was M-only-everywhere.
Linux uses this heavily: page faults, ecall-from-U, supervisor timer interrupts all need to land in S without an M-mode bounce. We have the exception path; the interrupt path and the privilege-checked CSR access path are next.