Series: RISC V Hardware
verilog
244 lines
· Updated 2026-07-04
riscv_pipeline.v
RISC_V_Hardware/verilog/rtl/riscv_pipeline.v
// ============================================================
// riscv_pipeline.v -- 5-stage pipelined RV32I CPU core
// IF -> ID -> EX -> MEM -> WB
// Features:
// * full forwarding (EX/MEM and MEM/WB -> EX)
// * load-use hazard detection (1-cycle stall)
// * branches/jumps resolved in EX, 2-cycle flush on taken
// Reuses: alu, regfile, imm_gen, control, branch_unit
// ============================================================
`timescale 1ns/1ps
module riscv_pipeline (
input wire clk,
input wire rst_n,
// instruction memory port (combinational ROM)
output wire [31:0] imem_addr,
input wire [31:0] imem_data,
// data memory port
output wire dmem_read,
output wire dmem_write,
output wire [31:0] dmem_addr,
output wire [2:0] dmem_funct3,
output wire [31:0] dmem_wdata,
input wire [31:0] dmem_rdata,
// debug
output wire [31:0] dbg_pc
);
// ================= IF stage =================
reg [31:0] pc;
wire [31:0] pc_plus4 = pc + 32'd4;
wire stall; // freeze PC and IF/ID (load-use)
wire redirect; // EX resolved taken branch / jump
wire [31:0] redirect_pc;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) pc <= 32'b0;
else if (redirect) pc <= redirect_pc;
else if (!stall) pc <= pc_plus4;
end
assign imem_addr = pc;
assign dbg_pc = pc;
// ---------------- IF/ID register ----------------
reg [31:0] ifid_pc, ifid_instr;
reg ifid_valid;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ifid_pc <= 32'b0; ifid_instr <= 32'h00000013; ifid_valid <= 1'b0;
end else if (redirect) begin // flush
ifid_instr <= 32'h00000013; ifid_valid <= 1'b0;
end else if (!stall) begin
ifid_pc <= pc; ifid_instr <= imem_data; ifid_valid <= 1'b1;
end
// stall: hold
end
// ================= ID stage =================
wire [6:0] id_opcode = ifid_instr[6:0];
wire [4:0] id_rd = ifid_instr[11:7];
wire [2:0] id_funct3 = ifid_instr[14:12];
wire [4:0] id_rs1 = ifid_instr[19:15];
wire [4:0] id_rs2 = ifid_instr[24:20];
wire id_reg_write, id_alu_src, id_mem_read, id_mem_write;
wire id_branch, id_jump, id_jalr, id_alu_a_pc;
wire [1:0] id_wb_sel;
wire [3:0] id_alu_op;
control u_control (
.opcode(id_opcode), .funct3(id_funct3), .funct7b5(ifid_instr[30]),
.reg_write(id_reg_write), .alu_src(id_alu_src), .wb_sel(id_wb_sel),
.mem_read(id_mem_read), .mem_write(id_mem_write),
.branch(id_branch), .jump(id_jump), .jalr(id_jalr),
.alu_a_pc(id_alu_a_pc), .alu_op(id_alu_op)
);
wire [31:0] id_rs1_data, id_rs2_data;
wire [4:0] wb_rd;
wire wb_reg_write;
wire [31:0] wb_data;
regfile #(.BYPASS(1)) u_regfile (
.clk(clk), .we(wb_reg_write),
.ra1(id_rs1), .ra2(id_rs2), .wa(wb_rd), .wd(wb_data),
.rd1(id_rs1_data), .rd2(id_rs2_data)
);
wire [31:0] id_imm;
imm_gen u_immgen (.instr(ifid_instr), .imm(id_imm));
// ---- load-use hazard detection ----
// instruction in EX is a load whose rd matches ID's rs1/rs2
reg idex_mem_read;
reg [4:0] idex_rd;
wire uses_rs1 = (id_opcode != 7'b0110111) && (id_opcode != 7'b1101111); // not LUI/JAL
wire uses_rs2 = (id_opcode == 7'b0110011) || (id_opcode == 7'b0100011) ||
(id_opcode == 7'b1100011); // R-type, store, branch
assign stall = idex_mem_read && (idex_rd != 5'd0) && ifid_valid &&
((uses_rs1 && (idex_rd == id_rs1)) ||
(uses_rs2 && (idex_rd == id_rs2)));
// ---------------- ID/EX register ----------------
reg [31:0] idex_pc, idex_rs1_data, idex_rs2_data, idex_imm;
reg [4:0] idex_rs1, idex_rs2;
reg [2:0] idex_funct3;
reg idex_reg_write, idex_alu_src, idex_mem_write;
reg idex_branch, idex_jump, idex_jalr, idex_alu_a_pc;
reg [1:0] idex_wb_sel;
reg [3:0] idex_alu_op;
wire id_bubble = stall | redirect | ~ifid_valid;
always @(posedge clk or negedge rst_n) begin
if (!rst_n || id_bubble) begin
idex_reg_write <= 1'b0; idex_mem_read <= 1'b0; idex_mem_write <= 1'b0;
idex_branch <= 1'b0; idex_jump <= 1'b0; idex_jalr <= 1'b0;
idex_alu_a_pc <= 1'b0; idex_alu_src <= 1'b0;
idex_wb_sel <= 2'b00; idex_alu_op <= 4'b0;
idex_pc <= 32'b0; idex_imm <= 32'b0;
idex_rs1_data <= 32'b0; idex_rs2_data <= 32'b0;
idex_rs1 <= 5'b0; idex_rs2 <= 5'b0; idex_rd <= 5'b0;
idex_funct3 <= 3'b0;
end else begin
idex_pc <= ifid_pc;
idex_rs1_data <= id_rs1_data; idex_rs2_data <= id_rs2_data;
idex_imm <= id_imm;
idex_rs1 <= id_rs1; idex_rs2 <= id_rs2; idex_rd <= id_rd;
idex_funct3 <= id_funct3;
idex_reg_write <= id_reg_write; idex_alu_src <= id_alu_src;
idex_mem_read <= id_mem_read; idex_mem_write <= id_mem_write;
idex_branch <= id_branch; idex_jump <= id_jump; idex_jalr <= id_jalr;
idex_alu_a_pc <= id_alu_a_pc;
idex_wb_sel <= id_wb_sel; idex_alu_op <= id_alu_op;
end
end
// ================= EX stage =================
// ---- forwarding muxes ----
reg [31:0] exmem_alu_y, exmem_imm, exmem_pc_plus4;
reg [4:0] exmem_rd;
reg exmem_reg_write;
reg [1:0] exmem_wb_sel;
// value available at end of MEM for a non-load producer
wire [31:0] exmem_fwd_val = (exmem_wb_sel == 2'b10) ? exmem_pc_plus4 :
(exmem_wb_sel == 2'b11) ? exmem_imm :
exmem_alu_y;
wire fwd1_exmem = exmem_reg_write && (exmem_rd != 5'd0) && (exmem_rd == idex_rs1);
wire fwd2_exmem = exmem_reg_write && (exmem_rd != 5'd0) && (exmem_rd == idex_rs2);
wire fwd1_memwb = wb_reg_write && (wb_rd != 5'd0) && (wb_rd == idex_rs1);
wire fwd2_memwb = wb_reg_write && (wb_rd != 5'd0) && (wb_rd == idex_rs2);
wire [31:0] ex_rs1_fwd = fwd1_exmem ? exmem_fwd_val :
fwd1_memwb ? wb_data :
idex_rs1_data;
wire [31:0] ex_rs2_fwd = fwd2_exmem ? exmem_fwd_val :
fwd2_memwb ? wb_data :
idex_rs2_data;
wire [31:0] ex_alu_a = idex_alu_a_pc ? idex_pc : ex_rs1_fwd;
wire [31:0] ex_alu_b = idex_alu_src ? idex_imm : ex_rs2_fwd;
wire [31:0] ex_alu_y;
wire ex_alu_zero;
alu u_alu (.a(ex_alu_a), .b(ex_alu_b), .alu_op(idex_alu_op),
.y(ex_alu_y), .zero(ex_alu_zero));
wire ex_br_taken;
branch_unit u_branch (.rs1(ex_rs1_fwd), .rs2(ex_rs2_fwd),
.funct3(idex_funct3), .taken(ex_br_taken));
wire ex_take_branch = idex_branch & ex_br_taken;
wire [31:0] ex_branch_target = idex_pc + idex_imm;
wire [31:0] ex_jalr_target = (ex_rs1_fwd + idex_imm) & ~32'b1;
assign redirect = idex_jump | idex_jalr | ex_take_branch;
assign redirect_pc = idex_jalr ? ex_jalr_target : ex_branch_target;
// ---------------- EX/MEM register ----------------
reg [31:0] exmem_rs2_data;
reg [2:0] exmem_funct3;
reg exmem_mem_read, exmem_mem_write;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
exmem_reg_write <= 1'b0; exmem_mem_read <= 1'b0; exmem_mem_write <= 1'b0;
exmem_wb_sel <= 2'b00; exmem_rd <= 5'b0;
exmem_alu_y <= 32'b0; exmem_rs2_data <= 32'b0;
exmem_imm <= 32'b0; exmem_pc_plus4 <= 32'b0; exmem_funct3 <= 3'b0;
end else begin
exmem_alu_y <= ex_alu_y;
exmem_rs2_data <= ex_rs2_fwd;
exmem_imm <= idex_imm;
exmem_pc_plus4 <= idex_pc + 32'd4;
exmem_rd <= idex_rd;
exmem_funct3 <= idex_funct3;
exmem_reg_write <= idex_reg_write;
exmem_mem_read <= idex_mem_read;
exmem_mem_write <= idex_mem_write;
exmem_wb_sel <= idex_wb_sel;
end
end
// ================= MEM stage =================
assign dmem_read = exmem_mem_read;
assign dmem_write = exmem_mem_write;
assign dmem_addr = exmem_alu_y;
assign dmem_funct3 = exmem_funct3;
assign dmem_wdata = exmem_rs2_data;
// ---------------- MEM/WB register ----------------
reg [31:0] memwb_alu_y, memwb_mem_rdata, memwb_imm, memwb_pc_plus4;
reg [4:0] memwb_rd;
reg memwb_reg_write;
reg [1:0] memwb_wb_sel;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
memwb_reg_write <= 1'b0; memwb_wb_sel <= 2'b00; memwb_rd <= 5'b0;
memwb_alu_y <= 32'b0; memwb_mem_rdata <= 32'b0;
memwb_imm <= 32'b0; memwb_pc_plus4 <= 32'b0;
end else begin
memwb_alu_y <= exmem_alu_y;
memwb_mem_rdata <= dmem_rdata;
memwb_imm <= exmem_imm;
memwb_pc_plus4 <= exmem_pc_plus4;
memwb_rd <= exmem_rd;
memwb_reg_write <= exmem_reg_write;
memwb_wb_sel <= exmem_wb_sel;
end
end
// ================= WB stage =================
assign wb_data = (memwb_wb_sel == 2'b01) ? memwb_mem_rdata :
(memwb_wb_sel == 2'b10) ? memwb_pc_plus4 :
(memwb_wb_sel == 2'b11) ? memwb_imm :
memwb_alu_y;
assign wb_rd = memwb_rd;
assign wb_reg_write = memwb_reg_write;
endmodule
Related articles
RISC V Hardware
bash
Updated 2026-07-04
build.sh
build.sh — bash source code from the RISC V Hardware learning materials (RISC_V_Hardware/build.sh).
Read article →
RISC V Hardware
latex
Updated 2026-07-04
style.tex
style.tex — latex source code from the RISC V Hardware learning materials (RISC_V_Hardware/latex/common/style.tex).
Read article →
RISC V Hardware
verilog
Updated 2026-07-04
alu.v
alu.v — verilog source code from the RISC V Hardware learning materials (RISC_V_Hardware/verilog/rtl/alu.v).
Read article →
RISC V Hardware
verilog
Updated 2026-07-04
branch_unit.v
branch_unit.v — verilog source code from the RISC V Hardware learning materials (RISC_V_Hardware/verilog/rtl/branch_unit.v).
Read article →
RISC V Hardware
verilog
Updated 2026-07-04
control.v
control.v — verilog source code from the RISC V Hardware learning materials (RISC_V_Hardware/verilog/rtl/control.v).
Read article →
RISC V Hardware
verilog
Updated 2026-07-04
dmem.v
dmem.v — verilog source code from the RISC V Hardware learning materials (RISC_V_Hardware/verilog/rtl/dmem.v).
Read article →