A from-scratch out-of-order (OoO) 32-bit RISC-V core, written in Chisel, simulated
and tested with ChiselSim + ScalaTest. The microarchitecture follows the BOOM
(Berkeley Out-of-Order Machine) family — explicit register renaming, a unified physical
register file, read-after-issue — but is deliberately stripped down to fit a small FPGA and
the RV32I + M ISA. It is the sibling/successor of the in-order pipelined RISC-V project;
the design rationale lives in that repo's OUT-OF-ORDER-DESIGN.md.
Status: first-pass implementation. The core elaborates and is exercised by ChiselSim tests; it is not yet a verified, timing-closed design. See the milestone plan below.
Compute out of order; commit in order. Architectural state — the register file and memory (including the VGA framebuffer and button MMIO) — only changes in program order, at the moment an instruction retires from the reorder buffer.
IN-ORDER FRONT END OUT OF ORDER IN-ORDER COMMIT
Fetch → Decode → Rename → Dispatch IssueQueue → PRF read → ReorderBuffer
(+ branch predict) ExecUnits (ALU/MULDIV/AGU) • free old preg
→ writeback (CDB) → wakeup • commit stores
LSU (LDQ/STQ) • MMIO at head
• redirect/flush
Key design choices (vs. a full BOOM): RV32IM only (no FP); gshare + small BTB + RAS (no TAGE);
one unified integer+memory issue queue with an age-matrix scheduler; deterministic, replay-free
load latency (no cache); commit-time stores with MMIO executed at the ROB head; 2–4 in-flight
branch snapshots. All sizes are parameterized in Params.scala.
build.sbt / project/ Chisel 7.0.0, Scala 2.13.16, ScalaTest 3.2.19
.github/workflows/test.yml CI: Verilator + `sbt test`
convert.py .bin -> .hex (little-endian 32-bit words)
load_program.py Stream a .hex to a board over UART (for a future FPGA bring-up)
src/main/scala/RISCV/
Params.scala OoOParams case class: all structural sizes/widths
Bundles.scala Frozen shared interfaces: MicroOp, DecodedInst, CDB, ROB, redirect
Memory.scala Word-addressed RAM + VGA region + button MMIO
Main.scala SoC top: Core + Memory (+ peripherals)
core/
Core.scala Stitches the whole OoO datapath together
Decoder.scala Combinational field + immediate extractor (RV32I)
frontend/ Fetch, BranchPredictor, Decode, Rename (MapTable/FreeList/BusyTable), Dispatch
execute/ IssueQueue, PhysRegFile, ExecAlu, ExecMulDiv
memory/ Lsu (LDQ/STQ + store-to-load forwarding + MMIO-at-head)
commit/ ReorderBuffer (allocate / retire / flush)
peripheral/ VGAController, UART (optional SoC peripherals)
src/test/scala/RISCV/ ChiselSim + ScalaTest specs (per-module + whole-core)
sbt compile # elaborate
sbt test # run the ChiselSim test suite (needs Verilator + a Unix-like env, e.g. WSL/Linux CI)
sbt "runMain RISCV.Main" # emit SystemVerilog into generated/Status: sbt compile and runMain RISCV.Main (full-SoC elaboration) pass; the ChiselSim suite
(30 tests across Decode/Rename/Frontend, ALU/MULDIV/PRF, ROB/LSU, and a whole-core smoke test)
passes under Verilator. ChiselSim shells out to a Unix which, so run the tests under WSL or
Linux CI, not a native Windows shell.
These are deliberate simplifications for the initial bring-up, not bugs:
- Branch predictor doesn't learn yet. The gshare/BTB/RAS update port isn't routed through
the fixed
FrontendIO, andWbPortcarries no resolvedpc, so the predictor effectively predicts not-taken. Every taken branch mispredicts and recovers correctly at commit — this is a performance gap, not a correctness one. (Fix: add a predictor-update channel +WbPort.pc.) - Recovery is commit-time full-flush, not branch-mask fast recovery. Correct and simple; a mispredicted branch drains to the ROB head before the redirect fires. (Milestone 5.)
- Sub-word stores are word-granular.
SW/LWare exact;SB/SHsplice over a zero base word rather than a true read-modify-write of the single memory port, so they're fully correct only when the untouched lanes are zero or are also written. - VGA-region reads aren't supported by
Memoryport 2 (the framebuffer is CPU-write-only). - One MUL/DIV unit (mul and div serialize) and a single in-flight load (one MSHR).
MainSpecis a smoke test (no architectural assertions) because the core has no commit-trace/debug readout port yet. Self-checking against a golden RV32IM model is milestone 1.
- Self-checking test harness (golden RV32IM reference, tandem-checked at retire).
- Rename + PRF + ROB, single FU, in-order issue (validates the plumbing).
- Issue queue + wakeup → true out-of-order issue.
- LSU (LDQ/STQ) with conservative ordering + store-to-load forwarding + MMIO-at-head.
- Branch prediction + branch-mask fast recovery.
- Optimize: speculative ALU wakeup, DSP multiplier, wider front end.