rv32g-core

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

commit 8ff59035f7ac85e58ee138217a3d3674e38d8d6b
parent 56f88f259a5816f0990835871442c477c73c251d
Author: Vineet Kumar <git@vineetk.net>
Date:   Sat, 22 Aug 2026 04:25:40 +0530

remove expensive redundant name-based opcode decoding

Recovery-Origin: e0d56cb9dad1438e5645bfbb464f2ab287696347
Recovery-Status: reconstructed history

Reconstructed direct opcode/funct3 control decoding and removed unused instruction-name enum and PC passthrough. Final hart/pkg/decoder are hash-verified originals; the temporary explicit ADDI-as-NOP block is an inferred intermediate state for the following documented removal.

Diffstat:
Mrtl/rv32g_hart.sv | 3+--
Mrtl/rv32g_pkg.sv | 47-----------------------------------------------
Mrtl/stage_decode.sv | 182+++++++++++++++++++++----------------------------------------------------------
3 files changed, 48 insertions(+), 184 deletions(-)

diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv @@ -96,7 +96,6 @@ module rv32g_hart stage_decode u_decode(.clk, .rstN, .inst(ifid_inst), .wb_we, .wb_rd_num(memwb_reg.rd_num), .wb_data(memwb_value), - .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(idex_rs1_val), .rs2_val(idex_rs2_val), @@ -157,7 +156,7 @@ module rv32g_hart // latch decode outputs in normal operation idex_reg.valid <= ifid_reg.valid && imem_valid; idex_reg.ctrl <= dec_ctrl; - idex_reg.pc <= dec_pc; + idex_reg.pc <= ifid_reg.pc; idex_reg.imm <= dec_imm; idex_reg.rs1_forward <= dec_rs1_forward; idex_reg.rs2_forward <= dec_rs2_forward; diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv @@ -14,53 +14,6 @@ package rv32g_pkg; JTYPE} inst_type_t; - typedef enum logic [5:0] {NAME_INVALID, - // rv32i - LUI, - AUIPC, - JAL, - JALR, - BEQ, - BNE, - BLT, - BGE, - BLTU, - BGEU, - LB, - LH, - LW, - LBU, - LHU, - SB, - SH, - SW, - ADDI, - SLTI, - SLTIU, - XORI, - ORI, - ANDI, - SLLI, - SRLI, - SRAI, - ADD, - SUB, - SLL, - SLT, - SLTU, - XOR, - SRL, - SRA, - OR, - AND, - FENCE, - FENCE_I, - FENCE_TSO, - PAUSE, - ECALL, - EBREAK} - inst_name_t; - // based off of Unprivileged ISA Chapter 36 Table 72 (no reserved) typedef enum logic [6:0] {OPCODE_INVALID = 'b0000000, // rv32i/m diff --git a/rtl/stage_decode.sv b/rtl/stage_decode.sv @@ -12,16 +12,12 @@ module stage_decode output logic [31:0] rs2_val, output logic [4:0] rs1_num, output logic [4:0] rs2_num, - output logic [4:0] rd_num, - // passthrough - input reg_t pc_in, - output reg_t pc_out); + output logic [4:0] rd_num); logic [6:0] funct7; logic [2:0] funct3; opcode_t opcode; inst_type_t inst_type; - inst_name_t name; reg_t regfile [0:31]; reg_t regfile_b [0:31]; @@ -32,9 +28,6 @@ module stage_decode funct3 = inst[14:12]; rd_num = inst[11:7]; opcode = opcode_t'(inst[6:0]); - - // passthrorugh - pc_out = pc_in; end // parse the type for each instruction @@ -69,71 +62,7 @@ module stage_decode endcase end - // parse what the actual instruction - always_comb begin - case ({funct7, funct3, opcode}) inside - // rv32i - {7'b???????, 3'b???, OPCODE_LUI}: name = LUI; - {7'b???????, 3'b???, OPCODE_AUIPC}: name = AUIPC; - {7'b???????, 3'b???, OPCODE_JAL}: name = JAL; - {7'b???????, 3'b???, OPCODE_JALR}: name = JALR; - {7'b???????, 3'b000, OPCODE_BRANCH}: name = BEQ; - {7'b???????, 3'b001, OPCODE_BRANCH}: name = BNE; - {7'b???????, 3'b100, OPCODE_BRANCH}: name = BLT; - {7'b???????, 3'b101, OPCODE_BRANCH}: name = BGE; - {7'b???????, 3'b110, OPCODE_BRANCH}: name = BLTU; - {7'b???????, 3'b111, OPCODE_BRANCH}: name = BGEU; - {7'b???????, 3'b000, OPCODE_LOAD}: name = LB; - {7'b???????, 3'b001, OPCODE_LOAD}: name = LH; - {7'b???????, 3'b010, OPCODE_LOAD}: name = LW; - {7'b???????, 3'b100, OPCODE_LOAD}: name = LBU; - {7'b???????, 3'b101, OPCODE_LOAD}: name = LHU; - {7'b???????, 3'b000, OPCODE_STORE}: name = SB; - {7'b???????, 3'b001, OPCODE_STORE}: name = SH; - {7'b???????, 3'b010, OPCODE_STORE}: name = SW; - {7'b???????, 3'b000, OPCODE_OP_IMM}: name = ADDI; - {7'b???????, 3'b010, OPCODE_OP_IMM}: name = SLTI; - {7'b???????, 3'b011, OPCODE_OP_IMM}: name = SLTIU; - {7'b???????, 3'b100, OPCODE_OP_IMM}: name = XORI; - {7'b???????, 3'b110, OPCODE_OP_IMM}: name = ORI; - {7'b???????, 3'b111, OPCODE_OP_IMM}: name = ANDI; - {7'b0000000, 3'b001, OPCODE_OP_IMM}: name = SLLI; - {7'b0000000, 3'b101, OPCODE_OP_IMM}: name = SRLI; - {7'b0100000, 3'b101, OPCODE_OP_IMM}: name = SRAI; - {7'b0000000, 3'b000, OPCODE_OP}: name = ADD; - {7'b0100000, 3'b000, OPCODE_OP}: name = SUB; - {7'b0000000, 3'b001, OPCODE_OP}: name = SLL; - {7'b0000000, 3'b010, OPCODE_OP}: name = SLT; - {7'b0000000, 3'b011, OPCODE_OP}: name = SLTU; - {7'b0000000, 3'b100, OPCODE_OP}: name = XOR; - {7'b0000000, 3'b101, OPCODE_OP}: name = SRL; - {7'b0100000, 3'b101, OPCODE_OP}: name = SRA; - {7'b0000000, 3'b110, OPCODE_OP}: name = OR; - {7'b0000000, 3'b111, OPCODE_OP}: name = AND; - {7'b???????, 3'b000, OPCODE_MISC_MEM}: begin - // FENCE{,.I,.TSO}/PAUSE encoding is a bit different - if (rs1_num == '0 && rd_num == '0) begin - if (inst[31:20] == 'b1000_0011_0011) name = FENCE_TSO; - else if (inst[31:20] == 'b0000_0001_0000) name = PAUSE; - else name = FENCE; - end else name = NAME_INVALID; - end - {7'b0000000, 3'b000, OPCODE_SYSTEM}: begin - // ECALL and EBREAK encoding is also a bit different - if (rs1_num == '0 && rd_num == '0 && rs2_num[4:1] == '0) begin - name = (rs2_num[0] == '0) ? ECALL : EBREAK; - end else name = NAME_INVALID; - end - // Zifencei - {7'b0000000, 3'b001, OPCODE_MISC_MEM}: begin - if (rs1_num == '0 && rd_num == '0 && inst[31:20] == '0) name = FENCE_I; - else name = NAME_INVALID; - end - default: name = NAME_INVALID; - endcase - end - - // set control signals based on instruction name + // set control signals always_comb begin ctrl = '{aluop: ALU_NONE, aluop1src: ALUOP1_NONE, @@ -170,91 +99,74 @@ module stage_decode ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_REG; ctrl.pcsrc = PC_IMM; + ctrl.branchop = branchop_t'(funct3); end OPCODE_LOAD: begin ctrl.aluop = ALU_ADD; ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_IMM; ctrl.wbsrc = WB_MEM; + ctrl.loadop = loadop_t'(funct3); end OPCODE_STORE: begin ctrl.aluop = ALU_ADD; ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_IMM; + ctrl.storeop = storeop_t'(funct3); end OPCODE_OP_IMM: begin ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_IMM; ctrl.wbsrc = WB_ALU; + unique case (funct3) + // rv32i; done in same order as it appears in chapter 36 of unprivileged manual + 3'b000: begin + if (rd_num == 0 && rs1_num == 0 && imm == 0) begin + ctrl.aluop = ALU_NONE; + ctrl.aluop1src = ALUOP1_NONE; + ctrl.aluop2src = ALUOP2_NONE; + ctrl.wbsrc = WB_NONE; + end else ctrl.aluop = ALU_ADD; + end + 3'b010: ctrl.aluop = ALU_SLT; + 3'b011: ctrl.aluop = ALU_SLTU; + 3'b100: ctrl.aluop = ALU_XOR; + 3'b110: ctrl.aluop = ALU_OR; + 3'b111: ctrl.aluop = ALU_AND; + 3'b001: ctrl.aluop = (funct7 == '0) ? ALU_SLL : ALU_NONE; + 3'b101: begin + if ({funct7[6], funct7[4:0]} == '0) ctrl.aluop = (funct7[5] == '0) ? ALU_SRL : ALU_SRA; + else ctrl.aluop = ALU_NONE; + end + endcase end OPCODE_OP: begin ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_REG; ctrl.wbsrc = WB_ALU; + unique case ({funct7, funct3}) + // rv32i + {7'b0000000, 3'b000}: ctrl.aluop = ALU_ADD; + {7'b0100000, 3'b000}: ctrl.aluop = ALU_SUB; + {7'b0000000, 3'b001}: ctrl.aluop = ALU_SLL; + {7'b0000000, 3'b010}: ctrl.aluop = ALU_SLT; + {7'b0000000, 3'b011}: ctrl.aluop = ALU_SLTU; + {7'b0000000, 3'b100}: ctrl.aluop = ALU_XOR; + {7'b0000000, 3'b101}: ctrl.aluop = ALU_SRL; + {7'b0100000, 3'b101}: ctrl.aluop = ALU_SRA; + {7'b0000000, 3'b110}: ctrl.aluop = ALU_OR; + {7'b0000000, 3'b111}: ctrl.aluop = ALU_AND; + default: ctrl.aluop = ALU_NONE; + endcase end - OPCODE_MISC_MEM: ; - OPCODE_SYSTEM: ; - default: ; - endcase - - unique case (name) - // rv32i - LUI: ; - AUIPC: ; - JAL: ; - JALR: ; - BEQ: ctrl.branchop = BRANCH_BEQ; - BNE: ctrl.branchop = BRANCH_BNE; - BLT: ctrl.branchop = BRANCH_BLT; - BGE: ctrl.branchop = BRANCH_BGE; - BLTU: ctrl.branchop = BRANCH_BLTU; - BGEU: ctrl.branchop = BRANCH_BGEU; - LB: ctrl.loadop = LD_LB; - LH: ctrl.loadop = LD_LH; - LW: ctrl.loadop = LD_LW; - LBU: ctrl.loadop = LD_LBU; - LHU: ctrl.loadop = LD_LHU; - SB: ctrl.storeop = ST_SB; - SH: ctrl.storeop = ST_SH; - SW: ctrl.storeop = ST_SW; - ADDI: begin - if (rd_num == 0 && rs1_num == 0 && imm == 0) begin // handle nop by doing a nop - ctrl.aluop = ALU_NONE; - ctrl.aluop1src = ALUOP1_NONE; - ctrl.aluop2src = ALUOP2_NONE; - ctrl.wbsrc = WB_NONE; - end else begin - ctrl.aluop = ALU_ADD; - end + OPCODE_MISC_MEM: ctrl.fence_flush = rs1_num == '0 && rd_num == '0 && inst[31:20] == '0; + OPCODE_SYSTEM: begin + // ECALL and EBREAK encoding is also a bit different + if (rs1_num == '0 && rd_num == '0 && rs2_num[4:1] == '0) begin + ctrl.exception = (rs2_num[0] == '0) ? EXC_CALL : EXC_BKPT; + end else ctrl.exception = EXC_NONE; end - SLTI: ctrl.aluop = ALU_SLT; - SLTIU: ctrl.aluop = ALU_SLTU; - XORI: ctrl.aluop = ALU_XOR; - ORI: ctrl.aluop = ALU_OR; - ANDI: ctrl.aluop = ALU_AND; - SLLI: ctrl.aluop = ALU_SLL; - SRLI: ctrl.aluop = ALU_SRL; - SRAI: ctrl.aluop = ALU_SRA; - ADD: ctrl.aluop = ALU_ADD; - SUB: ctrl.aluop = ALU_SUB; - SLL: ctrl.aluop = ALU_SLL; - SLT: ctrl.aluop = ALU_SLT; - SLTU: ctrl.aluop = ALU_SLTU; - XOR: ctrl.aluop = ALU_XOR; - SRL: ctrl.aluop = ALU_SRL; - SRA: ctrl.aluop = ALU_SRA; - OR: ctrl.aluop = ALU_OR; - AND: ctrl.aluop = ALU_AND; - // MISC-MEM instructions are basically NOPs on single-hart system - FENCE: ; - FENCE_TSO: ; - PAUSE: ; - // SYSTEM instructions do traps - ECALL: ctrl.exception = EXC_CALL; - EBREAK: ctrl.exception = EXC_BKPT; - // Zifencei - FENCE_I: ctrl.fence_flush = 1'b1; - default: ctrl.exception = EXC_ILL_INST; + default: ; endcase end