commit 78d9f04bd0c4aa9e4853d1fa336d4064b3ad2f91
parent 452fc47fa674c4c686416465b9c939568df910fa
Author: Vineet Kumar <git@vineetk.net>
Date: Wed, 8 Jul 2026 15:11:42 +0530
implement privileged MRET instruction
Diffstat:
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