P49 makes mstatus.MPP real. The chip now has a 2-bit cur_priv
register, mret returns to the priv-when-trapped, and the ECALL
cause code reflects priv-at-call.
Status: RTL pass. Probes (WARL + storage + full M→S→trap→M round trip with probe-local trap handler) all PASS at 5,102,225 clocks. FreeRTOS demo runs to halt cleanly afterwards.
What changed in the RTL
- New
cur_privregister, resets to M (2'b11). enter_trapcapturescur_privintomstatus.MPPand setscur_priv <= M.mretrestorescur_privfrommstatus.MPP, then sets MPP back to M (post-mret MPP defaults to least-priv-supported per spec; we pick M so a stuck-in-S bug fails loudly).- ECALL cause = M/S/U-mode based on
cur_privat the call. - WARL on mstatus.MPP: writes of
U(2'b00) and reserved2'b10coerce toS(2'b01). M and S pass through. We don’t have U-mode hardware so this keeps a software typo from silently entering an unsupported state.
What still goes to M
Every trap. We don’t yet consult medeleg/mideleg for routing,
so even from S-mode the trap goes to mtvec and the handler runs
in M. That’s spec-correct behavior when medeleg=0/mideleg=0
(which they are at reset), and it lets the existing FreeRTOS
trap handler keep working unchanged.
S-mode trap routing is the next big rung.
Probe scope
priv_tracking_probe (error codes 92-97):
- mstatus.MPP storage round-trip for both M and S.
- WARL coerce of U (
2'b00) and reserved (2'b10) → S. - Independent of other mstatus bits (no smearing).
- Restoring MPP=M before handing off to FreeRTOS.
priv_transition_probe (error codes 100-104) — full M→S→trap→M
round trip:
- Save
mtvec/mstatus, install probe-local naked handler. - Set
mepcto a forward label, writemstatuswith MPP=S, runmret. Chip switchescur_privto S. - The label runs
ecall. Trap fires; chip capturescur_priv=Sinto MPP, setscur_priv=M, jumps to handler. - Handler captures
mcause/mstatus, sets MPP=M, advances mepc past the ecall, runsmret. Execution resumes after the ecall in M-mode. - Probe verifies
mcause==MCAUSE_ECALL_SMODE, captured MPP==S, and that a follow-up ecall yieldsMCAUSE_ECALL_MMODE. Restore mtvec/mstatus.
This exercises the full priv-transition path end to end on the chip. The remaining work (S-mode trap routing through stvec instead of mtvec) is the next rung.
One RTL fix surfaced
csr_mtvec_hi was 10 bits wide ([11:2]) — capping mtvec at
4 KiB. The FreeRTOS handler at 0x100 was always under that, so
nothing noticed; our probe handler at 0x51f8 was not. P49
widens to the full 30-bit base ([31:2]).
Files
src/top.sv- priv register + MPP wiring + mret restore + ECALL causeapp/main.c- priv_tracking_probe
Harden
NOT RUN. Estimated +25 cells over P48 (2-bit priv reg, MPP mux,
WARL coerce mux).
What just happened?
The chip can finally distinguish M from S in priv state. That’s the first real behavioral step into the privileged-architecture spec; everything before P49 was M-mode-only with the S CSRs as storage. The next rung is S-mode trap routing.