rv32g-core

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

commit 78d9f04bd0c4aa9e4853d1fa336d4064b3ad2f91
parent 452fc47fa674c4c686416465b9c939568df910fa
Author: Vineet Kumar <git@vineetk.net>
Date:   Wed,  8 Jul 2026 15:11:42 +0530

implement privileged MRET instruction

Diffstat:
MREADME.org | 5++++-
Mrtl/rv32g_hart.sv | 10++++++++--
Mrtl/rv32g_pkg.sv | 9+++++++--
Mrtl/stage_decode.sv | 29+++++++++++++++++++++++++----
Mrtl/stage_execute.sv | 22+++++++++++++++++++---
5 files changed, 63 insertions(+), 12 deletions(-)

diff --git a/README.org b/README.org @@ -63,7 +63,10 @@ run on it. I may change my mind regarding this later on. hours): - Without instruction memory cache: ~40.5 MHz, 15.8k LUT4 - With instruction memory cache: ~33.9 MHz, 24.1k LUT4 - +- July 8, 2026: Zicsr and PRIV-MRET Completed + - clock speed and LUT4s after PnR (cache_depth=4096): + - With instruction memory cache: ~33.9 MHz, 24.1k LUT4 + - this shows the extra CSR registers had a negligible effect * Resources Used - [[https://docs.riscv.org/reference/isa/_attachments/riscv-unprivileged.pdf][RISC-V Instruction Set Manual, Volume I: Unprivileged Architecture (Version 20260120)]] diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv @@ -37,6 +37,9 @@ module rv32g_hart reg_t csr_out; logic csr_illegal; + // CSR has xEPC registers for use with xRET instructions + reg_t csr_mepc; + csr_regfile u_csr(.clk, .rstN, .illegal(csr_illegal), .mepc_shadow(csr_mepc), .raddr(csr_addr), .rden(ctrl.csr_rden), .rdata(csr_rdata), .waddr(csr_addr), .wren(ctrl.csr_wren), .wdata(csr_out)); @@ -81,7 +84,8 @@ module rv32g_hart .csr_immen(ctrl.csr_immen), .csr_imm(ctrl.csr_imm), .csr_rdata(csr_rdata), - .csr_out(csr_out)); + .csr_out(csr_out), + .is_mret(ctrl.is_mret)); // TODO EX/MEM pipeline register // always_ff @(posedge clk or negedge rstN) begin always_ff @(posedge clk) begin @@ -121,7 +125,9 @@ module rv32g_hart else next_pc = pc + 4; end // jalr - PC_ALU: next_pc = alu_out & ~32'd1; // spec says to clear LSB and /may/ raise an exception + PC_ALU: next_pc = alu_out & ~32'b1; // spec says to clear LSB and /may/ raise an exception + // CSR xRET + PC_MRET: next_pc = csr_mepc & ~32'b11; // bit 0 always cleared for alignment, bit 1 when no C-extension endcase end diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv @@ -65,7 +65,9 @@ package rv32g_pkg; CSRRC, CSRRWI, CSRRSI, - CSRRCI} + CSRRCI, + // privileged spec machine-mode (subset) + MRET} inst_name_t; // based off of Unprivileged ISA Chapter 36 Table 72 (no reserved) @@ -155,7 +157,9 @@ package rv32g_pkg; typedef enum logic [1:0] {PC_SEQ, // PC = PC + 4 (regular) PC_IMM, // PC = PC + imm (branch and jal) - PC_ALU // PC = rs1 + imm (jalr) + PC_ALU, // PC = rs1 + imm (jalr) + // for xRET instructions + PC_MRET // PC = mepc (mret) } pcsrc_t; typedef enum logic [2:0] {WB_NONE, // no register writeback @@ -291,5 +295,6 @@ package rv32g_pkg; logic [4:0] csr_imm; exception_t exception; logic fence_flush; + logic is_mret; } ctrl_t; endpackage: rv32g_pkg diff --git a/rtl/stage_decode.sv b/rtl/stage_decode.sv @@ -24,7 +24,6 @@ module stage_decode funct3 = inst[14:12]; rd_num = inst[11:7]; opcode = opcode_t'(inst[6:0]); - csr_addr = inst[31:20]; end // parse the type for each instruction @@ -126,6 +125,11 @@ module stage_decode {7'b???????, 3'b101, OPCODE_SYSTEM}: name = CSRRWI; {7'b???????, 3'b110, OPCODE_SYSTEM}: name = CSRRSI; {7'b???????, 3'b111, OPCODE_SYSTEM}: name = CSRRCI; + // privileged machine-mode (subset) + {7'b0011000, 3'b000, OPCODE_SYSTEM}: begin + if (rs1_num == '0 && rd_num == '0 && rs2_num == 5'b00010) name = MRET; + else name = NAME_INVALID; + end default: name = NAME_INVALID; endcase end @@ -140,12 +144,13 @@ module stage_decode branchop: BRANCH_NONE, wbsrc: WB_NONE, csrop: CSR_NONE, - csr_rden: 1'b0, - csr_wren: 1'b0, + csr_rden: 1'b0, + csr_wren: 1'b0, csr_immen: 1'b0, csr_imm: inst[19:15], exception: EXC_NONE, - fence_flush: 1'b0}; + fence_flush: 1'b0, + is_mret: 1'b0}; // the major opcodes share the same signals, just operation varies based on funct unique case (opcode) @@ -276,24 +281,28 @@ module stage_decode default: ctrl.exception = EXC_ILL_INST; // Zicsr CSRRW: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RW; ctrl.csr_rden = rd_num != 0; ctrl.csr_wren = 1; ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end CSRRS: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RS; ctrl.csr_rden = 1; ctrl.csr_wren = rs1_num != 0; ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end CSRRC: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RC; ctrl.csr_rden = 1; ctrl.csr_wren = rs1_num != 0; ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end CSRRWI: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RW; ctrl.csr_rden = rd_num != 0; ctrl.csr_wren = 1; @@ -301,6 +310,7 @@ module stage_decode ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end CSRRSI: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RS; ctrl.csr_rden = 1; ctrl.csr_wren = ctrl.csr_imm != 0; @@ -308,12 +318,23 @@ module stage_decode ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end CSRRCI: begin + csr_addr = inst[31:20]; ctrl.csrop = CSR_RC; ctrl.csr_rden = 1; ctrl.csr_wren = ctrl.csr_imm != 0; ctrl.csr_immen = 1; ctrl.wbsrc = (rd_num != 0) ? WB_ALU : WB_NONE; end + // privileged machine-mode (subset) + MRET: begin + csr_addr = CSR_MSTATUS; + ctrl.is_mret = 1; + ctrl.csrop = CSR_NONE; + ctrl.csr_rden = 1; + ctrl.csr_wren = 1; + ctrl.csr_immen = 0; + ctrl.pcsrc = PC_MRET; + end endcase end diff --git a/rtl/stage_execute.sv b/rtl/stage_execute.sv @@ -17,13 +17,17 @@ module stage_execute input logic csr_immen, input logic [4:0] csr_imm, input reg_t csr_rdata, - output reg_t csr_out); + output reg_t csr_out, + // privileged machine-mode (subset) + input logic is_mret); - reg_t a; - reg_t b; + reg_t a, b; logic [4:0] shamt; logic [31:0] csr_wdata; + // privileged machine-mode (subset) + reg_t mstatus_old, mstatus_new; + always_comb begin // assign inputs unique case (op1src) @@ -77,6 +81,18 @@ module stage_execute CSR_RS: csr_out = csr_rdata | csr_wdata; CSR_RC: csr_out = csr_rdata & ~csr_wdata; endcase + + // privileged machine-mode (subset) + if (is_mret) begin + mstatus_old = csr_rdata; + mstatus_new = mstatus_old; + // MIE bit 3, MPIE bit 7, MPP bits 12-11 + mstatus_new[3] = mstatus_old[7]; // set MIE to MPIE + mstatus_new[7] = 1'b1; // enable MPIE 1 + mstatus_new[12:11] = 2'b11; // set MPP to M-mode + // with only M-mode, don't need to clear MPRV (bit 17) + csr_out = mstatus_new; + end end endmodule: stage_execute