FPGA & digital Built and simulated
Pipelined RISC-V CPU
A processor that overlaps five instructions at once without letting any of them read a value too early.
- IF/ID
- ID/EX
- EX/MEM
- MEM/WB
What I did
I built a single-cycle RV32I core first, then split it into five pipeline stages and added forwarding, load-use stall detection and branch flushing.
Result
- 3.8× throughput over the single-cycle baseline at near one instruction per cycle.
- Passed 100+ directed and randomised tests in Vivado XSim; timing closed on a RealDigital Blackboard FPGA.
- No full ISA compliance result is claimed.
- The trace below is a teaching model, not a cycle-accurate run of the final RTL.
Design decision
Forward first, stall only when forced
Forwarding hands a fresh result straight to the next instruction. Only a load followed by its use costs a one-cycle bubble, which keeps dependent arithmetic near one instruction per cycle.
Try it
Step the clock through a short program.
sub gets x3 from EX/MEM; and takes x4 from EX/MEM and x3 from MEM/WB.
Showing cycles 1 to 7 of 7.
| Instruction | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| add x3, x1, x2 | IF | ID | EX | MEM | WB | ||
| sub x4, x3, x5 | IF | ID | EX | MEM | WB | ||
| and x6, x3, x4 | IF | ID | EX | MEM | WB |
- Cycle 4: x3 forwarded from EX/MEM into EX for sub x4, x3, x5
- Cycle 5: x3 forwarded from MEM/WB into EX for and x6, x3, x4
- Cycle 5: x4 forwarded from EX/MEM into EX for and x6, x3, x4
3 instructions finish in 7 cycles under this model. "hold" marks a stalled stage.
More detail
Why pipelining needs bookkeeping
A single-cycle core does everything in one long clock. Pipelining shortens the clock by overlapping instructions, which breaks whenever one needs a result still in flight.
Pipeline registers and forwarding
ID/EX carries operands, immediate, register indices, PC and control; EX/MEM carries the result and destination; MEM/WB carries the value to commit.
Forwarding compares EX source registers with EX/MEM and MEM/WB destinations; EX/MEM wins because it is newer.
Other design decisions
Resolve branches in EX
Compare forwarded operands in execute and flush two younger instructions on a taken branch. Instead of: resolving in decode.
Simpler operand access, but every taken branch costs two slots.