i-RISCV CPU Internals
The i-RISCV core is a single-issue, in-order processor. The following mechanisms are supported.
Pipeline
A six-stage pipeline (IF1/IF2/ID/EX/MEM/WB) with synchronous instruction and data memory access.
flowchart LR
IF1["IF1<br/>PC gen"] --> IF2["IF2<br/>imem data phase"]
IF2 --> ID["ID<br/>decode + reg read"]
ID --> EX["EX<br/>ALU / branch resolve"]
EX --> MEM["MEM<br/>dmem addr phase,<br/>CSR r/w, traps"]
MEM --> WB["WB<br/>dmem data phase,<br/>regfile commit"]
Supported mechanisms:
- Compressed (C) handling entirely within fetch: 16-bit RVC expansion to full-width instructions, self-correcting length prediction, and straddling-instruction splicing
- Same-cycle result forwarding between stages for dependent instructions
- Load-use / CSR-use / AMO-use hazard detection with a single-cycle consumer stall; the FP register file carries its own structurally identical check
- Memory-system back-pressure handling: bus-arbitration waits, multi-cycle atomics, in-flight page-table walks (data-side and instruction-side)
- Multi-cycle execution units: RV64M divide, RV64F/D divide/square-root, and the serial (one-element-per-cycle) vector engine
- Vector load/store with per-element address translation, mid-instruction fault latching with
vstartresume, and misaligned access splitting across two bus words - Vector load-use: any instruction in ID is stalled for one cycle while a vector load/store sits in EX, so neither a vector consumer of the load’s result nor a scalar consumer racing the store’s write landing can observe stale data
- Branch/jump misprediction resolution in EX with a fixed squash of all younger instructions
- Trap/MRET/SRET/FENCE.I/SFENCE.VMA redirects resolved at the commit point (MEM)
- Synchronous exceptions take priority over pending interrupts sampled at the commit point
Branch Prediction
A trained fetch-address predictor, build-time optional (BPU_ENABLE):
- 64-entry direct-mapped branch target buffer (BTB) remembering last-resolved targets
- 256-entry pattern history table (PHT) with 2-bit saturating counters for conditional-branch direction
- Return address stack (RAS, default 8 entries) giving call-site-specific predictions for shared
retinstructions; push/pop only on resolved outcomes, never speculatively - Mispredictions corrected by the ordinary EX-stage redirect path, in both prediction directions
Spectre/Meltdown resistance. This core is not susceptible to Spectre-v1/v2 or Meltdown-class attacks:
- Every mis-speculated instruction is squashed before it reaches MEM, the only stage that can touch memory/TLB – so a speculative load can never leave an observable cache/TLB footprint (the precondition both Spectre variants rely on)
- All trained predictor state (BTB, PHT, and RAS) is unconditionally erased on every privilege-domain crossing (trap, MRET, SRET, FENCE.I), so no prediction history or call stack can survive across a privilege transition (closes the Spectre-v2 branch-target-injection setup at the source)
- Meltdown requires a permission check deferred past speculative use of a faulting load; this core’s MMU translation and PMP permission checks are combinational and gate the real bus access in the same cycle, so no such path exists
MMU with PMP
The MMU implements Sv39/Sv48/Sv57 virtual memory plus H-extension two-stage (VS + G) translation. Supported mechanisms:
- Variable-depth page-table walker (3/4/5 levels) for instruction and data accesses, including superpage support
- H-extension two-stage translation with separate VS-stage and G-stage walkers/TLBs – see the Tagged TLB section for the shared TLB’s tagging and selective invalidation
- Hardware A/D-bit management implementing both
SvadeandSvadu: a leaf PTE that would otherwise succeed but hasA=0, orD=0on a write/AMO, is either auto-updated in place by the walker (a real atomic read-modify-write under the cross-hart bus grant; a cached TLB entry whose A/D no longer match the current access is evicted and re-walked) whenADUE=1, or reported as a genuine page fault for software fixup whenADUE=0–menvcfg.ADUEgoverns S-mode and G-stage translations,henvcfg.ADUEthe VS stage (spec-forced to 0 whilemenvcfg.ADUE=0); both reset to 1
Physical Memory Protection (PMP), build-time optional (PMP_ENABLE; at 0 the pmpcfg/pmpaddr CSRs raise illegal-instruction and every check passes unconditionally):
- 64 entries restricting S/U-mode physical read/write/exec access, orthogonal to page-table permissions
- TOR/NA4/NAPOT address matching with lowest-index-wins priority; addresses WARL-masked to the 54-bit
address >> 2width of this design’s 56-bit physical address space - Per-entry r/w/x permissions; M-mode bypasses unlocked entries but not locked ones
- No-match default: M-mode is always granted, S/U-mode denied – so merely having PMP present (all entries still
A=OFF) denies every S/U-mode access until firmware configures at least one permissive entry before entering S-mode - Entry locking (config and address read-only until reset), including the locked-TOR lower-bound rule
- Enforcement on every physical bus access: instruction fetch, load, store, and page-table-walker PTE reads; a denial reports the ordinary access-fault cause of the original access
Smepmp (Enhanced PMP) is layered directly on top via three mseccfg bits:
MML(Machine Mode Lockdown): replaces the per-entry permission rule with a fixed 16-row table over{L,R,W,X}– M-mode-only, S/U-mode-only (which removes the base rule’s own M-mode bypass), and shared-region patterns; sticky-set until resetMMWP(Machine-mode Whitelist Policy): flips the M-mode no-match default from granted to denied; sticky-set until resetRLB(Rule Locking Bypass): while set, locked entries’ config/address become writable again; cannot be set once any entry is already locked (stuck at 0 until reset)
Tagged TLB
The MMU’s translation lookaside buffer is tagged so that cached translations remain valid across address-space switches and can be invalidated selectively instead of wholesale. Supported mechanisms:
- 16-entry TLB whose entries are tagged with the active ASID (full 16-bit
satp.ASIDwidth) and the PTE’s global bit - Global mappings hit under every address space; non-global entries only match their own ASID, so a
satpwrite needs no flush – a new address space can never match stale entries installed under another ASID - Selective invalidation via the 4-case
SFENCE.VMA rs1, rs2predicate: whole-TLB, per-ASID, per-address (a superpage entry matches any address within its range), or per-address-and-ASID; global mappings are exempt only when an ASID is named - Svinval:
SINVAL.VMAprovides identical selectivity toSFENCE.VMA; theSFENCE.W.INVAL/SFENCE.INVAL.IRordering fences are architecturally no-ops on this in-order core - H extension: separate VS-stage and G-stage TLBs;
HFENCE.VVMAinvalidates the ASID-tagged VS-stage entries with the same 4-case predicate,HFENCE.GVMAthe G-stage entries – which are tagged byhgatp.VMID(14 bits) instead of an ASID, so a VMID-scoped flush touches only that guest’s cached translations and two guests under different VMIDs can switch without any intervening flush
Privilege Levels and Traps
M/S/U privilege levels with reset into M-mode; MRET restores the level from mstatus.MPP (resetting it to U), SRET from sstatus.SPP. The H extension layers an orthogonal virtualization bit on top of these – see the Hypervisor page.
Supported mechanisms:
- Full machine CSR set (read-only identification CSRs
mvendorid/marchid/mimpid/mhartid/mconfigptr, plusmstatus, self-describingmisa,medeleg/mideleg,mie/mip,mtvecDirect + Vectored,mcounteren,mscratch,mepc/mcause/mtval,mcycle/minstret) and supervisor CSR set (sstatusas a masked view ofmstatus,sie/sip,stvec,scounteren,sscratch,sepc/scause/stval,satpWARL-limited to Bare/Sv39/Sv48/Sv57) - CSRRS/CSRRC implement the spec’s own strict
rs1==x0no-write rule – a real (non-x0) register holding 0 is still a genuine write attempt mstatus.MXR/SUMare real bits consumed by the MMU’s permission check (SUMnever applies to instruction fetch);FSandVSare likewise real, reset to Off (any F/D or vector instruction traps as illegal-instruction until software enables them), and auto-transition to Dirty in hardware on the first state-modifying commit – the vector floating-point forms specifically are gated by bothFSandVSat oncemstatus.MPRV/MPP/MPVare real and consulted by the data-side translation (the full six-row truth table), never affecting instruction fetch or HLV/HSV- Zicfilp forward-edge CFI: a non-exempt
JALRarms an expected-landing-pad state; the next fetched instruction must be a label-matchingLPADor a software-check exception (mcause=18,mtval=2) fires – enabled per privilege viamenvcfg.LPE/henvcfg.LPE/senvcfg.LPE/mseccfg.MLPE, with the state trap-preserved inxPELP - Zicfiss backward-edge CFI: a dedicated shadow stack (
sspCSR) withSSPUSH/SSPOPCHK; a mismatched pop raises the same software-check exception (mtval=3), and the shadow-stack’s own pages use a new PTE encoding that only the SS instructions may write – any other write is a fatal access fault - Ssdbltrp/Smdbltrp double-trap detection: per-level sticky trap-in-flight bits (
mstatus.MDT,sstatus.SDT,vsstatus.SDT); an unexpected trap into a level already in flight redirects one level up asmcause=16with the suppressed cause inmtval2, and an unexpected trap targeting M-mode halts the hart in a permanent critical-error state - Unprivileged counters (
cycle/time/instret) and F/D’sfflags/frm/fcsr; RV32’s high-half shadow CSRs are illegal instructions on this RV64 core - Synchronous exceptions: instruction/load/store misaligned access, illegal instruction, breakpoint, ECALL from U/S/M, and instruction/load/store page faults once paging is active; the H extension adds VS-mode ECALL (cause 10), virtual-instruction (22), and guest-page-fault causes (20/21/23)
- Interrupts: machine/supervisor x {software, timer, external}; MSIP/MTIP/MEIP are hardware-driven from the CLINT/CLINT/PLIC; SEIP becomes a read-only view of the IMSIC S-file’s delivery signal when AIA’s
mvien.SEIEis set (the PLIC’s own dedicated S-mode context is ORed in unconditionally); STIP becomes the realmtime >= stimecmpcomparator once Sstc’smenvcfg.STCEis enabled - Interrupt priority: external > software > timer, machine level before supervisor level; the H extension adds a VS tier below both and AIA adds SGEIP (bit 12) to the same scheme
menvcfg/henvcfg/senvcfg: realSTCE(Sstc),PBMTE(Svpbmt),ADUE(Svade/Svadu),FIOM,CBZE/CBCFE/CBIE(Zicboz/Zicbom), andLPE/SSE/DTE(Zicfilp/Zicfiss/Ssdbltrp) fields –henvcfgmirrors them for VS-stage (ADUE/SSE/DTEspec-forced to 0 whenever theirmenvcfgcounterpart clears), andsenvcfgmirrors the U-mode subsetWFIgenuinely stalls fetch/issue until an individually-enabled-and-pending interrupt is observed – independent of the global enable bits – withmstatus.TW/hstatus.VTWtimeout-wait and the U-mode/VU-mode rules enforced via a bounded hardware timer
Documented simplifications. Not silent scope cuts:
XS/UBEread a constant 0 – unimplemented; nothing in this design needs them yet- Every remaining
menvcfg/henvcfg/senvcfgfield is WPRI and reads 0
Synchronized Timer
A single shared high-resolution timer, synchronized across all harts by construction:
- Exactly one
mtimeregister in the whole design (the CLINT), incrementing every core clock cycle – e.g. 10 ns resolution at a 100 MHz core clock; every hart’stimeCSR (rdtime) observes the literal same value in the same cycle, with no per-core replica to resynchronize and no drift to bound - Per-hart state is only
mtimecmp[hart]andmsip[hart]; the register layout matches the SiFive/QEMU “virt” CLINT, so an unmodified OpenSBI/Linux driver works against it - Sstc: a real per-hart
stimecmpCSR compared against the same sharedmtime, gated by M-mode’smenvcfg.STCE– once enabled, S-mode programs its own timer interrupt directly with no CLINT MMIO access and no M-mode relay; while disabled, S-mode access tostimecmptraps illegal-instruction (the mechanism firmware uses to detect whether the hand-off is safe)