commit a27f857278de6f860ba720e069848f206e54bd43
parent 56b8baf09f4372bae891e08a3a93fff1741fe48c
Author: Vineet Kumar <git@vineetk.net>
Date: Tue, 14 Jul 2026 13:26:21 +0530
properly latch the pipeline registers
Diffstat:
4 files changed, 67 insertions(+), 48 deletions(-)
diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv
@@ -35,26 +35,28 @@ module rv32g_hart
exmem_reg_t exmem_reg;
memwb_reg_t memwb_reg;
+ // temporary fetch signals
+ logic [31:0] fetch_inst;
+ reg_t pc_reg;
+
+ // temporary decode signals
+ ctrl_t dec_ctrl;
+ reg_t dec_pc, dec_imm, dec_rs1_val, dec_rs2_val;
+ logic [4:0] dec_rs1_num, dec_rs2_num, dec_rd_num;
+ logic [11:0] dec_csr_addr;
+
+ // temporary execute signals
+ reg_t ex_alu_out, ex_csr_out;
+ logic ex_branch_taken;
+
+ // temporary memory signals
+ logic [31:0] mem_mem_out;
+
// hazards
logic if_stall;
- logic csr_read_stall;
- logic csr_read_stall_active;
assign if_stall = !imem_valid;
- // stall one cycle when csr_rden is active due to registered read
- always_ff @(posedge clk or negedge rstN) begin
- if (!rstN) begin
- csr_read_stall_active <= 1'b0;
- end else begin
- if (idex_reg.ctrl.csr_rden && !csr_read_stall_active)
- csr_read_stall_active <= 1'b1;
- else if (csr_read_stall_active)
- csr_read_stall_active <= 1'b0;
- end
- end
- assign csr_read_stall = csr_read_stall_active;
-
// TODO move in between decode and execute for better organization?
csr_regfile u_csr(.clk, .rstN, .addr(idex_reg.csr_addr), .rs1_num(idex_reg.rs1_num),
.mepc_shadow(csr_mepc), .illegal(csr_illegal),
@@ -64,10 +66,6 @@ module rv32g_hart
// =============
// === FETCH ===
// =============
-
- // temporary signals
- logic [31:0] fetch_inst;
- reg_t pc_reg;
assign pc_out = pc_reg;
@@ -86,7 +84,7 @@ module rv32g_hart
if (!rstN) begin
pc_reg <= BASE_ADDR;
ifid_reg <= '0;
- end else if (!if_stall && !csr_read_stall) begin
+ end else if (!if_stall) begin
pc_reg <= next_pc;
ifid_reg <= ifid_next;
end
@@ -96,12 +94,6 @@ module rv32g_hart
// === DECODE ===
// ==============
- // temporary signals
- ctrl_t dec_ctrl;
- reg_t dec_pc, dec_imm, dec_rs1_val, dec_rs2_val;
- logic [4:0] dec_rs1_num, dec_rs2_num, dec_rd_num;
- logic [11:0] dec_csr_addr;
-
stage_decode u_decode(.clk, .rstN, .regfile, .inst(ifid_reg.inst),
.pc_in(ifid_reg.pc), .pc_out(dec_pc),
.ctrl(dec_ctrl), .imm(dec_imm),
@@ -115,9 +107,11 @@ module rv32g_hart
if (!rstN) begin
// NOP by default
idex_reg <= '0;
- end else if (if_stall || csr_read_stall) begin
+ idex_reg.pc <= pc_reg;
+ end else if (if_stall) begin
// insert NOP
idex_reg <= '0;
+ idex_reg.pc <= pc_reg;
end else begin
// latch decode outputs in normal operation
idex_reg.ctrl <= dec_ctrl;
@@ -129,6 +123,7 @@ module rv32g_hart
idex_reg.rs2_num <= dec_rs2_num;
idex_reg.rd_num <= dec_rd_num;
idex_reg.csr_addr <= dec_csr_addr;
+ idex_reg.csr_rdata <= csr_rdata;
end
end
@@ -163,18 +158,33 @@ module rv32g_hart
.csr_wren(idex_reg.ctrl.csr_wren),
.csr_immen(idex_reg.ctrl.csr_immen),
.csr_imm(idex_reg.ctrl.csr_imm),
- .csr_rdata(csr_rdata),
+ .csr_rdata(idex_reg.csr_rdata),
.is_mret(idex_reg.ctrl.is_mret),
- .out(exmem_reg.alu_out),
- .branch_taken(exmem_reg.branch_taken),
- .csr_out(exmem_reg.csr_out));
+ .out(ex_alu_out),
+ .branch_taken(ex_branch_taken),
+ .csr_out(ex_csr_out));
// EX/MEM pipeline register
always_ff @(posedge clk or negedge rstN) begin
- if (!rstN) exmem_reg = '0;
- else begin
- $display("EX: op: %h, op1src: %d, op2src: %d, branchop: %d, out: %h, branch_taken: %d",
+ if (!rstN) begin
+ exmem_reg = '0;
+ exmem_reg.pc = idex_reg.pc;
+ end else begin
+ $display("EX: op: %h, op1src: %d, op2src: %d, branchop: %d, out: %h, branch_taken: %d, rs1_val: %h, rs2_val: %h",
idex_reg.ctrl.aluop, idex_reg.ctrl.aluop1src, idex_reg.ctrl.aluop2src,
- idex_reg.ctrl.branchop, exmem_reg.alu_out, exmem_reg.branch_taken);
+ idex_reg.ctrl.branchop, exmem_reg.alu_out, exmem_reg.branch_taken,
+ idex_reg.rs1_val, idex_reg.rs2_val);
+
+ exmem_reg.alu_out = ex_alu_out;
+ exmem_reg.csr_out = ex_csr_out;
+ exmem_reg.rs2_val = idex_reg.rs2_val;
+ exmem_reg.pc = idex_reg.pc;
+ exmem_reg.imm = idex_reg.imm;
+ exmem_reg.rd_num = idex_reg.rd_num;
+ exmem_reg.branch_taken = ex_branch_taken;
+ exmem_reg.branchop = idex_reg.ctrl.branchop;
+ exmem_reg.memop = idex_reg.ctrl.memop;
+ exmem_reg.wbsrc = idex_reg.ctrl.wbsrc;
+ exmem_reg.pcsrc = idex_reg.ctrl.pcsrc;
end
// if (!dmem_valid && ctrl.memop != MEM_NONE) begin
// // TODO stall
@@ -185,19 +195,27 @@ module rv32g_hart
// ==============
// === MEMORY ===
// ==============
-
+
stage_memory u_mem(.memop(exmem_reg.memop),
.alu_out(exmem_reg.alu_out),
.rs2_val(exmem_reg.rs2_val),
- .mem_out(memwb_reg.mem_out),
- .alu_out_passthrough(memwb_reg.alu_out),
+ .mem_out(mem_mem_out),
.dmem_addr, .dmem_rdata, .dmem_wdata, .dmem_we, .dmem_be);
// MEM/WB pipeline register
always_ff @(posedge clk or negedge rstN) begin
- if (!rstN) memwb_reg = '0;
- else begin
+ if (!rstN) begin
+ memwb_reg = '0;
+ end else begin
$display("MEM: memop: %h, dmem_addr: %h, dmem_rdata: %h, dmem_wdata: %h, dmem_we: %d, dmem_be: %b, mem_out: %h",
exmem_reg.memop, dmem_addr, dmem_rdata, dmem_wdata, dmem_we, dmem_be, memwb_reg.mem_out);
+
+ memwb_reg.alu_out = exmem_reg.alu_out;
+ memwb_reg.mem_out = mem_mem_out;
+ memwb_reg.csr_out = exmem_reg.csr_out;
+ memwb_reg.pc = exmem_reg.pc;
+ memwb_reg.imm = exmem_reg.imm;
+ memwb_reg.rd_num = exmem_reg.rd_num;
+ memwb_reg.wbsrc = exmem_reg.wbsrc;
end
// else ; // TODO I need to think of what else should happen
end
@@ -224,11 +242,11 @@ module rv32g_hart
always_comb begin
unique case (exmem_reg.pcsrc)
// normal case
- PC_SEQ: next_pc = pc_reg + 4;
+ PC_SEQ: next_pc = exmem_reg.pc + 4;
// branch or jal
PC_IMM: begin
- if ((exmem_reg.branchop != BRANCH_NONE) ? exmem_reg.branch_taken : 1'b1) next_pc = pc_reg + memwb_reg.imm;
- else next_pc = pc_reg + 4;
+ if ((exmem_reg.branchop != BRANCH_NONE) ? exmem_reg.branch_taken : 1'b1) next_pc = exmem_reg.pc + memwb_reg.imm;
+ else next_pc = exmem_reg.pc + 4;
end
// jalr
PC_ALU: next_pc = memwb_reg.alu_out & ~32'b1; // spec says to clear LSB and /may/ raise an exception
diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv
@@ -317,15 +317,16 @@ package rv32g_pkg;
logic [4:0] rs1_num;
logic [4:0] rs2_num;
logic [4:0] rd_num;
+ reg_t csr_rdata;
logic [11:0] csr_addr;
} idex_reg_t;
typedef struct packed {
reg_t alu_out;
- reg_t mem_out;
reg_t csr_out;
reg_t rs2_val;
reg_t pc;
+ reg_t imm;
logic [4:0] rd_num;
logic branch_taken;
branchop_t branchop;
@@ -338,8 +339,8 @@ package rv32g_pkg;
reg_t alu_out;
reg_t mem_out;
reg_t csr_out;
- reg_t imm;
reg_t pc;
+ reg_t imm;
logic [4:0] rd_num;
wbsrc_t wbsrc;
} memwb_reg_t;
diff --git a/rtl/stage_memory.sv b/rtl/stage_memory.sv
@@ -11,8 +11,7 @@ module stage_memory
// byte-enable signal is used so that memory controller doesn't overwrite adjacent bytes in word
output logic [3:0] dmem_be,
- output logic [31:0] mem_out,
- output logic [31:0] alu_out_passthrough);
+ output logic [31:0] mem_out);
logic [7:0] bdata;
logic [15:0] hdata;
diff --git a/rtl/tb_rv32g.sv b/rtl/tb_rv32g.sv
@@ -83,7 +83,8 @@ module tb_rv32g
parameter int MAX_CYCLES = 10000;
always_ff @(posedge clk) begin
if (exception == EXC_ILL_INST && seen_first_real_inst) begin
- $display("!!! Program terminated (exception ILL_INST at PC %h), inst: %h !!!", pc_out, dut.ifid_reg.inst);
+ $display("!!! Program terminated (exception ILL_INST at PC %h), inst: %h, csrop: %d, csr_ill_rd: %d, csr_ill_wr: %d !!!",
+ pc_out, dut.ifid_reg.inst, dut.idex_reg.ctrl.csrop, dut.u_csr.illegal_rd, dut.u_csr.illegal_wr);
$finish();
end