commit fb189d59d5c8b1f339870a151775b9889a46fcc8
parent d233e76dcd37b3d6773d865129119f9c0afbf921
Author: Vineet Kumar <git@vineetk.net>
Date: Mon, 1 Jun 2026 18:01:11 -0400
add execute stage
Diffstat:
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