Dual-Issue RISC-V CPU
Taking a working 5-stage pipelined RISC-V processor and extending it to issue two instructions per cycle — in Verilog, strictly in-order. The interesting part isn't the second ALU; it's the six rules that decide when issuing two instructions together would silently produce a wrong answer.
Interactive demo
The real test program, cycle by cycle
Cycle 0 — ready.
At a glance
- Language
- Verilog-2005
- Extension
- 727 lines, 2-wide in-order
- Verification
- 7 targeted testbench cases
- Course
- EECE 321 Computer Organization
Two lanes, one gatekeeper
Where the second instruction can and cannot go
Code
The gatekeeper, and the forwarding case it creates
wire dual_issue_ok = !b_reads_a_rd && // B reads what A is about to write
!mem_structural && // both want the memory port
!wb_conflict && // both write the same rd
!branch_in_a && // branch resolves in EX — stay conservative
!branch_in_b &&
!pipeline_hazard; // load-use anywhere in the pair
Six independent reasons, all of which must be false. Any one of them and lane B becomes a bubble.
// Encoding: 2'b11 = forward from A's EX result (intra-pair, A -> B)
// 2'b10 = forward from EX/MEM (one cycle back)
// 2'b01 = forward from MEM/WB (two cycles back)
always @(*) begin
forward_a_b = 2'b00;
// Highest priority: A's result in the SAME EX cycle
if (id_ex_regwrite_a && (id_ex_rd_a != 5'd0) && (id_ex_rd_a == id_ex_rs1_b))
forward_a_b = 2'b11;
else if (ex_mem_regwrite_a && (ex_mem_rd_a == id_ex_rs1_b))
forward_a_b = 2'b10;
else if (mem_wb_regwrite_a && (mem_wb_rd_a == id_ex_rs1_b))
forward_a_b = 2'b01;
end
Two instructions in EX at once creates a dependency distance of zero — a case a single-issue pipeline cannot produce.
Why it never reaches 2×
The honest result
A 2-wide machine has a theoretical CPI of 0.5. This one does not get close, and the report says why rather than hiding it: branches force single issue every time, load-use hazards stall both lanes at once, adjacent instructions are very often RAW-dependent, and a load/store pair collides on the single memory port.
That gap between the theoretical number and the measured one is the lesson of the project — ILP is limited by the program, not just the hardware.
Inside the repo
Structure
- □cpu_debug.vprovided 5-stage baseline — 460 lines
- □tb_cpu_debug.vprovided baseline testbench
- ▸cpu_dual_issue.vthe extension — 727 lines
- ▸tb_cpu_dual_issue.v7 targeted test cases — 281 lines
- □report.mdtechnical report, 412 lines
- □README.mdbuild + GTKWave signal guide
Skills, in context
Where each one actually showed up