Skip to content

About

A 32-bit RV32I RISC-V CPU built from scratch in SystemVerilog, featuring a 5-stage pipeline, forwarding, hazard detection, and a simulation-driven verification suite with directed, constrained-random, assertion, and functional coverage testing.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

 

History

39 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

📌 Note: This project is the successor to my earlier CPU work. From hand-built 8-bit CPU in Verilog, followed by a Python simulator for a custom 24-bit RISC pipeline. This is the full version: a real RV32I core, written in SystemVerilog, with a proper simulation-based verification suite behind it.


🧠 32-bit RISC-V CPU (SystemVerilog)

🚀 Overview

A RV32I CPU core built from scratch in SystemVerilog — starting from a single-cycle reference implementation, then rebuilt as a 5-stage pipeline with forwarding and hazard detection. Every stage of the design is backed by simulation: 199 directed test cases across 12 testbenches, 100% hand-built functional coverage, 300 constrained-random programs checked against a reference model, and SystemVerilog assertions embedded directly in the RTL.


💡 Key Features

  • 🧱 RV32I base ISA — full instruction set, decoded from raw instruction bits
  • ⚙️ 5-Stage Pipeline (IF → ID → EX → MEM → WB), plus a single-cycle reference core kept side-by-side as a correctness oracle
  • 🔁 Three forwarding paths (EX/MEM→EX, MEM/WB→EX, MEM/WB→ID) resolving RAW hazards without stalling, plus a single-cycle stall for the one case forwarding can't close (load-use)
  • 🧭 EX-stage branch/jump resolution, chosen after measuring it beats ID-stage resolution on loop-heavy code
  • ✅ SystemVerilog assertions embedded directly in the RTL — x0 invariants, pipeline bubble discipline, PC alignment, branch/jump redirect priority
  • 📊 Hand-rolled functional coverage model — 39 tracked bins (every opcode, every ALU op, both forwarding paths, all branch outcomes, hazard stalls, x0 edge cases), 100% hit
  • 🎲 Constrained-random instruction testing — 300 randomly generated programs, each checked bit-for-bit against a single-cycle reference model
  • 🎥 Live terminal demo — watch the core compute Fibonacci numbers in real time

🧠 Architecture

flowchart LR
    IF["IF<br/>Fetch"] --> ID["ID<br/>Decode +<br/>Register Read"]
    ID --> EX["EX<br/>ALU /<br/>Branch Resolve"]
    EX --> MEM["MEM<br/>Data Memory"]
    MEM --> WB["WB<br/>Writeback"]

    MEM -. "EX/MEM → EX forward" .-> EX
    WB -. "MEM/WB → EX forward" .-> EX
    WB -. "MEM/WB → ID forward" .-> ID
Loading

A taken branch or jump resolves in EX, flushing the 2 instructions fetched behind it. Load-use hazards (the one case forwarding can't absorb) cost a single stall cycle in ID. Everything else — back-to-back dependent ALU ops, values needed one or two instructions later — is resolved by forwarding with zero stall cost.


📂 Project Structure

Path Contents
rtl/ Synthesizable SystemVerilog source (core, ALU, regfile, control unit, decoder, pipeline registers, hazard/forwarding units)
verification/ Testbenches — directed, coverage, and constrained-random
sim/ PowerShell scripts to build and run each testbench
docs/ Design notes, decisions log, screenshots

🧪 Verification & Testing

Test Suite What It Verifies Cases Script
ALU Every ALU operation (ADD, SUB, AND, OR, XOR, SLL, SRL, SRA, SLT, SLTU) 20 run_alu.ps1
Register File x0 hard-wiring, read/write timing 10 run_regfile.ps1
Control Unit Opcode → control signal decoding 34 run_control_unit.ps1
Instruction Decoder Raw instruction field extraction 11 run_decoder.ps1
Single-Cycle Core End-to-end single-cycle datapath 16 run_riscv_core_singlecycle.ps1
Hazard Unit Load-use stall detection 8 run_hazard_unit.ps1
Forwarding Unit EX/MEM and MEM/WB bypass selection 13 run_forwarding_unit.ps1
Pipelined Core Full 5-stage pipeline integration 17 run_riscv_core_pipelined.ps1
Full-Program Integration 63-instruction program, dual-core equivalence 29 run_full_program.ps1
CPI Sanity Check Forwarding keeps CPI near 1.0 2 run_cpi_sanity.ps1
Compliance-Style Edge Cases Classic RV32I corner cases (overflow, shift masking, signed/unsigned boundaries) 21 run_compliance.ps1
Fibonacci Smoke Test Real program correctness, fib(0)–fib(14) 18 run_fibonacci.ps1
Total directed cases 199

Beyond directed testing:

  • Functional coverage (run_coverage.ps1) — 39/39 bins hit (100%), found and closed two real gaps the directed suite alone had missed.
  • Constrained-random testing (run_random.ps1) — 300 randomly generated instruction streams, each verified against a single-cycle reference model. 300/300 passed.

See docs/design-notes.md for the full design and verification decisions log.


🎥 Live Fibonacci Demo

cd sim
.\run_fibonacci_demo.ps1

Runs the Fibonacci program on the pipelined core and narrates each term as it's computed, replayed at a human-watchable pace on a loop until you press Ctrl+C — a live look at real code executing on the core rather than a pass/fail log.

Fibonacci_Demo.mp4


📸 Results Showcase

Full 5-stage pipeline — hazard & forwarding integration tests

Pipelined core test results

63-instruction program run on both cores — dual-core equivalence + measured cycle counts

Full program integration test results

Compliance-style edge case testing — classic RV32I corner cases

Compliance-style test results

Functional coverage report — 100% across 39 tracked bins

Functional coverage report

Constrained-random testing — 300 randomized programs vs. reference model

Constrained-random test results


🛠️ Getting Started

Prerequisites

Icarus Verilog (iverilog, vvp).

  • macOS: brew install icarus-verilog
  • Ubuntu/Debian: sudo apt install iverilog
  • Windows: WSL + apt, or the prebuilt installer from the Icarus site

Running the tests (PowerShell)

cd sim
.\run_alu.ps1
.\run_regfile.ps1
.\run_control_unit.ps1
.\run_decoder.ps1
.\run_riscv_core_singlecycle.ps1
.\run_hazard_unit.ps1
.\run_forwarding_unit.ps1
.\run_riscv_core_pipelined.ps1
.\run_full_program.ps1
.\run_cpi_sanity.ps1
.\run_compliance.ps1
.\run_fibonacci.ps1
.\run_coverage.ps1
.\run_random.ps1

Each directed suite ends in ALL TESTS PASSED; run_coverage.ps1 ends in FULL FUNCTIONAL COVERAGE; run_random.ps1 reports a pass/fail/ inconclusive count across its randomized runs.

(run_alu.sh also exists for bash/WSL/Git Bash use; the newer modules only have .ps1 runners since PowerShell is the primary shell in use.)


Conclusion

This project carries the same goal as the 8-bit CPU and the 24-bit Python simulator before it — understanding a processor by building one — but taken all the way to a real, synthesizable RTL implementation with an actual verification methodology behind it: directed testing, assertions, functional coverage, and constrained-random testing against a reference model. See docs/design-notes.md for the complete design and verification decisions log.

About

A 32-bit RV32I RISC-V CPU built from scratch in SystemVerilog, featuring a 5-stage pipeline, forwarding, hazard detection, and a simulation-driven verification suite with directed, constrained-random, assertion, and functional coverage testing.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages