rv32g-core

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

commit fb189d59d5c8b1f339870a151775b9889a46fcc8
parent d233e76dcd37b3d6773d865129119f9c0afbf921
Author: Vineet Kumar <git@vineetk.net>
Date:   Mon,  1 Jun 2026 18:01:11 -0400

add execute stage

Diffstat:
Mrtl/rv32g_hart.sv | 54++++++++++++++++++++++++++++++++++++++++--------------
Mrtl/rv32g_pkg.sv | 2+-
Mrtl/stage_decode.sv | 31++++++++++++++++++++++++-------
Artl/stage_execute.sv | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 123 insertions(+), 22 deletions(-)

diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv @@ -1,16 +1,16 @@ module rv32g_hart import rv32g_pkg::*; - (input logic clk, - input logic rstN, + (input logic clk, + input logic rstN, - output logic [31:0] imem_addr, - input logic [31:0] imem_data, - input logic imem_valid, + output logic [31:0] imem_addr, + input logic [31:0] imem_data, + input logic imem_valid, - output logic [31:0] dmem_addr, - input logic [31:0] dmem_data, - output logic [31:0] dmem_wdata, - output logic dmem_we); + output logic [31:0] dmem_addr, + input logic [31:0] dmem_data, + output logic [31:0] dmem_wdata, + output logic dmem_we); // x0 to x31 reg_t regfile [0:31]; @@ -23,6 +23,8 @@ module rv32g_hart reg_t rs1_val; reg_t rs2_val; logic [4:0] rd_num; + reg_t alu_out; + logic branch_taken; assign regfile[0] = 'd0; always_ff @(posedge clk or negedge rstN) begin @@ -40,13 +42,37 @@ module rv32g_hart end stage_decode u_decode(.inst, .regfile, .ctrl, .imm, .rs1_val, .rs2_val, .rd_num); + // TODO ID/EX pipeline register + // TODO find out what else needs to be done in between stages like exceptions + stage_execute u_execute(.op(ctrl.aluop), + .op1src(ctrl.aluop1src), + .op2src(ctrl.aluop2src), + .branchop(ctrl.branchop), + .out(alu_out), + .imm, .pc, .rs1_val, .rs2_val, .branch_taken); + // TODO EX/MEM pipeline register + // TODO find out what else needs to be done in between stages + + // TODO stage_mem u_mem(.ctrl, .alu_out, .dmem_addr, .dmem_data, .dmem_wdata, .dmem_we); + // TODO MEM/WB pipeline register + // TODO find out what else needs to be done in between stages - // TODO stage_execute u_execute(.*); - // TODO stage_mem u_mem(.*); - // TODO stage_wb u_wb(.*); + // TODO stage_wb u_wb(.ctrl, .alu_out, .rd_num, .regfile); + // TODO find out what else needs to be done after this stage - // TODO replace with mux handling jumping - assign next_pc = pc + 4; + always_comb begin + unique case (ctrl.pcsrc) + // normal case + PC_SEQ: next_pc = pc + 4; + // branch or jal + PC_IMM: begin + if ((ctrl.branchop != BRANCH_NONE) ? branch_taken : 1'b1) next_pc = pc + imm; + else next_pc = pc + 4; + end + // jalr + PC_ALU: next_pc = alu_out & ~32'd1; // spec says to clear LSB + endcase + end endmodule: rv32g_hart diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv @@ -58,7 +58,7 @@ package rv32g_pkg; } pcsrc_t; typedef enum logic [2:0] {WB_NONE, // no register writeback - WB_PC, // JAL writes using PC adder + WB_PC, // JAL/R writes using PC adder, i.e. rd = pc + 4 WB_ALU, // R-type and I-type instructions WB_MEM, // All load instructions WB_IMM // LUI writes back an immediate value diff --git a/rtl/stage_decode.sv b/rtl/stage_decode.sv @@ -229,7 +229,6 @@ module stage_decode ctrl.wbsrc = WB_PC; end OPCODE_BRANCH: begin - ctrl.aluop = ALU_SUB; ctrl.aluop1src = ALUOP1_REG; ctrl.aluop2src = ALUOP2_REG; ctrl.pcsrc = PC_IMM; @@ -266,12 +265,30 @@ module stage_decode 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; + BEQ: begin + ctrl.aluop = ALU_SUB; + ctrl.branchop = BRANCH_BEQ; + end + BNE: begin + ctrl.aluop = ALU_SUB; + ctrl.branchop = BRANCH_BNE; + end + BLT: begin + ctrl.aluop = ALU_SLT; + ctrl.branchop = BRANCH_BLT; + end + BGE: begin + ctrl.aluop = ALU_SLT; + ctrl.branchop = BRANCH_BGE; + end + BLTU: begin + ctrl.aluop = ALU_SLTU; + ctrl.branchop = BRANCH_BLTU; + end + BGEU: begin + ctrl.aluop = ALU_SLTU; + ctrl.branchop = BRANCH_BGEU; + end LB: ctrl.memop = MEM_LB; LH: ctrl.memop = MEM_LH; LW: ctrl.memop = MEM_LW; diff --git a/rtl/stage_execute.sv b/rtl/stage_execute.sv @@ -0,0 +1,58 @@ +module stage_execute + import rv32g_pkg::*; + (input aluop_t op, + input aluop1src_t op1src, + input aluop2src_t op2src, + input branchop_t branchop, + input logic [31:0] imm, + input reg_t pc, + input reg_t rs1_val, + input reg_t rs2_val, + output logic [31:0] out, + output logic branch_taken); + + reg_t a; + reg_t b; + logic [4:0] shamt = b[4:0]; + + always_comb begin + // assign inputs + unique case (op1src) + ALUOP1_NONE: a = '0; + ALUOP1_REG: a = rs1_val; + ALUOP1_PC: a = pc; + endcase + unique case (op2src) + ALUOP2_NONE: b = '0; + ALUOP2_REG: b = rs2_val; + ALUOP2_IMM: b = imm; + endcase + + // perform computation + unique case (op) + ALU_NONE: out = '0; + ALU_ADD: out = a + b; + ALU_SUB: out = $signed(a) - $signed(b); + ALU_XOR: out = a ^ b; + ALU_OR: out = a | b; + ALU_AND: out = a & b; + ALU_SLL: out = a << shamt; + ALU_SRL: out = a >> shamt; + ALU_SRA: out = $signed(a) >>> shamt; + ALU_SLT: out = ($signed(a) < $signed(b)) ? 1 : 0; + ALU_SLTU: out = (a < b) ? 1 : 0; + endcase + + // check if branch should be taken + unique case (branchop) + BRANCH_NONE: branch_taken = 0; + BRANCH_BEQ: branch_taken = (out == 0) ? 1 : 0; + BRANCH_BNE: branch_taken = (out != 0) ? 1 : 0; + BRANCH_BLT: branch_taken = (out[0] == 1'b1) ? 1 : 0; + BRANCH_BGE: branch_taken = (out[0] != 1'b1) ? 1 : 0; + BRANCH_BLTU: branch_taken = (out[0] == 1'b1) ? 1 : 0; + BRANCH_BGEU: branch_taken = (out[0] != 1'b1) ? 1 : 0; + endcase + end + +endmodule: stage_execute