rv32g-core

Pipelined, synthesizable, and fabricable RISC-V core in SystemVerilog+Yosys+OpenROAD
Log | Files | Refs | LICENSE

commit 55ea46312b825c0c2c238bb14f6c853a334c6a3e
parent 6d5c2288a6e946986faf1f57f0355c30c1b0c6b8
Author: Vineet Kumar <git@vineetk.net>
Date:   Mon, 13 Jul 2026 10:12:00 +0530

insert stall after csr regfile registered read

Diffstat:
Mrtl/csr_regfile.sv | 13+++++++++++--
Mrtl/rv32g_hart.sv | 46++++++++++++++++++++++++++++++++--------------
Mrtl/rv32g_pkg.sv | 10++++++++--
Mrtl/stage_decode.sv | 4++--
4 files changed, 53 insertions(+), 20 deletions(-)

diff --git a/rtl/csr_regfile.sv b/rtl/csr_regfile.sv @@ -10,6 +10,7 @@ module csr_regfile // write port input logic wren, input reg_t wdata, + input logic [4:0] rs1_num, // xEPC shadows for xRET output reg_t mepc_shadow); @@ -29,12 +30,20 @@ module csr_regfile always_comb begin illegal_rd = 1'b0; - illegal_wr = csr_rw_mask == 2'b11; + illegal_wr = csr_rw_mask == 2'b11 && rs1_num != '0; idx = '0; // numbers for csr_rw_mask and priv_level from privileged spec 2.1 // register numbers from privileged spec 2.2 unique case ({csr_rw_mask, priv_level}) + // unprivileged ro + 4'b1100: begin + unique case (csr_low_addr) + // unprivileged counter/timer + 8'h00: idx = CSR_REG_CYCLE; + default: illegal_rd = 1'b1; + endcase + end // supervisor rw 4'b0001: begin unique case (csr_low_addr) @@ -123,7 +132,7 @@ module csr_regfile else rdata <= '0; // perform sequential write when wren and not read-only - if (wren && !illegal_wr) begin + if (wren && !illegal_wr && rs1_num != '0) begin regfile[idx] <= wdata; if (idx == CSR_REG_MEPC) mepc_reg <= wdata; end diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv @@ -19,29 +19,44 @@ module rv32g_hart output exception_t exception); // x0 to x31 - reg_t regfile [0:31]; - reg_t next_pc; + reg_t regfile [0:31]; + reg_t next_pc; // Zicsr - reg_t csr_rdata; - logic csr_illegal; + reg_t csr_rdata; + logic csr_illegal; // CSR has xEPC registers for use with xRET instructions - reg_t csr_mepc; + reg_t csr_mepc; // pipeline registers - ifid_reg_t ifid_reg, ifid_next; - idex_reg_t idex_reg; - exmem_reg_t exmem_reg; - memwb_reg_t memwb_reg; + ifid_reg_t ifid_reg, ifid_next; + idex_reg_t idex_reg; + exmem_reg_t exmem_reg; + memwb_reg_t memwb_reg; // hazards - logic if_stall; + 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), + csr_regfile u_csr(.clk, .rstN, .addr(idex_reg.csr_addr), .rs1_num(idex_reg.rs1_num), .mepc_shadow(csr_mepc), .illegal(csr_illegal), .rden(idex_reg.ctrl.csr_rden), .rdata(csr_rdata), .wren(idex_reg.ctrl.csr_wren), .wdata(exmem_reg.csr_out)); @@ -71,7 +86,7 @@ module rv32g_hart if (!rstN) begin pc_reg <= BASE_ADDR; ifid_reg <= '0; - end else if (!if_stall) begin + end else if (!if_stall && !csr_read_stall) begin pc_reg <= next_pc; ifid_reg <= ifid_next; end @@ -84,12 +99,13 @@ module rv32g_hart // temporary signals ctrl_t dec_ctrl; reg_t dec_pc, dec_imm, dec_rs1_val, dec_rs2_val; - logic [4:0] dec_rd_num; + 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), + .rs1_num(dec_rs1_num), .rs2_num(dec_rs2_num), .rs1_val(dec_rs1_val), .rs2_val(dec_rs2_val), .rd_num(dec_rd_num), .csr_addr(dec_csr_addr)); // ID/EX pipeline register @@ -99,7 +115,7 @@ module rv32g_hart if (!rstN) begin // NOP by default idex_reg <= '0; - end else if (if_stall) begin + end else if (if_stall || csr_read_stall) begin // insert NOP idex_reg <= '0; end else begin @@ -109,6 +125,8 @@ module rv32g_hart idex_reg.imm <= dec_imm; idex_reg.rs1_val <= dec_rs1_val; idex_reg.rs2_val <= dec_rs2_val; + idex_reg.rs1_num <= dec_rs1_num; + idex_reg.rs2_num <= dec_rs2_num; idex_reg.rd_num <= dec_rd_num; idex_reg.csr_addr <= dec_csr_addr; end diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv @@ -177,7 +177,9 @@ package rv32g_pkg; } csrop_t; // numbers from privileged spec 2.2 - typedef enum logic [11:0] {// supervisor trap setup + typedef enum logic [11:0] {// unprivileged counter/timers + CSR_CYCLE = 'hC00, + // supervisor trap setup CSR_SSTATUS = 'h100, CSR_SIE = 'h104, CSR_STVEC = 'h105, @@ -224,7 +226,9 @@ package rv32g_pkg; CSR_MNCAUSE = 'h742, CSR_MNSTATUS = 'h744 } csr_t; - typedef enum logic [$clog2(CSR_SIZE)-1:0] {// supervisor trap setup + typedef enum logic [$clog2(CSR_SIZE)-1:0] {// unprivileged counter/timers + CSR_REG_CYCLE, + // supervisor trap setup CSR_REG_SSTATUS, CSR_REG_SIE, CSR_REG_STVEC, @@ -310,6 +314,8 @@ package rv32g_pkg; logic [31:0] imm; reg_t rs1_val; reg_t rs2_val; + logic [4:0] rs1_num; + logic [4:0] rs2_num; logic [4:0] rd_num; logic [11:0] csr_addr; } idex_reg_t; diff --git a/rtl/stage_decode.sv b/rtl/stage_decode.sv @@ -8,6 +8,8 @@ module stage_decode output logic [31:0] imm, output logic [31:0] rs1_val, output logic [31:0] rs2_val, + output logic [4:0] rs1_num, + output logic [4:0] rs2_num, output logic [4:0] rd_num, output logic [11:0] csr_addr, // passthrough @@ -15,8 +17,6 @@ module stage_decode output reg_t pc_out); logic [6:0] funct7; - logic [4:0] rs2_num; - logic [4:0] rs1_num; logic [2:0] funct3; opcode_t opcode; inst_type_t inst_type;