rv32g-core

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

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:
Mrtl/rv32g_hart.sv | 180+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mrtl/rv32g_pkg.sv | 106++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mrtl/stage_decode.sv | 40++++++++++++++--------------------------
Mrtl/stage_execute.sv | 19+++++++++++--------
Mrtl/stage_fetch.sv | 39++++++++++++++++++++++++++++++---------
Mrtl/tb_rv32g.sv | 18+++++++++---------
Mrtl/unified_memory.sv | 21+++++++++++++--------
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