commit 3b842277ef7a42858e7de7cd9fc8dca5800753a0
parent 57f97ea9a2d53e0e618a00e05e2eb24a5fc10c6b
Author: Vineet Kumar <git@vineetk.net>
Date: Thu, 6 Aug 2026 23:02:33 +0530
reduce pipeline critical path delay
Recovery-Origin: 4373f2fe41311b37aaf19ddc05a57f7e55661cb0
Recovery-Status: reconstructed history
Reconstructed from intact earlier amendment 75f524f and later surviving RTL: preserve pipeline optimization, registered PC+4 and pipeline reset changes. Exact final amendment object is missing; allocation of later observed changes to this commit is inferred.
Diffstat:
7 files changed, 229 insertions(+), 194 deletions(-)
diff --git a/rtl/rv32g_hart.sv b/rtl/rv32g_hart.sv
@@ -18,8 +18,6 @@ module rv32g_hart
output exception_t exception);
- // x0 to x31
- reg_t regfile [0:31];
reg_t next_pc;
// pipeline registers
@@ -29,28 +27,30 @@ module rv32g_hart
memwb_reg_t memwb_reg;
// temporary fetch signals
- logic [31:0] fetch_addr, fetch_inst;
- reg_t pc_reg;
+ logic [31:0] fetch_inst, ifid_inst, replay_inst;
+ logic replay_valid;
+ reg_t pc_reg, pc_plus4; // pre-computed registered +4 for critical path
// temporary decode signals
ctrl_t dec_ctrl;
- reg_t dec_pc, dec_imm, dec_rs1_val, dec_rs2_val;
+ reg_t dec_pc, dec_imm, idex_rs1_val, idex_rs2_val;
logic [4:0] dec_rs1_num, dec_rs2_num, dec_rd_num;
// temporary execute signals
- reg_t ex_rs1_val, ex_rs2_val, ex_alu_out;
+ reg_t ex_rs1_val, ex_rs2_val, ex_alu_out, ex_jalr_target, ex_wb_value;
logic ex_branch_taken;
// temporary memory signals
memop_t mem_memop;
logic [31:0] mem_mem_out;
- reg_t exmem_value, memwb_value;
+ reg_t memwb_value;
// temporary writeback signals
logic wb_valid, wb_we;
// hazards and control flow
logic dec_uses_rs1, dec_uses_rs2;
+ forward_t dec_rs1_forward, dec_rs2_forward;
logic load_use_stall;
logic redirect_valid;
reg_t redirect_target;
@@ -60,24 +60,32 @@ module rv32g_hart
// =============
assign pc_out = pc_reg;
- // re-read decoded instruction on load-use stall
- assign fetch_addr = load_use_stall ? ifid_reg.pc : pc_reg;
+ assign ifid_inst = replay_valid ? replay_inst : fetch_inst;
stage_fetch u_fetch(.clk, .rstN, .flush_i(idex_reg.valid && idex_reg.ctrl.fence_flush),
- .pc(fetch_addr), .imem_addr, .imem_data, .inst(fetch_inst));
+ .pc(pc_reg), .imem_addr, .imem_data, .imem_valid,
+ .inst(fetch_inst));
// IF/ID pipeline register
- always_ff @(posedge clk or negedge rstN) begin
+ always_ff @(posedge clk) begin
if (!rstN) begin
pc_reg <= BASE_ADDR;
- ifid_reg <= '0;
+ pc_plus4 <= BASE_ADDR + 4;
+ ifid_reg.valid <= 1'b0;
+ replay_valid <= 1'b0;
end else if (redirect_valid) begin
pc_reg <= redirect_target;
- ifid_reg <= '0;
- end else if (!load_use_stall) begin
+ pc_plus4 <= redirect_target + 4;
+ ifid_reg.valid <= 1'b0;
+ replay_valid <= 1'b0;
+ end else if (load_use_stall) begin
+ if (!replay_valid) replay_inst <= fetch_inst;
+ replay_valid <= 1'b1;
+ end else begin
pc_reg <= next_pc;
+ pc_plus4 <= next_pc + 4;
ifid_reg.valid <= 1'b1;
ifid_reg.pc <= pc_reg;
- ifid_reg.inst <= fetch_inst;
+ replay_valid <= 1'b0;
end
end
@@ -85,45 +93,73 @@ module rv32g_hart
// === DECODE ===
// ==============
- stage_decode u_decode(.clk, .rstN, .regfile, .inst(fetch_inst),
+ 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(dec_rs1_val), .rs2_val(dec_rs2_val),
+ .rs1_val(idex_rs1_val), .rs2_val(idex_rs2_val),
.rd_num(dec_rd_num));
// assign control hazard signals
always_comb begin
- dec_uses_rs1 = dec_ctrl.aluop1src == ALUOP1_REG;
- dec_uses_rs2 = dec_ctrl.aluop2src == ALUOP2_REG ||
- dec_ctrl.memop == MEM_SB ||
- dec_ctrl.memop == MEM_SH ||
- dec_ctrl.memop == MEM_SW;
-
- load_use_stall = idex_reg.valid && idex_reg.ctrl.wbsrc == WB_MEM &&
- idex_reg.rd_num != 0 && ifid_reg.valid && imem_valid &&
- ((dec_uses_rs1 && dec_rs1_num == idex_reg.rd_num) ||
- (dec_uses_rs2 && dec_rs2_num == idex_reg.rd_num));
+ dec_uses_rs1 = 1'b0;
+ dec_uses_rs2 = 1'b0;
+ dec_rs1_forward = FWD_REG;
+ dec_rs2_forward = FWD_REG;
+
+ unique case (opcode_t'(ifid_inst[6:0]))
+ OPCODE_JALR, OPCODE_LOAD, OPCODE_OP_IMM: dec_uses_rs1 = 1'b1;
+ OPCODE_BRANCH, OPCODE_STORE, OPCODE_OP: begin
+ dec_uses_rs1 = 1'b1;
+ dec_uses_rs2 = 1'b1;
+ end
+ default: ;
+ endcase
+
+ if (dec_uses_rs1 && dec_rs1_num != 0) begin
+ if (idex_reg.valid && idex_reg.ctrl.wbsrc != WB_NONE &&
+ idex_reg.ctrl.wbsrc != WB_MEM && idex_reg.rd_num == dec_rs1_num)
+ dec_rs1_forward = FWD_EXMEM;
+ else if (exmem_reg.valid && exmem_reg.wbsrc != WB_NONE &&
+ exmem_reg.wbsrc != WB_MEM && exmem_reg.rd_num == dec_rs1_num)
+ dec_rs1_forward = FWD_MEMWB;
+ end
+
+ if (dec_uses_rs2 && dec_rs2_num != 0) begin
+ if (idex_reg.valid && idex_reg.ctrl.wbsrc != WB_NONE &&
+ idex_reg.ctrl.wbsrc != WB_MEM && idex_reg.rd_num == dec_rs2_num)
+ dec_rs2_forward = FWD_EXMEM;
+ else if (exmem_reg.valid && exmem_reg.wbsrc != WB_NONE &&
+ exmem_reg.wbsrc != WB_MEM && exmem_reg.rd_num == dec_rs2_num)
+ dec_rs2_forward = FWD_MEMWB;
+ end
+
+ load_use_stall = ifid_reg.valid && imem_valid &&
+ ((idex_reg.valid && idex_reg.ctrl.wbsrc == WB_MEM &&
+ idex_reg.rd_num != 0 &&
+ ((dec_uses_rs1 && dec_rs1_num == idex_reg.rd_num) ||
+ (dec_uses_rs2 && dec_rs2_num == idex_reg.rd_num))) ||
+ (exmem_reg.valid && exmem_reg.wbsrc == WB_MEM &&
+ exmem_reg.rd_num != 0 &&
+ ((dec_uses_rs1 && dec_rs1_num == exmem_reg.rd_num) ||
+ (dec_uses_rs2 && dec_rs2_num == exmem_reg.rd_num))));
end
// ID/EX pipeline register
- always_ff @(posedge clk) begin
+ always_ff @(posedge clk or negedge rstN) begin
if (!rstN) begin
- // NOP by default
idex_reg <= '0;
- idex_reg.pc <= BASE_ADDR;
end else if (redirect_valid || load_use_stall) begin
- // NOP bubble
- idex_reg <= '0;
+ idex_reg.valid <= 1'b0;
end else begin
// 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.imm <= dec_imm;
- idex_reg.rs1_num <= dec_rs1_num;
- idex_reg.rs2_num <= dec_rs2_num;
+ idex_reg.rs1_forward <= dec_rs1_forward;
+ idex_reg.rs2_forward <= dec_rs2_forward;
idex_reg.rd_num <= dec_rd_num;
end
end
@@ -134,39 +170,29 @@ module rv32g_hart
// select EX stage operands via forwarding from EX/MEM and MEM/WB pipeline registers
always_comb begin
- unique case (exmem_reg.wbsrc)
- WB_PC: exmem_value = exmem_reg.pc + 4;
- WB_ALU: exmem_value = exmem_reg.alu_out;
- WB_IMM: exmem_value = exmem_reg.imm;
- default: exmem_value = '0;
- endcase
-
- unique case (memwb_reg.wbsrc)
- WB_PC: memwb_value = memwb_reg.pc + 4;
- WB_ALU: memwb_value = memwb_reg.alu_out;
- WB_MEM: memwb_value = mem_mem_out;
- WB_IMM: memwb_value = memwb_reg.imm;
- default: memwb_value = '0;
+ unique case (idex_reg.ctrl.wbsrc)
+ WB_PC: ex_wb_value = idex_reg.pc + 4;
+ WB_ALU: ex_wb_value = ex_alu_out;
+ WB_IMM: ex_wb_value = idex_reg.imm;
+ default: ex_wb_value = '0;
endcase
- ex_rs1_val = (idex_reg.rs1_num == 0) ? '0 : dec_rs1_val;
- ex_rs2_val = (idex_reg.rs2_num == 0) ? '0 : dec_rs2_val;
+ memwb_value = memwb_reg.wbsrc == WB_MEM ? mem_mem_out :
+ memwb_reg.wb_value;
- if (idex_reg.rs1_num != 0) begin
- if (exmem_reg.valid && exmem_reg.wbsrc != WB_NONE &&
- exmem_reg.wbsrc != WB_MEM && exmem_reg.rd_num == idex_reg.rs1_num)
- ex_rs1_val = exmem_value;
- else if (wb_we && memwb_reg.rd_num == idex_reg.rs1_num)
- ex_rs1_val = memwb_value;
- end
+ unique case (idex_reg.rs1_forward)
+ FWD_REG: ex_rs1_val = idex_rs1_val;
+ FWD_EXMEM: ex_rs1_val = exmem_reg.wb_value;
+ FWD_MEMWB: ex_rs1_val = memwb_reg.wb_value;
+ default: ex_rs1_val = '0;
+ endcase
- if (idex_reg.rs2_num != 0) begin
- if (exmem_reg.valid && exmem_reg.wbsrc != WB_NONE &&
- exmem_reg.wbsrc != WB_MEM && exmem_reg.rd_num == idex_reg.rs2_num)
- ex_rs2_val = exmem_value;
- else if (wb_we && memwb_reg.rd_num == idex_reg.rs2_num)
- ex_rs2_val = memwb_value;
- end
+ unique case (idex_reg.rs2_forward)
+ FWD_REG: ex_rs2_val = idex_rs2_val;
+ FWD_EXMEM: ex_rs2_val = exmem_reg.wb_value;
+ FWD_MEMWB: ex_rs2_val = memwb_reg.wb_value;
+ default: ex_rs2_val = '0;
+ endcase
end
stage_execute u_execute(.clk,
@@ -179,18 +205,17 @@ module rv32g_hart
.op2src(idex_reg.ctrl.aluop2src),
.branchop(idex_reg.ctrl.branchop),
.out(ex_alu_out),
- .branch_taken(ex_branch_taken));
+ .branch_taken(ex_branch_taken),
+ .jalr_target(ex_jalr_target));
// EX/MEM pipeline register
- always_ff @(posedge clk) begin
+ always_ff @(posedge clk or negedge rstN) begin
if (!rstN) begin
exmem_reg <= '0;
- exmem_reg.pc <= BASE_ADDR;
end else begin
exmem_reg.valid <= idex_reg.valid;
exmem_reg.alu_out <= ex_alu_out;
exmem_reg.rs2_val <= ex_rs2_val;
- exmem_reg.pc <= idex_reg.pc;
- exmem_reg.imm <= idex_reg.imm;
+ exmem_reg.wb_value <= ex_wb_value;
exmem_reg.rd_num <= idex_reg.rd_num;
exmem_reg.branch_taken <= ex_branch_taken;
exmem_reg.branchop <= idex_reg.ctrl.branchop;
@@ -220,14 +245,14 @@ module rv32g_hart
end
PC_ALU: begin
redirect_valid = 1'b1;
- redirect_target = ex_alu_out & ~32'b1;
+ redirect_target = ex_jalr_target & ~32'b1;
end
default: ;
endcase
end
end
- next_pc = redirect_valid ? redirect_target : pc_reg + 4;
+ next_pc = redirect_valid ? redirect_target : pc_plus4;
end
// ==============
@@ -244,15 +269,13 @@ module rv32g_hart
.mem_out(mem_mem_out),
.dmem_addr, .dmem_rdata, .dmem_wdata, .dmem_we, .dmem_be);
// MEM/WB pipeline register
- always_ff @(posedge clk) begin
+ always_ff @(posedge clk or negedge rstN) begin
if (!rstN) begin
memwb_reg <= '0;
- memwb_reg.pc <= BASE_ADDR;
end else begin
memwb_reg.valid <= exmem_reg.valid;
memwb_reg.alu_out <= exmem_reg.alu_out;
- memwb_reg.pc <= exmem_reg.pc;
- memwb_reg.imm <= exmem_reg.imm;
+ memwb_reg.wb_value <= exmem_reg.wb_value;
memwb_reg.rd_num <= exmem_reg.rd_num;
memwb_reg.memop <= exmem_reg.memop;
memwb_reg.wbsrc <= exmem_reg.wbsrc;
@@ -264,20 +287,13 @@ module rv32g_hart
// === WRITEBACK ===
// =================
+ // assigns signals that are forwarded back to ID stage
always_comb begin
wb_valid = memwb_reg.valid &&
(memwb_reg.wbsrc != WB_MEM || dmem_valid);
wb_we = wb_valid && memwb_reg.wbsrc != WB_NONE && memwb_reg.rd_num != 0;
end
- stage_writeback u_wb(.clk, .rstN, .regfile, .valid(wb_valid),
- .wbsrc(memwb_reg.wbsrc),
- .rd_num(memwb_reg.rd_num),
- .pc(memwb_reg.pc),
- .imm(memwb_reg.imm),
- .alu_out(memwb_reg.alu_out),
- .mem_out(mem_mem_out));
-
assign exception = memwb_reg.valid ? memwb_reg.exception : EXC_NONE;
endmodule: rv32g_hart
diff --git a/rtl/rv32g_pkg.sv b/rtl/rv32g_pkg.sv
@@ -14,51 +14,51 @@ package rv32g_pkg;
JTYPE}
inst_type_t;
- typedef enum {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}
+ 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)
@@ -158,6 +158,11 @@ package rv32g_pkg;
WB_IMM // LUI writes back an immediate value
} wbsrc_t;
+ typedef enum logic [1:0] {FWD_REG,
+ FWD_EXMEM,
+ FWD_MEMWB
+ } forward_t;
+
// very rudimentary. privileged spec (chapter 3) defines this formally, but left to implementation
// on unprivileged spec.
typedef enum logic [1:0] {EXC_NONE,
@@ -182,7 +187,6 @@ package rv32g_pkg;
typedef struct packed {
logic valid;
reg_t pc;
- logic [31:0] inst;
} ifid_reg_t;
typedef struct packed {
@@ -190,8 +194,8 @@ package rv32g_pkg;
ctrl_t ctrl;
reg_t pc;
logic [31:0] imm;
- logic [4:0] rs1_num;
- logic [4:0] rs2_num;
+ forward_t rs1_forward;
+ forward_t rs2_forward;
logic [4:0] rd_num;
} idex_reg_t;
@@ -199,8 +203,7 @@ package rv32g_pkg;
logic valid;
reg_t alu_out;
reg_t rs2_val;
- reg_t pc;
- reg_t imm;
+ reg_t wb_value;
logic [4:0] rd_num;
logic branch_taken;
branchop_t branchop;
@@ -214,8 +217,7 @@ package rv32g_pkg;
typedef struct packed {
logic valid;
reg_t alu_out;
- reg_t pc;
- reg_t imm;
+ reg_t wb_value;
logic [4:0] rd_num;
memop_t memop;
wbsrc_t wbsrc;
diff --git a/rtl/stage_decode.sv b/rtl/stage_decode.sv
@@ -3,7 +3,6 @@ module stage_decode
(input logic clk,
input logic rstN,
input logic [31:0] inst,
- input logic [31:0] regfile [0:31],
input logic wb_we,
input logic [4:0] wb_rd_num,
input logic [31:0] wb_data,
@@ -23,6 +22,8 @@ module stage_decode
opcode_t opcode;
inst_type_t inst_type;
inst_name_t name;
+ reg_t regfile [0:31];
+ reg_t regfile_b [0:31];
always_comb begin
funct7 = inst[31:25];
@@ -201,30 +202,12 @@ module stage_decode
AUIPC: ;
JAL: ;
JALR: ;
- 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
+ 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.memop = MEM_LB;
LH: ctrl.memop = MEM_LH;
LW: ctrl.memop = MEM_LW;
@@ -279,10 +262,15 @@ module stage_decode
rs1_val <= '0;
rs2_val <= '0;
end else begin
+ if (wb_we) begin
+ regfile[wb_rd_num] <= wb_data;
+ regfile_b[wb_rd_num] <= wb_data;
+ end
+
rs1_val <= (rs1_num == 0) ? '0 :
((wb_we && wb_rd_num == rs1_num) ? wb_data : regfile[rs1_num]);
rs2_val <= (rs2_num == 0) ? '0 :
- ((wb_we && wb_rd_num == rs2_num) ? wb_data : regfile[rs2_num]);
+ ((wb_we && wb_rd_num == rs2_num) ? wb_data : regfile_b[rs2_num]);
end
end
diff --git a/rtl/stage_execute.sv b/rtl/stage_execute.sv
@@ -10,9 +10,10 @@ module stage_execute
input reg_t rs1_val,
input reg_t rs2_val,
output logic [31:0] out,
- output logic branch_taken);
+ output logic branch_taken,
+ output logic [31:0] jalr_target);
- reg_t a, b;
+ reg_t a, b;
logic [4:0] shamt;
always_comb begin
@@ -45,15 +46,17 @@ module stage_execute
ALU_SLTU: out = (a < b) ? 1 : 0;
endcase
+ jalr_target = rs1_val + imm;
+
// 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;
+ BRANCH_BEQ: branch_taken = rs1_val == rs2_val;
+ BRANCH_BNE: branch_taken = rs1_val != rs2_val;
+ BRANCH_BLT: branch_taken = $signed(rs1_val) < $signed(rs2_val);
+ BRANCH_BGE: branch_taken = $signed(rs1_val) >= $signed(rs2_val);
+ BRANCH_BLTU: branch_taken = rs1_val < rs2_val;
+ BRANCH_BGEU: branch_taken = rs1_val >= rs2_val;
endcase
end
diff --git a/rtl/stage_fetch.sv b/rtl/stage_fetch.sv
@@ -4,11 +4,11 @@ module stage_fetch
input logic flush_i, // from fence.i
input logic [31:0] pc,
input logic [31:0] imem_data,
+ input logic imem_valid,
output logic [31:0] imem_addr,
output logic [31:0] inst);
-// FIXME currently extra cycle from BSRAM is not handled
-//`define USE_ICACHE // TODO ifdef can be removed once pipelined with BSRAM
+`define USE_ICACHE
`ifdef USE_ICACHE
localparam CACHE_SIZE = 256; // words
@@ -20,6 +20,10 @@ module stage_fetch
logic [IDX_WIDTH-1:0] idx;
(* keep *) logic [TAG_WIDTH-1:0] tag_mem [0:CACHE_SIZE-1];
logic [TAG_WIDTH-1:0] tag_pc;
+ logic [31:0] cache_data;
+ logic [IDX_WIDTH-1:0] resp_idx;
+ logic [TAG_WIDTH-1:0] cache_tag, resp_tag;
+ logic cache_valid, resp_valid;
logic hit;
always_comb begin
@@ -27,21 +31,38 @@ module stage_fetch
idx = pc[IDX_WIDTH+1:2];
tag_pc = pc[31:IDX_WIDTH+2];
- hit = valid[idx] && (tag_mem[idx] == tag_pc);
- inst = (hit) ? cache[idx] : imem_data;
+ hit = resp_valid && cache_valid && (cache_tag == resp_tag);
+ inst = hit ? cache_data : imem_data;
end
always_ff @(posedge clk or negedge rstN) begin
if (!rstN) begin // async flush
for (int i = 0; i < CACHE_SIZE; i++) valid[i] <= 0;
+ cache_data <= '0;
+ cache_tag <= '0;
+ cache_valid <= 0;
+ resp_idx <= '0;
+ resp_tag <= '0;
+ resp_valid <= 0;
end else if (flush_i) begin // sync flush
for (int i = 0; i < CACHE_SIZE; i++) valid[i] <= 0;
- end else begin // cache miss :(
- if (!valid[idx] || cache[idx] !== imem_data) begin
- cache[idx] <= imem_data;
- tag_mem[idx] <= tag_pc;
- valid[idx] <= 1;
+ cache_valid <= 0;
+ resp_valid <= 0;
+ end else begin
+ // fill using the registered backing-memory response
+ if (resp_valid && imem_valid && !hit) begin
+ cache[resp_idx] <= imem_data;
+ tag_mem[resp_idx] <= resp_tag;
+ valid[resp_idx] <= 1;
end
+
+ // registered cache read for the current fetch request
+ cache_data <= cache[idx];
+ cache_tag <= tag_mem[idx];
+ cache_valid <= valid[idx];
+ resp_idx <= idx;
+ resp_tag <= tag_pc;
+ resp_valid <= 1;
end
end
diff --git a/rtl/tb_rv32g.sv b/rtl/tb_rv32g.sv
@@ -78,30 +78,30 @@ module tb_rv32g
always_ff @(posedge clk or negedge rstN) begin
if (!rstN)
seen_first_real_inst <= 0;
- else if (dut.fetch_inst != 32'b0)
+ else if (dut.ifid_inst != 32'b0)
seen_first_real_inst <= 1;
end
parameter int MAX_CYCLES = 10000;
always_ff @(posedge clk) begin
$display("IF: pc: %h, next_pc: %h, imem_valid: %d, imem[%h]: %h, inst: %h",
- dut.ifid_reg.pc, dut.next_pc, dut.imem_valid, dut.imem_addr, dut.imem_data, dut.fetch_inst);
+ dut.ifid_reg.pc, dut.next_pc, dut.imem_valid, dut.imem_addr, dut.imem_data, dut.ifid_inst);
$display("ID: ctrl: %h, imm: %h, rs1_val: %h, rs2_val: %h, rd_num: %d",
- dut.idex_reg.ctrl, dut.idex_reg.imm, dut.dec_rs1_val, dut.dec_rs2_val, dut.idex_reg.rd_num);
+ dut.idex_reg.ctrl, dut.idex_reg.imm, dut.idex_rs1_val, dut.idex_rs2_val, dut.idex_reg.rd_num);
$display("EX: op: %h, op1src: %d, op2src: %d, branchop: %d, out: %h, branch_taken: %d, rs1_val: %h, rs2_val: %h",
dut.idex_reg.ctrl.aluop, dut.idex_reg.ctrl.aluop1src, dut.idex_reg.ctrl.aluop2src,
dut.idex_reg.ctrl.branchop, dut.exmem_reg.alu_out, dut.exmem_reg.branch_taken,
dut.ex_rs1_val, dut.ex_rs2_val);
$display("MEM: memop: %h, dmem_addr: %h, dmem_rdata: %h, dmem_wdata: %h, dmem_we: %d, dmem_be: %b, mem_out: %h",
dut.exmem_reg.memop, dut.dmem_addr, dut.dmem_rdata, dut.dmem_wdata, dut.dmem_we, dut.dmem_be, dut.mem_mem_out);
- $display("WB: wbsrc: %d, rd_num: %d, pc: %h, imm: %h, alu_out: %h, mem_out: %h",
- dut.memwb_reg.wbsrc, dut.memwb_reg.rd_num, dut.memwb_reg.pc, dut.memwb_reg.imm,
+ $display("WB: wbsrc: %d, rd_num: %d, wb_value: %h, alu_out: %h, mem_out: %h",
+ dut.memwb_reg.wbsrc, dut.memwb_reg.rd_num, dut.memwb_reg.wb_value,
dut.memwb_reg.alu_out, dut.mem_mem_out);
if (dut.idex_reg.ctrl.exception != EXC_NONE) begin
unique case (dut.idex_reg.ctrl.exception)
EXC_NONE: ;
- EXC_ILL_INST: $display("Exception: illegal instruction, inst: %h", dut.fetch_inst);
+ EXC_ILL_INST: $display("Exception: illegal instruction, inst: %h", dut.ifid_inst);
EXC_CALL: $display("Exception: ECALL");
EXC_BKPT: $display("Exception: BREAK");
endcase
@@ -112,13 +112,13 @@ module tb_rv32g
$display("+++ %s: pass (%0d cycles) +++", rom_filename, cycle_cnt);
$finish();
end else if (exception == EXC_BKPT) begin
- $display("--- %s: fail (test %0d, %0d cycles) ---", rom_filename, dut.regfile[3], cycle_cnt);
+ $display("--- %s: fail (test %0d, %0d cycles) ---", rom_filename, dut.u_decode.regfile[3], cycle_cnt);
$finish();
end
- if (exception == EXC_ILL_INST && seen_first_real_inst && dut.fetch_inst == 32'b0) begin
+ if (exception == EXC_ILL_INST && seen_first_real_inst && dut.ifid_inst == 32'b0) begin
$display("!!! Program terminated (exception ILL_INST at PC %h), inst: %h !!!",
- pc_out, dut.fetch_inst);
+ pc_out, dut.ifid_inst);
$finish();
end
diff --git a/rtl/unified_memory.sv b/rtl/unified_memory.sv
@@ -15,25 +15,30 @@ module unified_memory
output logic [31:0] dmem_rdata,
output logic dmem_valid);
+ localparam IDX_WIDTH = $clog2(CACHE_DEPTH);
+
logic [31:0] mem [0:CACHE_DEPTH-1];
- logic [31:0] imem_idx, dmem_idx;
+ logic [IDX_WIDTH-1:0] imem_idx, dmem_idx;
+ logic imem_in_range, dmem_in_range;
// initial $readmemh("rom.txt", mem);
always_comb begin
- imem_idx = (imem_addr - BASE_ADDR) >> 2;
- dmem_idx = (dmem_addr - BASE_ADDR) >> 2;
+ imem_idx = imem_addr[IDX_WIDTH+1:2];
+ dmem_idx = dmem_addr[IDX_WIDTH+1:2];
+ imem_in_range = imem_addr[31:IDX_WIDTH+2] == BASE_ADDR[31:IDX_WIDTH+2];
+ dmem_in_range = dmem_addr[31:IDX_WIDTH+2] == BASE_ADDR[31:IDX_WIDTH+2];
end
always_ff @(posedge clk) begin
- imem_valid <= (imem_idx < CACHE_DEPTH) ? 1'b1 : 1'b0;
- dmem_valid <= (dmem_idx < CACHE_DEPTH) ? 1'b1 : 1'b0;
+ imem_valid <= imem_in_range;
+ dmem_valid <= dmem_in_range;
- imem_data <= (imem_idx < CACHE_DEPTH) ? mem[imem_idx] : '0;
- dmem_rdata <= (dmem_idx < CACHE_DEPTH) ? mem[dmem_idx] : '0;
+ imem_data <= imem_in_range ? mem[imem_idx] : '0;
+ dmem_rdata <= dmem_in_range ? mem[dmem_idx] : '0;
// for self-modifying memory
- if (dmem_we && dmem_idx < CACHE_DEPTH) begin
+ if (dmem_we && dmem_in_range) begin
for (int b = 0; b < 4; b++) begin
if (dmem_be[b]) mem[dmem_idx][b << 3 +: 8] <= dmem_wdata[b << 3 +: 8];
end