Without writing a single line of code, only markdown files, I (and Claude Code) implemented two RISC-V 32I processors, aptly named "RiscVibe": a single-cycle processor, and a 5-stage pipelined processor. From making the Python assembler and custom assembly programs, to creating a test suite and an interactive processor visualizer.
To demonstrate, here is the 5-stage RiscVibe processor calculating fibonacci numbers displayed using the trace visualizer.

Processors:
- Single-stage (
riscvibe_1stage/) - All operations complete in one clock cycle, combinational instruction memory, no pipeline hazards - 5-stage pipeline (
riscvibe_5stage/) - Classic IF/ID/EX/MEM/WB pipeline with data forwarding and hazard detection
See project-docs/PROCESSORS.md for detailed comparison and usage guide.
- 5-stage pipeline: Instruction Fetch (IF), Instruction Decode (ID), Execute (EX), Memory Access (MEM), Write Back (WB)
- Full RV32I support: All 37 base integer instructions including arithmetic, logic, branches, jumps, loads, and stores
- Data hazard handling:
- EX-to-EX forwarding (1-cycle distance)
- MEM-to-EX forwarding (2-cycle distance)
- WB-to-ID bypass for register reads
- Load-use stall detection with automatic pipeline bubble insertion
- Control hazard handling: Branch/jump detection with pipeline flush (2-cycle penalty)
- Memory system:
- 4KB instruction memory (ROM, word-addressed)
- 4KB data memory (byte-addressable, little-endian)
- Byte, halfword, and word load/store with sign/zero extension
- Two-pass assembler written in Python
- Full RV32I instruction support
- Pseudo-instruction expansion (li, mv, j, ret, call, etc.)
- Label resolution for branches and jumps
- ABI register name support (ra, sp, a0-a7, t0-t6, s0-s11, etc.)
- Outputs Verilog-compatible hex files
- Automated regression test runner
- 12 functional test programs covering ALU, loops, memory, and all hazard scenarios
- Per-test register value validation
- VCD waveform generation for debugging
- Interactive web-based visualization of pipeline execution
- Cycle-by-cycle stepping with playback controls
- Real-time display of all 5 pipeline stages with PC and disassembled instructions
- Program view panel with FDXMW stage indicators showing instruction flow through pipeline
- Register file contents with change highlighting and hex/decimal toggle
- Hazard and forwarding signal visualization
- JSON Lines trace format for external tool integration
RiscVibe/
├── riscvibe_1stage/ # Single-cycle processor
│ ├── rtl/ # SystemVerilog RTL modules
│ ├── tb/ # Testbench
│ ├── sim/traces/ # Simulation trace outputs
│ ├── architecture.yaml # Visualizer architecture config
│ └── Makefile # Build system
├── riscvibe_5stage/ # 5-stage pipelined processor
│ ├── rtl/ # SystemVerilog RTL modules
│ │ ├── riscvibe_5stage_top.sv # Top module
│ │ ├── *_stage.sv # Pipeline stages (IF/ID/EX/MEM/WB)
│ │ ├── hazard_unit.sv # Hazard detection
│ │ ├── forwarding_unit.sv # Data forwarding
│ │ └── ... # ALU, control, memory modules
│ ├── tb/ # Testbench
│ ├── sim/traces/ # Simulation trace outputs
│ ├── architecture.yaml # Visualizer architecture config
│ └── Makefile # Build system
├── programs/ # Test programs (.S and .hex)
├── riscvibe_asm/ # Python assembler
├── sim/visualizer/ # Pipeline visualizer web app
│ ├── app.py # Flask backend server
│ ├── trace_parser.py # JSONL trace file parser
│ └── templates/static/ # HTML/CSS/JavaScript
├── project-docs/ # Design documentation
├── run_visualizer.sh # Visualizer launch script
└── regression_pipeline.py # Automated test runner
- Icarus Verilog (
iverilog,vvp) - Open-source Verilog/SystemVerilog simulator - Python 3.8+ - For the assembler and test runner
- GTKWave - Waveform viewer for debugging
macOS (Homebrew):
brew install icarus-verilog gtkwave python3Ubuntu/Debian:
sudo apt install iverilog gtkwave python3Arch Linux:
sudo pacman -S iverilog gtkwave pythonChoose a processor: cd riscvibe_1stage (single-cycle) or cd riscvibe_5stage (pipelined)
cd riscvibe_5stage
makecd riscvibe_5stage
make PROGRAM=test_fibcd riscvibe_5stage
make trace PROGRAM=test_fibcd riscvibe_5stage
make waveBoth processors support these Makefile targets:
| Target | Description |
|---|---|
make or make all |
Compile and simulate (default: test_alu) |
make compile |
Compile only |
make sim |
Run simulation only |
make trace |
Compile and run with trace logging for visualizer |
make wave |
Open waveforms in GTKWave |
make clean |
Remove generated files |
PROGRAM- Test program name without .hex (default:test_alu)MAX_CYCLES- Maximum simulation cycles (default:10000)
| Test | Description |
|---|---|
test_alu |
All ALU operations: add, sub, shifts, comparisons, logical |
test_fib |
Fibonacci sequence with loops and branches |
test_bubblesort |
Bubble sort algorithm with memory operations |
| Test | Description |
|---|---|
test_hazard_ex_ex |
EX-to-EX data forwarding |
test_hazard_mem_ex |
MEM-to-EX data forwarding |
test_hazard_load_use |
Load-use stall insertion |
test_hazard_branch |
Branch flush verification |
test_hazard_jal |
JAL instruction hazards |
test_hazard_jalr |
JALR instruction hazards |
test_hazard_x0 |
x0 register hardwiring |
test_hazard_chain |
Chained data dependencies |
test_hazard_comprehensive |
Combined hazard scenarios |
# Run all tests
./regression_pipeline.py
# Verbose output
./regression_pipeline.py -v
# Run specific test
./regression_pipeline.py --test test_fib
# List available tests
./regression_pipeline.py --listResults are saved to sim/regression_report.txt with detailed logs in sim/logs/.
Create a .S file in the programs/ directory. Example:
# my_program.S - Simple example
addi x1, x0, 10 # x1 = 10
addi x2, x0, 20 # x2 = 20
add x3, x1, x2 # x3 = x1 + x2 = 30
# Store result to memory
sw x3, 0(x0) # mem[0] = 30
# End program (required)
ecallImportant: All programs must end with ecall or ebreak to terminate simulation.
R-Type (register-register): add, sub, sll, slt, sltu, xor, srl, sra, or, and
I-Type (immediate): addi, slti, sltiu, xori, ori, andi, slli, srli, srai
Load: lb, lh, lw, lbu, lhu
Store: sb, sh, sw
Branch: beq, bne, blt, bge, bltu, bgeu
Jump: jal, jalr
Upper immediate: lui, auipc
System: ecall, ebreak
Pseudo-instructions: nop, li, mv, not, neg, j, jr, ret, call, beqz, bnez, blez, bgez, bltz, bgtz
python3 -m riscvibe_asm programs/my_program.S -o programs/my_program.hexAdd -v for verbose output showing each instruction encoded.
make TESTPROG=programs/my_program.hexOr step by step:
make compile TESTPROG=programs/my_program.hex
make simAfter simulation, view internal signals with GTKWave:
make waveThe VCD file is saved to sim/riscvibe_5stage.vcd.
The pipeline visualizer provides an interactive web-based view of pipeline execution:
-
Generate a trace file:
cd riscvibe_5stage make trace PROGRAM=test_fibThis creates
sim/traces/test_fib_trace.jsonlcontaining cycle-by-cycle processor state. -
Start the visualizer:
cd sim/visualizer python app.py -
Open in browser: Navigate to
http://localhost:5050 -
Load and explore:
- Click "Load Architecture" and select the architecture file (
riscvibe_1stage/architecture.yamlorriscvibe_5stage/architecture.yaml) matching your trace - Click "Load Trace" to load your trace file (e.g.,
riscvibe_5stage/sim/traces/test_fib_trace.jsonl) - Use playback controls or keyboard shortcuts:
Space- Play/Pause←/→- Step backward/forwardHome/End- Jump to start/end
- View pipeline stages with PC and disassembled instructions (e.g.,
addi x1, x0, 10) - Toggle register display between hex and decimal formats
- Monitor hazard and forwarding signals in real-time
- Click "Load Architecture" and select the architecture file (
To run C programs, you'll need a RISC-V cross-compiler toolchain:
-
Install the toolchain:
# macOS brew tap riscv-software-src/riscv brew install riscv-tools # Ubuntu sudo apt install gcc-riscv64-unknown-elf
-
Write your C program:
// my_program.c int main() { int a = 10; int b = 20; int c = a + b; return c; }
-
Compile and convert to hex:
# Compile to object file riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -T linker.ld my_program.c -o my_program.elf # Extract binary riscv64-unknown-elf-objcopy -O binary my_program.elf my_program.bin # Convert to hex (you'll need a bin2hex script) xxd -p -c 4 my_program.bin | awk '{print $1}' > my_program.hex
Note: You'll need a minimal linker script (
linker.ld) and startup code for proper execution. For simple programs, writing directly in assembly is recommended.
| Region | Address Range | Size | Description |
|---|---|---|---|
| Instruction Memory | 0x0000 - 0x0FFF |
4 KB | Read-only, word-aligned |
| Data Memory | 0x0000 - 0x0FFF |
4 KB | Read/write, byte-addressable |
Note: Instruction and data memory are separate (Harvard architecture).
RiscVibe implements the complete RV32I base integer instruction set (37 instructions):
| Format | Instructions | Count |
|---|---|---|
| R-type | ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND | 10 |
| I-type | ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI | 9 |
| Load | LB, LH, LW, LBU, LHU | 5 |
| Store | SB, SH, SW | 3 |
| Branch | BEQ, BNE, BLT, BGE, BLTU, BGEU | 6 |
| Jump | JAL, JALR | 2 |
| U-type | LUI, AUIPC | 2 |
- Branch prediction: Add a simple branch predictor (BTB or bimodal) to reduce branch penalty from 2 cycles to ~1 cycle on average
- Memory-mapped I/O: Add UART or other peripherals for external communication
- Interrupt support: Implement basic interrupt handling with CSR registers
- Performance counters: Add cycle counter, instruction counter, and other CSRs
- M extension: Integer multiplication and division (MUL, DIV, REM)
- C extension: Compressed 16-bit instructions for improved code density
- Zicsr extension: Control and Status Register instructions
- F extension: Single-precision floating-point
- FPGA synthesis: Provide constraints and scripts for common FPGA boards (Arty A7, DE10-Nano)
- Formal verification: Add formal property checking with SymbiYosys
- Continuous integration: GitHub Actions for automated regression on PRs
- Code coverage: Add simulation coverage metrics
- Verilator support: Add Verilator compilation for faster simulation
- Cache hierarchy: Instruction and data caches with configurable size/associativity
- MMU/virtual memory: Page tables and address translation for OS support
- Multi-core: Extend to dual-core with cache coherency
- Out-of-order execution: Superscalar pipeline with register renaming
Additional documentation is available in project-docs/:
- PROCESSORS.md - Processor implementations guide (single-stage vs 5-stage)
- PIPELINE.md - Pipeline architecture overview
- pipeline-impl.md - Detailed implementation specification
- hazards_tb_impl.md - Hazard testing documentation
- simulator_impl.md - Pipeline visualizer implementation
- assembler.md - Assembler design and usage
This project is open source. See individual files for license information.