diff options
Diffstat (limited to 'rv64cpu')
| -rw-r--r-- | rv64cpu/rtl/core/csr_unit.sv | 118 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/decoder.sv | 57 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/ex_mem_reg.sv | 5 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/hazard_unit.sv | 92 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/id_ex_reg.sv | 9 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/id_stage.sv | 2 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/if_stage.sv | 23 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/mem_stage.sv | 33 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/mem_wb_reg.sv | 21 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/richard_core.sv | 161 | ||||
| -rw-r--r-- | rv64cpu/rtl/core/wb_stage.sv | 24 | ||||
| -rw-r--r-- | rv64cpu/tb/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | rv64cpu/tb/core/tb_core.cc | 425 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_csr.cc | 354 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_decoder.cc | 9 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_hazard_unit.cc | 46 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_if_stage.cc | 35 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_trap_ctrl.cc | 16 | ||||
| -rw-r--r-- | rv64cpu/tb/unit/tb_wb_stage.cc | 33 |
19 files changed, 1269 insertions, 201 deletions
diff --git a/rv64cpu/rtl/core/csr_unit.sv b/rv64cpu/rtl/core/csr_unit.sv index 1749790..0427eeb 100644 --- a/rv64cpu/rtl/core/csr_unit.sv +++ b/rv64cpu/rtl/core/csr_unit.sv @@ -32,12 +32,13 @@ module csr_unit input logic [2:0] csr_op, input logic [11:0] csr_addr, input logic [XLEN-1:0] csr_rs1_data, - input logic trap_illegal_instr, - input logic trap_is_ecall, - input logic trap_is_ebreak, - input logic trap_is_mret, - input logic trap_is_sret, - input logic [XLEN-1:0] trap_epc, + input logic trap_ctrl_trigger, + input logic trap_ctrl_to_s_mode, + input logic [XLEN-1:0] trap_ctrl_mepc, + input logic [XLEN-1:0] trap_ctrl_mcause, + input logic [XLEN-1:0] trap_ctrl_mtval, + input logic trap_ctrl_mret_en, + input logic trap_ctrl_sret_en, output logic [XLEN-1:0] csr_rdata, output logic [XLEN-1:0] csr_mstatus, output logic [XLEN-1:0] csr_sstatus, @@ -51,6 +52,8 @@ module csr_unit output logic [1:0] csr_priv_mode, output logic [XLEN-1:0] csr_trap_vector, output logic csr_trap_to_s_mode, + output logic [XLEN-1:0] csr_trap_vector_next, + output logic csr_trap_to_s_mode_next, output logic csr_illegal_access ); @@ -110,16 +113,17 @@ module csr_unit logic is_clear_cmd; logic has_non_zero_data; logic csr_write_en; - logic trap_event; - logic [5:0] trap_cause_index; - logic trap_delegated_to_s; - logic [XLEN-1:0] trap_cause_value; logic [XLEN-1:0] trap_vector_target; - logic [XLEN-1:0] trap_vector_q; logic csr_addr_supported; logic csr_write_illegal; logic [XLEN-1:0] csr_read_data; + logic [XLEN-1:0] trap_vector_q; logic trap_to_s_q; + logic trap_taken; + logic trap_taken_to_s_mode; + logic [XLEN-1:0] trap_taken_mepc; + logic [XLEN-1:0] trap_taken_mcause; + logic [XLEN-1:0] trap_taken_mtval; // Canonicalize addresses, decode operations, and prepare CSR/trap metadata. assign csr_addr_canonical = canonical_addr(csr_addr); @@ -129,27 +133,31 @@ module csr_unit assign has_non_zero_data = |csr_rs1_data; assign csr_addr_supported = csr_is_supported(csr_addr_canonical); assign csr_write_en = csr_req & csr_addr_supported & (is_write_cmd | ((is_set_cmd | is_clear_cmd) & has_non_zero_data)); - assign trap_event = trap_illegal_instr | trap_is_ecall | trap_is_ebreak; - assign trap_cause_value = compute_trap_cause(priv_mode_q, trap_illegal_instr, trap_is_ecall, trap_is_ebreak); - assign trap_cause_index = trap_cause_value[5:0]; - assign trap_delegated_to_s = trap_event && (priv_mode_q != PRIV_MODE_M) && medeleg_reg[trap_cause_index]; - assign trap_vector_target = trap_delegated_to_s ? stvec_reg : mtvec_reg; - assign csr_read_data = read_csr(csr_addr_canonical); - assign csr_rdata = csr_read_data; - assign csr_mstatus = mstatus_reg; - assign csr_sstatus = sstatus_reg; - assign csr_medeleg = medeleg_reg; - assign csr_mideleg = mideleg_reg; - assign csr_mtvec = mtvec_reg; - assign csr_stvec = stvec_reg; - assign csr_mepc = mepc_reg; - assign csr_sepc = sepc_reg; - assign csr_satp = satp_reg; - assign csr_priv_mode = priv_mode_q; - assign csr_trap_vector = trap_vector_q; - assign csr_trap_to_s_mode = trap_to_s_q; - assign csr_write_illegal = (csr_req && (is_write_cmd || is_set_cmd || is_clear_cmd) && !csr_is_writable(csr_addr_canonical)); - assign csr_illegal_access = csr_req && (!csr_addr_supported || csr_write_illegal); + assign trap_taken = trap_ctrl_trigger; + assign trap_taken_to_s_mode = trap_ctrl_to_s_mode; + assign trap_taken_mepc = trap_ctrl_mepc; + assign trap_taken_mcause = trap_ctrl_mcause; + assign trap_taken_mtval = trap_ctrl_mtval; + + assign trap_vector_target = trap_taken_to_s_mode ? stvec_reg : mtvec_reg; + assign csr_read_data = read_csr(csr_addr_canonical); + assign csr_rdata = csr_read_data; + assign csr_mstatus = mstatus_reg; + assign csr_sstatus = sstatus_reg; + assign csr_medeleg = medeleg_reg; + assign csr_mideleg = mideleg_reg; + assign csr_mtvec = mtvec_reg; + assign csr_stvec = stvec_reg; + assign csr_mepc = mepc_reg; + assign csr_sepc = sepc_reg; + assign csr_satp = satp_reg; + assign csr_priv_mode = priv_mode_q; + assign csr_trap_vector = trap_vector_q; + assign csr_trap_to_s_mode = trap_to_s_q; + assign csr_trap_vector_next = trap_vector_target; + assign csr_trap_to_s_mode_next = trap_taken_to_s_mode; + assign csr_write_illegal = (csr_req && (is_write_cmd || is_set_cmd || is_clear_cmd) && !csr_is_writable(csr_addr_canonical)); + assign csr_illegal_access = csr_req && (!csr_addr_supported || csr_write_illegal); // Main CSR state machine: counts cycles, processes CSR writes, and reacts to traps/returns. always_ff @(posedge clk) begin @@ -186,7 +194,7 @@ module csr_unit end else begin mcycle_reg <= mcycle_reg + 64'd1; time_reg <= time_reg + 64'd1; - if (!trap_event) begin + if (!trap_taken) begin minstret_reg <= minstret_reg + 64'd1; end @@ -245,11 +253,11 @@ module csr_unit endcase end - if (trap_event) begin + if (trap_taken) begin trap_vector_q <= trap_vector_target; - trap_to_s_q <= trap_delegated_to_s; + trap_to_s_q <= trap_taken_to_s_mode; // Update privilege-specific CSRs based on whether the trap was delegated. - if (trap_delegated_to_s) begin + if (trap_taken_to_s_mode) begin logic [XLEN-1:0] next_sstatus; next_sstatus = sstatus_reg; next_sstatus[SSTATUS_SPIE_BIT] = sstatus_reg[SSTATUS_SIE_BIT]; @@ -257,9 +265,9 @@ module csr_unit next_sstatus[SSTATUS_SPP_BIT] = (priv_mode_q == PRIV_MODE_S); sstatus_reg <= next_sstatus; mstatus_reg <= apply_sstatus_to_mstatus(mstatus_reg, next_sstatus); - sepc_reg <= trap_epc; - scause_reg <= trap_cause_value; - stval_reg <= trap_illegal_instr ? trap_epc : '0; + sepc_reg <= trap_taken_mepc; + scause_reg <= trap_taken_mcause; + stval_reg <= trap_taken_mtval; priv_mode_q <= PRIV_MODE_S; end else begin logic [XLEN-1:0] next_mstatus; @@ -269,13 +277,13 @@ module csr_unit next_mstatus[MSTATUS_MPP_HI:MSTATUS_MPP_LO] = encode_priv_mode(priv_mode_q); mstatus_reg <= next_mstatus; sstatus_reg <= mirror_mstatus_to_sstatus(sstatus_reg, next_mstatus); - mepc_reg <= trap_epc; - mcause_reg <= trap_cause_value; - mtval_reg <= trap_illegal_instr ? trap_epc : '0; + mepc_reg <= trap_taken_mepc; + mcause_reg <= trap_taken_mcause; + mtval_reg <= trap_taken_mtval; priv_mode_q <= PRIV_MODE_M; end end else begin - if (trap_is_mret) begin + if (trap_ctrl_mret_en) begin priv_mode_t return_mode; logic [XLEN-1:0] next_mstatus; return_mode = decode_priv_mode(mstatus_reg[MSTATUS_MPP_HI:MSTATUS_MPP_LO]); @@ -288,7 +296,7 @@ module csr_unit priv_mode_q <= return_mode; end - if (trap_is_sret) begin + if (trap_ctrl_sret_en) begin logic [XLEN-1:0] next_sstatus; next_sstatus = sstatus_reg; next_sstatus[SSTATUS_SIE_BIT] = sstatus_reg[SSTATUS_SPIE_BIT]; @@ -432,28 +440,6 @@ module csr_unit endcase endfunction - function automatic logic [XLEN-1:0] compute_trap_cause( - input priv_mode_t mode, - input logic illegal_instr, - input logic is_ecall, - input logic is_ebreak - ); - logic [XLEN-1:0] cause_val; - cause_val = '0; - if (illegal_instr) begin - cause_val = EXC_INST_ILLEGAL; - end else if (is_ebreak) begin - cause_val = EXC_BREAKPOINT; - end else if (is_ecall) begin - case (mode) - PRIV_MODE_U: cause_val = EXC_ECALL_FROM_U; - PRIV_MODE_S: cause_val = EXC_ECALL_FROM_S; - default: cause_val = EXC_ECALL_FROM_M; - endcase - end - return cause_val; - endfunction - function automatic logic [XLEN-1:0] read_csr(input logic [11:0] addr); logic [XLEN-1:0] data_out; unique case (addr) diff --git a/rv64cpu/rtl/core/decoder.sv b/rv64cpu/rtl/core/decoder.sv index c948646..8e5e18f 100644 --- a/rv64cpu/rtl/core/decoder.sv +++ b/rv64cpu/rtl/core/decoder.sv @@ -23,6 +23,7 @@ module decoder import rv64_pkg::*; ( input logic [31:0] instr, // 32-bit instruction + input logic [1:0] priv_mode, // Current privilege mode from CSR // Register File output logic [4:0] rs1_addr, // Source register 1 address @@ -73,6 +74,12 @@ module decoder // ============================ // Instruction Field Extraction // ============================ + typedef enum logic [1:0] { + PRIV_MODE_U = 2'b00, + PRIV_MODE_S = 2'b01, + PRIV_MODE_M = 2'b11 + } priv_mode_t; + logic [6:0] opcode; logic [2:0] funct3; logic [6:0] funct7; @@ -88,6 +95,7 @@ module decoder // Immediate Generation // ============================ logic [XLEN-1:0] imm_i, imm_s, imm_b, imm_u, imm_j; + logic [11:0] csr_addr_field; // I-type: instr[31:20] assign imm_i = {{52{instr[31]}}, instr[31:20]}; @@ -103,6 +111,26 @@ module decoder // J-type: {instr[31], instr[19:12], instr[20], instr[30:21], 1'b0} assign imm_j = {{43{instr[31]}}, instr[31], instr[19:12], instr[20], instr[30:21], 1'b0}; + assign csr_addr_field = instr[31:20]; + + function automatic int priv_rank(logic [1:0] mode); + case (mode) + PRIV_MODE_U: return 0; + PRIV_MODE_S: return 1; + 2'b10: return 2; // Reserved/H-mode, treat as higher than S + default: return 3; // Machine mode (or anything else) highest + endcase + endfunction + + function automatic logic priv_ge(logic [1:0] cur, logic [1:0] req); + return priv_rank(cur) >= priv_rank(req); + endfunction + + function automatic logic csr_access_allowed(logic [1:0] cur, logic [11:0] addr); + logic [1:0] req_priv; + req_priv = addr[9:8]; + return priv_ge(cur, req_priv); + endfunction // ============================ // Main Decode Logic @@ -337,14 +365,28 @@ module decoder case ({funct7, instr[24:20]}) // {funct7, rs2} 12'b0000000_00000: is_ecall = 1'b1; // ECALL 12'b0000000_00001: is_ebreak = 1'b1; // EBREAK - 12'b0011000_00010: is_mret = 1'b1; // MRET - 12'b0001000_00010: is_sret = 1'b1; // SRET + 12'b0011000_00010: begin // MRET + if (priv_mode == PRIV_MODE_M) + is_mret = 1'b1; + else + illegal_instr = 1'b1; + end + 12'b0001000_00010: begin // SRET + if (priv_ge(priv_mode, PRIV_MODE_S)) + is_sret = 1'b1; + else + illegal_instr = 1'b1; + end 12'b0001000_00101: is_wfi = 1'b1; // WFI default: begin - if (funct7 == 7'b0001001) - is_sfence_vma = 1'b1; // SFENCE.VMA - else + if (funct7 == 7'b0001001) begin + if (priv_ge(priv_mode, PRIV_MODE_S)) + is_sfence_vma = 1'b1; // SFENCE.VMA + else + illegal_instr = 1'b1; + end else begin illegal_instr = 1'b1; + end end endcase end else begin @@ -355,6 +397,11 @@ module decoder is_csr = 1'b1; csr_op = funct3; imm = imm_i; // CSR address = instr[31:20] (reuse imm_i path) + if (!csr_access_allowed(priv_mode, csr_addr_field)) begin + reg_write_en = 1'b0; + is_csr = 1'b0; + illegal_instr = 1'b1; + end end end diff --git a/rv64cpu/rtl/core/ex_mem_reg.sv b/rv64cpu/rtl/core/ex_mem_reg.sv index 2cc5e9e..783dced 100644 --- a/rv64cpu/rtl/core/ex_mem_reg.sv +++ b/rv64cpu/rtl/core/ex_mem_reg.sv @@ -44,6 +44,7 @@ module ex_mem_reg input logic [4:0] ex_rs1_addr, // Serves as zimm for CSRRWI/SI/CI input logic [XLEN-1:0] ex_pc, // PC passed along for exceptions/CSRs input logic [XLEN-1:0] ex_imm, // Imm passed for CSR writes + input logic [INST_WIDTH-1:0] ex_instr, // Memory Control input logic ex_is_load, @@ -81,6 +82,7 @@ module ex_mem_reg output logic [4:0] mem_rs1_addr, output logic [XLEN-1:0] mem_pc, output logic [XLEN-1:0] mem_imm, + output logic [INST_WIDTH-1:0] mem_instr, // Memory Control output logic mem_is_load, @@ -108,6 +110,7 @@ module ex_mem_reg always_ff @(posedge clk) begin if (rst) begin + mem_instr <= '0; mem_alu_result <= '0; mem_branch_taken <= '0; mem_branch_target <= '0; @@ -139,6 +142,7 @@ module ex_mem_reg mem_is_fence_i <= '0; end else if (flush_en) begin + mem_instr <= '0; // Critical control signals must be zeroed out mem_branch_taken <= '0; mem_is_load <= '0; @@ -169,6 +173,7 @@ module ex_mem_reg mem_rs1_addr <= ex_rs1_addr; mem_pc <= ex_pc; mem_imm <= ex_imm; + mem_instr <= ex_instr; mem_is_load <= ex_is_load; mem_is_store <= ex_is_store; diff --git a/rv64cpu/rtl/core/hazard_unit.sv b/rv64cpu/rtl/core/hazard_unit.sv index 2798116..aef550c 100644 --- a/rv64cpu/rtl/core/hazard_unit.sv +++ b/rv64cpu/rtl/core/hazard_unit.sv @@ -37,6 +37,7 @@ module hazard_unit input logic ex_reg_write_en, input logic ex_is_load, input logic ex_branch_taken, + input logic [XLEN-1:0] ex_branch_target, // ----------------------- // Inputs from MEM Stage (EX/MEM register outputs) @@ -46,6 +47,13 @@ module hazard_unit input logic mem_stall_req, // D-Cache miss or memory busy // ----------------------- + // Trap Controller Inputs + // ----------------------- + input logic trap_flush_req, + input logic trap_pc_sel, + input logic [XLEN-1:0] trap_target_pc, + + // ----------------------- // Inputs from WB Stage (MEM/WB register outputs) // ----------------------- input logic [4:0] wb_rd_addr, @@ -66,7 +74,14 @@ module hazard_unit output logic id_ex_stall_en, output logic ex_mem_stall_en, output logic if_id_flush_en, - output logic id_ex_flush_en + output logic id_ex_flush_en, + + // ----------------------- + // Outputs: IF-stage PC redirect controls + // ----------------------- + output logic pc_redirect_sel, + output logic [XLEN-1:0] pc_redirect_target, + output logic pc_redirect_is_trap ); // ----------------------- @@ -112,40 +127,63 @@ module hazard_unit ((ex_rd_addr == id_rs1_addr) || (ex_rd_addr == id_rs2_addr)); // ----------------------- + // PC Redirect Selection (Trap has top priority) + // ----------------------- + always_comb begin + pc_redirect_sel = 1'b0; + pc_redirect_target = '0; + pc_redirect_is_trap = 1'b0; + + if (trap_pc_sel) begin + pc_redirect_sel = 1'b1; + pc_redirect_target = trap_target_pc; + pc_redirect_is_trap = 1'b1; + end else if (ex_branch_taken) begin + pc_redirect_sel = 1'b1; + pc_redirect_target = ex_branch_target; + end + end + + // ----------------------- // Stall and Flush Signal Generation // ----------------------- always_comb begin // Defaults: no stalls, no flushes - if_stall_en = 1'b0; - if_id_stall_en = 1'b0; - id_ex_stall_en = 1'b0; + if_stall_en = 1'b0; + if_id_stall_en = 1'b0; + id_ex_stall_en = 1'b0; ex_mem_stall_en = 1'b0; - if_id_flush_en = 1'b0; - id_ex_flush_en = 1'b0; - - // (1) Memory stall (D-Cache miss): freeze the entire pipeline - if (mem_stall_req) begin - if_stall_en = 1'b1; - if_id_stall_en = 1'b1; - id_ex_stall_en = 1'b1; - ex_mem_stall_en = 1'b1; - end + if_id_flush_en = 1'b0; + id_ex_flush_en = 1'b0; - // (2) Load-Use stall: freeze IF and IF/ID, insert bubble into ID/EX - if (load_use_stall && !mem_stall_req) begin - if_stall_en = 1'b1; - if_id_stall_en = 1'b1; - id_ex_flush_en = 1'b1; - end - - // (3) Branch/jump misprediction: flush IF/ID and ID/EX - // Branch flush overrides load-use stall (the stalled instruction is invalid anyway) - if (ex_branch_taken) begin + if (trap_flush_req) begin if_id_flush_en = 1'b1; id_ex_flush_en = 1'b1; - // Cancel load-use stall if branch is also taken - if_stall_en = 1'b0; - if_id_stall_en = 1'b0; + end else begin + // (1) Memory stall (D-Cache miss): freeze the entire pipeline + if (mem_stall_req) begin + if_stall_en = 1'b1; + if_id_stall_en = 1'b1; + id_ex_stall_en = 1'b1; + ex_mem_stall_en = 1'b1; + end + + // (2) Load-Use stall: freeze IF and IF/ID, insert bubble into ID/EX + if (load_use_stall && !mem_stall_req) begin + if_stall_en = 1'b1; + if_id_stall_en = 1'b1; + id_ex_flush_en = 1'b1; + end + + // (3) Branch/jump misprediction: flush IF/ID and ID/EX + // Branch flush overrides load-use stall (the stalled instruction is invalid anyway) + if (ex_branch_taken) begin + if_id_flush_en = 1'b1; + id_ex_flush_en = 1'b1; + // Cancel load-use stall if branch is also taken + if_stall_en = 1'b0; + if_id_stall_en = 1'b0; + end end end diff --git a/rv64cpu/rtl/core/id_ex_reg.sv b/rv64cpu/rtl/core/id_ex_reg.sv index e88ce17..9375bb4 100644 --- a/rv64cpu/rtl/core/id_ex_reg.sv +++ b/rv64cpu/rtl/core/id_ex_reg.sv @@ -32,6 +32,9 @@ module id_ex_reg // Inputs from ID Stage + // Instruction payload + input logic [INST_WIDTH-1:0] id_instr, + // Data/Operands input logic [XLEN-1:0] id_pc, input logic [XLEN-1:0] id_rs1_rdata, @@ -73,6 +76,9 @@ module id_ex_reg // Outputs to EX Stage + // Instruction payload + output logic [INST_WIDTH-1:0] ex_instr, + // Data/Operands output logic [XLEN-1:0] ex_pc, output logic [XLEN-1:0] ex_rs1_rdata, @@ -114,6 +120,7 @@ module id_ex_reg always_ff @(posedge clk) begin if (rst) begin + ex_instr <= '0; ex_pc <= '0; ex_rs1_rdata <= '0; ex_rs2_rdata <= '0; @@ -145,6 +152,7 @@ module id_ex_reg ex_is_fence <= '0; ex_is_fence_i <= '0; end else if (flush_en) begin + ex_instr <= '0; ex_alu_op <= ALU_ADD; // NOP is effectively ADD x0, x0, 0 ex_alu_src1_sel <= '0; ex_alu_src2_sel <= '0; @@ -170,6 +178,7 @@ module id_ex_reg ex_is_fence <= '0; ex_is_fence_i <= '0; end else if (~stall_en) begin + ex_instr <= id_instr; ex_pc <= id_pc; ex_rs1_rdata <= id_rs1_rdata; ex_rs2_rdata <= id_rs2_rdata; diff --git a/rv64cpu/rtl/core/id_stage.sv b/rv64cpu/rtl/core/id_stage.sv index 27d3daf..6a8bab9 100644 --- a/rv64cpu/rtl/core/id_stage.sv +++ b/rv64cpu/rtl/core/id_stage.sv @@ -26,6 +26,7 @@ module id_stage // Inputs from IF Stage (via IF/ID Pipeline Register) input logic [XLEN-1:0] if_pc, // PC of the current instruction input logic [INST_WIDTH-1:0] if_instr, // The 32-bit instruction fetched + input logic [1:0] csr_priv_mode, // Current privilege mode from CSR // Write-back Inputs from WB Stage (to RegFile) input logic wb_reg_write_en, // Register write enable from WB stage @@ -85,6 +86,7 @@ module id_stage decoder u_decoder ( .instr (if_instr), + .priv_mode (csr_priv_mode), // Outputs to RegFile read ports .rs1_addr (rs1_addr), diff --git a/rv64cpu/rtl/core/if_stage.sv b/rv64cpu/rtl/core/if_stage.sv index ebb589c..af6b34a 100644 --- a/rv64cpu/rtl/core/if_stage.sv +++ b/rv64cpu/rtl/core/if_stage.sv @@ -23,10 +23,11 @@ module if_stage input logic clk, input logic rst, - // Pipeline Control Signals (from Hazard Unit & EX Stage) - input logic stall_en, // Freeze PC (stall), 0: Normal operation - input logic ex_branch_taken, // Branch taken signal from EX Stage - input logic [XLEN-1:0] ex_branch_target, // Branch target address from EX Stage + // Pipeline Control Signals (from Hazard Unit & Trap Controller) + input logic stall_en, // Freeze PC (stall), 0: Normal operation + input logic pc_redirect_sel, // Branch/Trap redirect request + input logic [XLEN-1:0] pc_redirect_target,// Redirect target address + input logic pc_redirect_is_trap, // 1: Redirect request comes from trap controller output logic [XLEN-1:0] if_pc ); @@ -38,19 +39,21 @@ module if_stage logic [XLEN-1:0] next_pc; always_comb begin - if (ex_branch_taken) begin - next_pc = ex_branch_target; - end else begin - next_pc = pc_reg + 64'd4; - end + next_pc = pc_reg + 64'd4; end // Sequential Logic: Update PC Register always_ff @(posedge clk) begin if (rst) begin pc_reg <= 64'h0000_0000_8000_0000; + end else if (pc_redirect_sel && pc_redirect_is_trap) begin + pc_reg <= pc_redirect_target; end else if (~stall_en) begin - pc_reg <= next_pc; + if (pc_redirect_sel) begin + pc_reg <= pc_redirect_target; + end else begin + pc_reg <= next_pc; + end end end diff --git a/rv64cpu/rtl/core/mem_stage.sv b/rv64cpu/rtl/core/mem_stage.sv index a63a456..558d6d6 100644 --- a/rv64cpu/rtl/core/mem_stage.sv +++ b/rv64cpu/rtl/core/mem_stage.sv @@ -31,6 +31,7 @@ module mem_stage input logic [4:0] mem_rs1_addr, input logic [XLEN-1:0] mem_pc, input logic [XLEN-1:0] mem_imm, + input logic [INST_WIDTH-1:0] mem_instr, // Memory Control input logic mem_is_load, @@ -69,6 +70,7 @@ module mem_stage output logic [XLEN-1:0] mem_wb_alu_result, output logic [XLEN-1:0] mem_wb_mem_rdata, output logic [XLEN-1:0] mem_wb_pc, + output logic [INST_WIDTH-1:0] mem_wb_instr, output logic mem_wb_reg_write_en, output logic [4:0] mem_wb_rd_addr, @@ -85,6 +87,9 @@ module mem_stage output logic mem_wb_is_ebreak, output logic mem_wb_is_mret, output logic mem_wb_is_sret, + output logic mem_wb_load_fault, + output logic mem_wb_store_fault, + output logic [XLEN-1:0] mem_wb_fault_addr, // Control to Hazard Unit / Stall Logic output logic mem_stall_req // Stall pipeline if D-Cache doesn't respond @@ -93,11 +98,31 @@ module mem_stage logic [XLEN-1:0] load_data_aligned; logic [XLEN-1:0] store_data_aligned; logic [7:0] write_strobe; + logic load_addr_misaligned; + logic store_addr_misaligned; + + function automatic logic addr_misaligned( + input logic [2:0] size, + input logic [XLEN-1:0] addr + ); + logic result; + case (size) + 3'b000, 3'b100: result = 1'b0; // Byte L/S + 3'b001, 3'b101: result = addr[0]; // Halfword requires bit0 = 0 + 3'b010, 3'b110: result = |addr[1:0]; // Word requires bits[1:0] = 0 + 3'b011: result = |addr[2:0]; // Doubleword requires bits[2:0] = 0 + default: result = 1'b0; + endcase + return result; + endfunction + + assign load_addr_misaligned = mem_is_load && addr_misaligned(mem_mem_size, mem_alu_result); + assign store_addr_misaligned = mem_is_store && addr_misaligned(mem_mem_size, mem_alu_result); // -------------------------- // Memory Request Logic - assign dmem_read_req = mem_is_load; - assign dmem_write_req = mem_is_store; + assign dmem_read_req = mem_is_load & ~load_addr_misaligned; + assign dmem_write_req = mem_is_store & ~store_addr_misaligned; assign dmem_addr = mem_alu_result; // ALU result is the computed address // -------------------------- @@ -194,6 +219,7 @@ module mem_stage assign mem_wb_alu_result = mem_alu_result; assign mem_wb_mem_rdata = load_data_aligned; assign mem_wb_pc = mem_pc; + assign mem_wb_instr = mem_instr; assign mem_wb_reg_write_en = mem_reg_write_en; assign mem_wb_rd_addr = mem_rd_addr; @@ -210,5 +236,8 @@ module mem_stage assign mem_wb_is_ebreak = mem_is_ebreak; assign mem_wb_is_mret = mem_is_mret; assign mem_wb_is_sret = mem_is_sret; + assign mem_wb_load_fault = load_addr_misaligned; + assign mem_wb_store_fault = store_addr_misaligned; + assign mem_wb_fault_addr = (load_addr_misaligned | store_addr_misaligned) ? mem_alu_result : '0; endmodule diff --git a/rv64cpu/rtl/core/mem_wb_reg.sv b/rv64cpu/rtl/core/mem_wb_reg.sv index a70057c..6e8e13f 100644 --- a/rv64cpu/rtl/core/mem_wb_reg.sv +++ b/rv64cpu/rtl/core/mem_wb_reg.sv @@ -36,6 +36,7 @@ module mem_wb_reg input logic [XLEN-1:0] mem_pc, input logic [XLEN-1:0] mem_alu_result, input logic [XLEN-1:0] mem_mem_rdata, + input logic [INST_WIDTH-1:0] mem_instr, // Write back control input logic mem_reg_write_en, @@ -53,6 +54,9 @@ module mem_wb_reg input logic mem_is_ebreak, input logic mem_is_mret, input logic mem_is_sret, + input logic mem_load_fault, + input logic mem_store_fault, + input logic [XLEN-1:0] mem_fault_addr, // ----------------------- // Outputs to WB Stage @@ -61,6 +65,7 @@ module mem_wb_reg output logic [XLEN-1:0] wb_pc, output logic [XLEN-1:0] wb_alu_result, output logic [XLEN-1:0] wb_mem_rdata, + output logic [INST_WIDTH-1:0] wb_instr, // Write back control output logic wb_reg_write_en, @@ -77,7 +82,10 @@ module mem_wb_reg output logic wb_is_ecall, output logic wb_is_ebreak, output logic wb_is_mret, - output logic wb_is_sret + output logic wb_is_sret, + output logic wb_load_fault, + output logic wb_store_fault, + output logic [XLEN-1:0] wb_fault_addr ); // Sequential Logic: State Update @@ -86,6 +94,7 @@ module mem_wb_reg wb_pc <= '0; wb_alu_result <= '0; wb_mem_rdata <= '0; + wb_instr <= '0; wb_reg_write_en <= '0; wb_rd_addr <= '0; wb_wb_sel <= '0; @@ -99,6 +108,9 @@ module mem_wb_reg wb_is_ebreak <= '0; wb_is_mret <= '0; wb_is_sret <= '0; + wb_load_fault <= '0; + wb_store_fault <= '0; + wb_fault_addr <= '0; end else if (flush_en) begin // Target specific control signals to clear. Leave datapath elements intact // to avoid extreme power consumption from massive flip-flop state changes array. @@ -109,10 +121,14 @@ module mem_wb_reg wb_is_ebreak <= '0; wb_is_mret <= '0; wb_is_sret <= '0; + wb_load_fault <= '0; + wb_store_fault <= '0; + wb_fault_addr <= '0; end else if (!stall_en) begin wb_pc <= mem_pc; wb_alu_result <= mem_alu_result; wb_mem_rdata <= mem_mem_rdata; + wb_instr <= mem_instr; wb_reg_write_en <= mem_reg_write_en; wb_rd_addr <= mem_rd_addr; wb_wb_sel <= mem_wb_sel; @@ -126,6 +142,9 @@ module mem_wb_reg wb_is_ebreak <= mem_is_ebreak; wb_is_mret <= mem_is_mret; wb_is_sret <= mem_is_sret; + wb_load_fault <= mem_load_fault; + wb_store_fault <= mem_store_fault; + wb_fault_addr <= mem_fault_addr; end end diff --git a/rv64cpu/rtl/core/richard_core.sv b/rv64cpu/rtl/core/richard_core.sv index ae73e77..ad75cc8 100644 --- a/rv64cpu/rtl/core/richard_core.sv +++ b/rv64cpu/rtl/core/richard_core.sv @@ -34,8 +34,15 @@ module richard_core output logic [63:0] dmem_addr, output logic [63:0] dmem_wdata, output logic [2:0] dmem_size, - input logic [63:0] dmem_rdata + input logic [63:0] dmem_rdata, + + // Interface 3: External Interrupt Sources + input logic ext_timer_int, + input logic ext_software_int, + input logic ext_external_int ); + localparam int CSR_MSTATUS_MIE_BIT = 3; + // ----------------------- // Internal Wire Declarations // ----------------------- @@ -44,6 +51,8 @@ module richard_core logic [1:0] hu_forward_a_sel, hu_forward_b_sel; logic hu_if_stall_en, hu_if_id_stall_en, hu_id_ex_stall_en, hu_ex_mem_stall_en; logic hu_if_id_flush_en, hu_id_ex_flush_en; + logic hu_pc_redirect_sel, hu_pc_redirect_is_trap; + logic [XLEN-1:0] hu_pc_redirect_target; // (2) IF Stage Outputs logic [XLEN-1:0] if_pc_out; @@ -74,6 +83,7 @@ module richard_core // (5) ID/EX Pipeline Register Outputs logic [XLEN-1:0] ex_pc_in; + logic [INST_WIDTH-1:0] ex_instr_in; logic [XLEN-1:0] ex_rs1_rdata_in, ex_rs2_rdata_in; logic [4:0] ex_rs1_addr_in, ex_rs2_addr_in; logic [XLEN-1:0] ex_imm_in; @@ -99,6 +109,7 @@ module richard_core // (7) EX/MEM Pipeline Register Outputs logic [XLEN-1:0] mem_pc_in; + logic [INST_WIDTH-1:0] mem_instr_in; logic [XLEN-1:0] mem_alu_result_in; logic mem_branch_taken_in; logic [XLEN-1:0] mem_branch_target_in; @@ -119,6 +130,7 @@ module richard_core logic [XLEN-1:0] mw_reg_alu_result_in; logic [XLEN-1:0] mw_reg_mem_rdata_in; logic [XLEN-1:0] mw_reg_pc_in; + logic [INST_WIDTH-1:0] mw_reg_instr_in; logic [7:0] mw_reg_dmem_wstrb_in; logic mw_reg_reg_write_en_in; logic [4:0] mw_reg_rd_addr_in; @@ -129,12 +141,15 @@ module richard_core logic [4:0] mw_reg_rs1_addr_in; logic [XLEN-1:0] mw_reg_imm_in; logic mw_reg_illegal_instr_in, mw_reg_is_ecall_in, mw_reg_is_ebreak_in, mw_reg_is_mret_in, mw_reg_is_sret_in; + logic mw_reg_load_fault_in, mw_reg_store_fault_in; + logic [XLEN-1:0] mw_reg_fault_addr_in; logic mem_stall_req; // (9) MEM/WB Pipeline Register Outputs logic [XLEN-1:0] wb_pc_in; logic [XLEN-1:0] wb_alu_result_in; logic [XLEN-1:0] wb_mem_rdata_in; + logic [INST_WIDTH-1:0] wb_instr_in; logic wb_reg_write_en_in; logic [4:0] wb_rd_addr_in; logic [1:0] wb_wb_sel_in; @@ -144,6 +159,8 @@ module richard_core logic [4:0] wb_rs1_addr_in; logic [XLEN-1:0] wb_imm_in; logic wb_illegal_instr_in, wb_is_ecall_in, wb_is_ebreak_in, wb_is_mret_in, wb_is_sret_in; + logic wb_load_fault_flag, wb_store_fault_flag; + logic [XLEN-1:0] wb_fault_addr_in; // (10) WB Stage Outputs (to RegFile & CSR Unit) logic [XLEN-1:0] wb_rd_wdata; @@ -172,6 +189,32 @@ module richard_core logic trap_illegal_instr, trap_is_ecall, trap_is_ebreak, trap_is_mret, trap_is_sret; logic [XLEN-1:0] trap_epc; + // (12) Trap Controller + interrupt wiring + logic trap_load_fault, trap_store_fault; + logic trap_instr_page_fault, trap_load_page_fault, trap_store_page_fault; + logic [XLEN-1:0] trap_bad_addr; + logic [INST_WIDTH-1:0] trap_bad_instr; + logic trap_flush_req; + logic trap_pc_sel; + logic [XLEN-1:0] trap_next_pc; + logic trap_pc_sel_raw; + logic [XLEN-1:0] trap_next_pc_raw; + logic trap_ctrl_trigger; + logic [XLEN-1:0] trap_ctrl_mepc; + logic [XLEN-1:0] trap_ctrl_mcause; + logic [XLEN-1:0] trap_ctrl_mtval; + logic trap_ctrl_to_s_mode_flag; + logic trap_ctrl_mret_en; + logic trap_ctrl_sret_en; + logic csr_mstatus_mie; + + // Page-fault paths remain stubbed until the MMU surfaces violations. + assign trap_instr_page_fault = 1'b0; + assign trap_load_page_fault = 1'b0; + assign trap_store_page_fault = 1'b0; + + assign csr_mstatus_mie = csr_mstatus_value[CSR_MSTATUS_MIE_BIT]; + // --------------------------------------------------------- // (A) Hazard Unit Implementation // --------------------------------------------------------- @@ -184,9 +227,13 @@ module richard_core .ex_reg_write_en (ex_reg_write_en_in), .ex_is_load (ex_is_load_in), .ex_branch_taken (ex_branch_taken_out), + .ex_branch_target (ex_branch_target_out), .mem_rd_addr (mem_rd_addr_in), .mem_reg_write_en (mem_reg_write_en_in), .mem_stall_req (mem_stall_req), + .trap_flush_req (trap_flush_req), + .trap_pc_sel (trap_pc_sel), + .trap_target_pc (trap_next_pc), .wb_rd_addr (wb_rd_addr_in), .wb_reg_write_en (wb_reg_write_en_out), .forward_a_sel (hu_forward_a_sel), @@ -196,7 +243,10 @@ module richard_core .id_ex_stall_en (hu_id_ex_stall_en), .ex_mem_stall_en (hu_ex_mem_stall_en), .if_id_flush_en (hu_if_id_flush_en), - .id_ex_flush_en (hu_id_ex_flush_en) + .id_ex_flush_en (hu_id_ex_flush_en), + .pc_redirect_sel (hu_pc_redirect_sel), + .pc_redirect_target (hu_pc_redirect_target), + .pc_redirect_is_trap(hu_pc_redirect_is_trap) ); // --------------------------------------------------------- @@ -206,8 +256,9 @@ module richard_core .clk (clk), .rst (rst), .stall_en (hu_if_stall_en), - .ex_branch_taken (ex_branch_taken_out), - .ex_branch_target (ex_branch_target_out), + .pc_redirect_sel (hu_pc_redirect_sel), + .pc_redirect_target (hu_pc_redirect_target), + .pc_redirect_is_trap(hu_pc_redirect_is_trap), .if_pc (if_pc_out) ); @@ -232,6 +283,7 @@ module richard_core .rst (rst), .if_pc (if_id_pc), .if_instr (if_id_instr), + .csr_priv_mode (csr_priv_mode_cur), .wb_reg_write_en (wb_reg_write_en_out), .wb_rd_addr (wb_rd_addr), .wb_rd_wdata (wb_rd_wdata), @@ -272,6 +324,7 @@ module richard_core .rst (rst), .stall_en (hu_id_ex_stall_en), .flush_en (hu_id_ex_flush_en), + .id_instr (if_id_instr), .id_pc (id_pc_out), .id_rs1_rdata (id_rs1_rdata), .id_rs2_rdata (id_rs2_rdata), @@ -303,6 +356,7 @@ module richard_core .id_is_fence (id_is_fence), .id_is_fence_i (id_is_fence_i), .ex_pc (ex_pc_in), + .ex_instr (ex_instr_in), .ex_rs1_rdata (ex_rs1_rdata_in), .ex_rs2_rdata (ex_rs2_rdata_in), .ex_rs1_addr (ex_rs1_addr_in), @@ -386,6 +440,7 @@ module richard_core .ex_rs1_addr (ex_rs1_addr_in), .ex_pc (ex_pc_in), .ex_imm (ex_imm_in), + .ex_instr (ex_instr_in), .ex_is_load (ex_is_load_in), .ex_is_store (ex_is_store_in), .ex_mem_size (ex_mem_size_in), @@ -411,6 +466,7 @@ module richard_core .mem_rs1_addr (mem_rs1_addr_in), .mem_pc (mem_pc_in), .mem_imm (mem_imm_in), + .mem_instr (mem_instr_in), .mem_is_load (mem_is_load_in), .mem_is_store (mem_is_store_in), .mem_mem_size (mem_mem_size_in), @@ -442,6 +498,7 @@ module richard_core .mem_rs1_addr (mem_rs1_addr_in), .mem_pc (mem_pc_in), .mem_imm (mem_imm_in), + .mem_instr (mem_instr_in), .mem_is_load (mem_is_load_in), .mem_is_store (mem_is_store_in), .mem_mem_size (mem_mem_size_in), @@ -470,6 +527,7 @@ module richard_core .mem_wb_alu_result (mw_reg_alu_result_in), .mem_wb_mem_rdata (mw_reg_mem_rdata_in), .mem_wb_pc (mw_reg_pc_in), + .mem_wb_instr (mw_reg_instr_in), .mem_wb_reg_write_en (mw_reg_reg_write_en_in), .mem_wb_rd_addr (mw_reg_rd_addr_in), .mem_wb_wb_sel (mw_reg_wb_sel_in), @@ -483,19 +541,23 @@ module richard_core .mem_wb_is_ebreak (mw_reg_is_ebreak_in), .mem_wb_is_mret (mw_reg_is_mret_in), .mem_wb_is_sret (mw_reg_is_sret_in), + .mem_wb_load_fault (mw_reg_load_fault_in), + .mem_wb_store_fault (mw_reg_store_fault_in), + .mem_wb_fault_addr (mw_reg_fault_addr_in), .mem_stall_req (mem_stall_req) ); assign dmem_size = mem_mem_size_in; mem_wb_reg u_mem_wb_reg ( - .clk (clk), - .rst (rst), - .stall_en (1'b0), - .flush_en (1'b0), + .clk (clk), + .rst (rst), + .stall_en (1'b0), + .flush_en (trap_flush_req), .mem_pc (mw_reg_pc_in), .mem_alu_result (mw_reg_alu_result_in), .mem_mem_rdata (mw_reg_mem_rdata_in), + .mem_instr (mw_reg_instr_in), .mem_reg_write_en (mw_reg_reg_write_en_in), .mem_rd_addr (mw_reg_rd_addr_in), .mem_wb_sel (mw_reg_wb_sel_in), @@ -509,9 +571,13 @@ module richard_core .mem_is_ebreak (mw_reg_is_ebreak_in), .mem_is_mret (mw_reg_is_mret_in), .mem_is_sret (mw_reg_is_sret_in), + .mem_load_fault (mw_reg_load_fault_in), + .mem_store_fault (mw_reg_store_fault_in), + .mem_fault_addr (mw_reg_fault_addr_in), .wb_pc (wb_pc_in), .wb_alu_result (wb_alu_result_in), .wb_mem_rdata (wb_mem_rdata_in), + .wb_instr (wb_instr_in), .wb_reg_write_en (wb_reg_write_en_in), .wb_rd_addr (wb_rd_addr_in), .wb_wb_sel (wb_wb_sel_in), @@ -524,7 +590,10 @@ module richard_core .wb_is_ecall (wb_is_ecall_in), .wb_is_ebreak (wb_is_ebreak_in), .wb_is_mret (wb_is_mret_in), - .wb_is_sret (wb_is_sret_in) + .wb_is_sret (wb_is_sret_in), + .wb_load_fault (wb_load_fault_flag), + .wb_store_fault (wb_store_fault_flag), + .wb_fault_addr (wb_fault_addr_in) ); // --------------------------------------------------------- @@ -543,12 +612,17 @@ module richard_core .mem_wb_rs1_rdata (wb_rs1_rdata_in), .mem_wb_rs1_addr (wb_rs1_addr_in), .mem_wb_imm (wb_imm_in), + .mem_wb_instr (wb_instr_in), .mem_wb_illegal_instr(wb_illegal_instr_in), .mem_wb_is_ecall (wb_is_ecall_in), .mem_wb_is_ebreak (wb_is_ebreak_in), .mem_wb_is_mret (wb_is_mret_in), .mem_wb_is_sret (wb_is_sret_in), + .mem_wb_load_fault (wb_load_fault_flag), + .mem_wb_store_fault (wb_store_fault_flag), + .mem_wb_fault_addr (wb_fault_addr_in), .csr_rdata (csr_rdata), + .csr_illegal_access (csr_illegal_access_flag), .wb_rd_wdata (wb_rd_wdata), .wb_rd_addr (wb_rd_addr), .wb_reg_write_en (wb_reg_write_en_out), @@ -561,7 +635,11 @@ module richard_core .trap_is_ebreak (trap_is_ebreak), .trap_is_mret (trap_is_mret), .trap_is_sret (trap_is_sret), - .trap_epc (trap_epc) + .trap_epc (trap_epc), + .trap_load_fault (trap_load_fault), + .trap_store_fault (trap_store_fault), + .trap_bad_addr (trap_bad_addr), + .trap_bad_instr (trap_bad_instr) ); csr_unit u_csr_unit ( @@ -571,12 +649,13 @@ module richard_core .csr_op (csr_op), .csr_addr (csr_addr), .csr_rs1_data (csr_rs1_data), - .trap_illegal_instr (trap_illegal_instr), - .trap_is_ecall (trap_is_ecall), - .trap_is_ebreak (trap_is_ebreak), - .trap_is_mret (trap_is_mret), - .trap_is_sret (trap_is_sret), - .trap_epc (trap_epc), + .trap_ctrl_trigger (trap_ctrl_trigger), + .trap_ctrl_to_s_mode(trap_ctrl_to_s_mode_flag), + .trap_ctrl_mepc (trap_ctrl_mepc), + .trap_ctrl_mcause (trap_ctrl_mcause), + .trap_ctrl_mtval (trap_ctrl_mtval), + .trap_ctrl_mret_en (trap_ctrl_mret_en), + .trap_ctrl_sret_en (trap_ctrl_sret_en), .csr_rdata (csr_rdata), .csr_mstatus (csr_mstatus_value), .csr_sstatus (csr_sstatus_value), @@ -587,10 +666,52 @@ module richard_core .csr_mepc (csr_mepc_value), .csr_sepc (csr_sepc_value), .csr_satp (csr_satp_value), - .csr_priv_mode (csr_priv_mode_cur), - .csr_trap_vector (csr_trap_vector_value), - .csr_trap_to_s_mode (csr_trap_to_s_mode_flag), - .csr_illegal_access (csr_illegal_access_flag) + .csr_priv_mode (csr_priv_mode_cur), + .csr_trap_vector (), + .csr_trap_to_s_mode (), + .csr_trap_vector_next (csr_trap_vector_value), + .csr_trap_to_s_mode_next(csr_trap_to_s_mode_flag), + .csr_illegal_access (csr_illegal_access_flag) ); + trap_ctrl u_trap_ctrl ( + .wb_pc (wb_pc_in), + .trap_illegal_instr (trap_illegal_instr), + .trap_is_ecall (trap_is_ecall), + .trap_is_ebreak (trap_is_ebreak), + .trap_is_mret (trap_is_mret), + .trap_is_sret (trap_is_sret), + .trap_load_fault (trap_load_fault), + .trap_store_fault (trap_store_fault), + .trap_instr_page_fault(trap_instr_page_fault), + .trap_load_page_fault(trap_load_page_fault), + .trap_store_page_fault(trap_store_page_fault), + .trap_bad_addr (trap_bad_addr), + .trap_bad_instr (trap_bad_instr), + .ext_timer_int (ext_timer_int), + .ext_software_int (ext_software_int), + .ext_external_int (ext_external_int), + .csr_mstatus_mie (csr_mstatus_mie), + .csr_mtvec (csr_mtvec_base), + .csr_stvec (csr_stvec_base), + .csr_mepc (csr_mepc_value), + .csr_sepc (csr_sepc_value), + .csr_medeleg (csr_medeleg_value), + .csr_mideleg (csr_mideleg_value), + .csr_priv_mode (csr_priv_mode_cur), + .trap_trigger (trap_ctrl_trigger), + .trap_mepc (trap_ctrl_mepc), + .trap_mcause (trap_ctrl_mcause), + .trap_mtval (trap_ctrl_mtval), + .trap_to_s_mode (trap_ctrl_to_s_mode_flag), + .mret_en (trap_ctrl_mret_en), + .sret_en (trap_ctrl_sret_en), + .trap_flush_req (trap_flush_req), + .trap_pc_sel (trap_pc_sel_raw), + .trap_next_pc (trap_next_pc_raw) + ); + + assign trap_pc_sel = trap_pc_sel_raw; + assign trap_next_pc = trap_next_pc_raw; + endmodule diff --git a/rv64cpu/rtl/core/wb_stage.sv b/rv64cpu/rtl/core/wb_stage.sv index 26c9c7b..d9bfd93 100644 --- a/rv64cpu/rtl/core/wb_stage.sv +++ b/rv64cpu/rtl/core/wb_stage.sv @@ -26,6 +26,7 @@ module wb_stage input logic [XLEN-1:0] mem_wb_mem_rdata, input logic [XLEN-1:0] mem_wb_pc, input logic [XLEN-1:0] mem_wb_pc_plus_4, + input logic [INST_WIDTH-1:0] mem_wb_instr, input logic mem_wb_reg_write_en, input logic [4:0] mem_wb_rd_addr, @@ -42,9 +43,13 @@ module wb_stage input logic mem_wb_is_ebreak, input logic mem_wb_is_mret, input logic mem_wb_is_sret, + input logic mem_wb_load_fault, + input logic mem_wb_store_fault, + input logic [XLEN-1:0] mem_wb_fault_addr, // Inputs from CSR Unit input logic [XLEN-1:0] csr_rdata, // Data read from CSR unit + input logic csr_illegal_access,// CSR rejected the request // Outputs to Register File output logic [XLEN-1:0] wb_rd_wdata, @@ -63,14 +68,21 @@ module wb_stage output logic trap_is_ebreak, output logic trap_is_mret, output logic trap_is_sret, - output logic [XLEN-1:0] trap_epc // PC of the instruction causing the trap + output logic [XLEN-1:0] trap_epc, + output logic trap_load_fault, + output logic trap_store_fault, + output logic [XLEN-1:0] trap_bad_addr, + output logic [INST_WIDTH-1:0] trap_bad_instr ); // -------------------------- // Exception/Trap Detection // -------------------------- logic has_trap; - assign has_trap = mem_wb_illegal_instr | mem_wb_is_ecall | mem_wb_is_ebreak; + logic trap_blocker; + assign has_trap = mem_wb_illegal_instr | mem_wb_is_ecall | mem_wb_is_ebreak | + mem_wb_load_fault | mem_wb_store_fault; + assign trap_blocker = has_trap | csr_illegal_access; // -------------------------- // CSR Signal Routing @@ -106,16 +118,20 @@ module wb_stage // -------------------------- // We only write back if the instruction didn't trap. assign wb_rd_addr = mem_wb_rd_addr; - assign wb_reg_write_en = mem_wb_reg_write_en & ~has_trap; + assign wb_reg_write_en = mem_wb_reg_write_en & ~trap_blocker; // -------------------------- // Trap / Exception Signals // -------------------------- - assign trap_illegal_instr = mem_wb_illegal_instr; + assign trap_illegal_instr = mem_wb_illegal_instr | csr_illegal_access; assign trap_is_ecall = mem_wb_is_ecall; assign trap_is_ebreak = mem_wb_is_ebreak; assign trap_is_mret = mem_wb_is_mret; assign trap_is_sret = mem_wb_is_sret; assign trap_epc = mem_wb_pc; + assign trap_load_fault = mem_wb_load_fault; + assign trap_store_fault = mem_wb_store_fault; + assign trap_bad_addr = mem_wb_fault_addr; + assign trap_bad_instr = mem_wb_instr; endmodule diff --git a/rv64cpu/tb/CMakeLists.txt b/rv64cpu/tb/CMakeLists.txt index 7b660e8..361a6e7 100644 --- a/rv64cpu/tb/CMakeLists.txt +++ b/rv64cpu/tb/CMakeLists.txt @@ -204,12 +204,19 @@ add_verilator_target( ${RTL_CORE_DIR}/hazard_unit.sv ${RTL_CORE_DIR}/include/csr_pkg.sv ${RTL_CORE_DIR}/csr_unit.sv + ${RTL_CORE_DIR}/trap_ctrl.sv ${RTL_CORE_DIR}/richard_core.sv INCLUDE_DIRS ${RTL_COMMON_INCLUDES} TB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/core/tb_core.cc EXTRA_FLAGS -Wno-PINCONNECTEMPTY ) +add_test(NAME tb_core_delegation COMMAND $<TARGET_FILE:tb_core> --case mret_sret_redirect) +add_test(NAME tb_core_interrupt_priority COMMAND $<TARGET_FILE:tb_core> --case branch_interrupt_priority) +add_test(NAME tb_core_double_exception COMMAND $<TARGET_FILE:tb_core> --case double_exception_flush) +add_test(NAME tb_core_mem_fault COMMAND $<TARGET_FILE:tb_core> --case mem_fault_pipeline) +add_test(NAME tb_core_mie_masking COMMAND $<TARGET_FILE:tb_core> --case mie_masking_response) + # ═══════════════════════════════════════════════════════════════════════════════════ # SoC Integration Test # ═══════════════════════════════════════════════════════════════════════════════════ diff --git a/rv64cpu/tb/core/tb_core.cc b/rv64cpu/tb/core/tb_core.cc index 30fe554..39d4f19 100644 --- a/rv64cpu/tb/core/tb_core.cc +++ b/rv64cpu/tb/core/tb_core.cc @@ -20,11 +20,14 @@ // (instruction memory + data memory) to verify multi-cycle pipeline behavior. // It acts as a simple memory model, feeding instructions and returning data. +#include <algorithm> #include <iostream> #include <iomanip> #include <cstdint> +#include <iterator> #include <vector> #include <map> +#include <string> #include <verilated.h> #include "Vrichard_core.h" @@ -32,6 +35,53 @@ #include <verilated_vcd_c.h> #endif +// --------------------------------- +// CSR addresses & cause codes used in tests +// --------------------------------- +constexpr uint16_t CSR_MSTATUS = 0x300; +constexpr uint16_t CSR_MISA = 0x301; +constexpr uint16_t CSR_MEDELEG = 0x302; +constexpr uint16_t CSR_MIDELEG = 0x303; +constexpr uint16_t CSR_MIE = 0x304; +constexpr uint16_t CSR_MTVEC = 0x305; +constexpr uint16_t CSR_MEPC = 0x341; +constexpr uint16_t CSR_MSCRATCH = 0x340; +constexpr uint16_t CSR_MCAUSE = 0x342; +constexpr uint16_t CSR_MTVAL = 0x343; +constexpr uint16_t CSR_SSTATUS = 0x100; +constexpr uint16_t CSR_STVEC = 0x105; +constexpr uint16_t CSR_SSCRATCH = 0x140; +constexpr uint16_t CSR_SEPC = 0x141; +constexpr uint16_t CSR_SCAUSE = 0x142; +constexpr uint16_t CSR_STVAL = 0x143; + +constexpr int MSTATUS_MIE_BIT = 3; +constexpr int MSTATUS_MPIE_BIT = 7; +constexpr int MSTATUS_MPP_LO = 11; +constexpr int SSTATUS_SIE_BIT = 1; +constexpr int SSTATUS_SPIE_BIT = 5; +constexpr int SSTATUS_SPP_BIT = 8; + +constexpr uint64_t EXC_INST_ILLEGAL = 2ULL; +constexpr uint64_t EXC_BREAKPOINT = 3ULL; +constexpr uint64_t EXC_LOAD_FAULT = 5ULL; +constexpr uint64_t EXC_STORE_FAULT = 7ULL; +constexpr uint64_t EXC_ECALL_FROM_U = 8ULL; +constexpr uint64_t EXC_ECALL_FROM_M = 11ULL; +constexpr uint64_t INTERRUPT_BIT = 1ULL << 63; +constexpr uint64_t INT_M_EXTERNAL = 11ULL; +constexpr uint64_t INT_M_TIMER = 7ULL; + +constexpr uint64_t LOG_ENTRY_STRIDE = 24ULL; +constexpr uint64_t MACHINE_TRAP_VECTOR = 0x00000000ULL; +constexpr uint64_t SUPERVISOR_TRAP_VECTOR = 0x00000100ULL; +constexpr uint64_t MACHINE_LOG_PTR_ADDR = 0x000000000000FFF0ULL; +constexpr uint64_t SUPERVISOR_LOG_PTR_ADDR = 0x000000000000FFE0ULL; + +constexpr uint64_t PRIV_MODE_U = 0ULL; +constexpr uint64_t PRIV_MODE_S = 1ULL; +constexpr uint64_t PRIV_MODE_M = 3ULL; + // ----------------------- // RISC-V Instruction Encoding Helpers // ----------------------- @@ -81,6 +131,7 @@ static uint32_t rv_b_type(int32_t offset, uint32_t rs2, uint32_t rs1, uint32_t f // Common instruction shorthands static uint32_t NOP() { return 0x00000013; } // addi x0, x0, 0 +static uint32_t ILLEGAL() { return 0xFFFFFFFF; } static uint32_t ADDI(uint32_t rd, uint32_t rs1, int32_t imm) { return rv_i_type(static_cast<uint32_t>(imm) & 0xFFF, rs1, 0x0, rd, 0x13); } @@ -108,6 +159,51 @@ static uint32_t LUI(uint32_t rd, uint32_t imm) { static uint32_t JAL(uint32_t rd, int32_t offset) { return rv_j_type(offset, rd, 0x6f); } +static uint32_t JALR(uint32_t rd, uint32_t rs1, int32_t imm) { + return rv_i_type(static_cast<uint32_t>(imm) & 0xFFF, rs1, 0x0, rd, 0x67); +} +static uint32_t SYSTEM(uint32_t funct12, uint32_t rs1, uint32_t funct3, uint32_t rd) { + return ((funct12 & 0xFFF) << 20) | ((rs1 & 0x1F) << 15) | ((funct3 & 0x7) << 12) | ((rd & 0x1F) << 7) | 0x73; +} +static uint32_t ECALL() { return SYSTEM(0x000, 0, 0x0, 0); } +static uint32_t EBREAK() { return SYSTEM(0x001, 0, 0x0, 0); } +static uint32_t MRET() { return SYSTEM(0x302, 0, 0x0, 0); } +static uint32_t SRET() { return SYSTEM(0x102, 0, 0x0, 0); } +static uint32_t CSRInstrReg(uint32_t csr, uint32_t funct3, uint32_t rd, uint32_t rs1) { + return ((csr & 0xFFF) << 20) | ((rs1 & 0x1F) << 15) | ((funct3 & 0x7) << 12) | ((rd & 0x1F) << 7) | 0x73; +} +static uint32_t CSRRW(uint32_t rd, uint32_t csr, uint32_t rs1) { + return CSRInstrReg(csr, 0b001, rd, rs1); +} +static uint32_t CSRRS(uint32_t rd, uint32_t csr, uint32_t rs1) { + return CSRInstrReg(csr, 0b010, rd, rs1); +} +static uint32_t CSRRWI(uint32_t rd, uint32_t csr, uint32_t zimm) { + return CSRInstrReg(csr, 0b101, rd, zimm & 0x1F); +} + +static void emit_load_imm64(std::vector<uint32_t>& prog, uint32_t rd, uint64_t imm) { + const uint64_t upper = (imm + 0x800ULL) >> 12; + int32_t lower = static_cast<int32_t>(imm & 0xFFFULL); + if (lower >= 2048) { + lower -= 4096; + } + prog.push_back(LUI(rd, static_cast<uint32_t>(upper))); + prog.push_back(ADDI(rd, rd, lower)); +} + +static void emit_nops(std::vector<uint32_t>& prog, int count) { + for (int i = 0; i < count; ++i) { + prog.push_back(NOP()); + } +} + +static void emit_csrrw_imm64(std::vector<uint32_t>& prog, uint32_t tmp_reg, uint32_t csr, uint64_t value) { + // CSR source operand is read in ID stage, so we need a short drain window after loading tmp_reg. + emit_load_imm64(prog, tmp_reg, value); + emit_nops(prog, 6); + prog.push_back(CSRRW(0, csr, tmp_reg)); +} // ----------------------- // Test Infrastructure @@ -148,7 +244,7 @@ public: return NOP(); // Return NOP for unmapped addresses } - uint64_t read_data(uint64_t addr) { + uint64_t read_data(uint64_t addr) const { // Align to doubleword uint64_t aligned = addr & ~0x7ULL; auto it = dmem.find(aligned); @@ -162,6 +258,92 @@ public: } }; +static std::vector<uint32_t> make_machine_trap_handler_program() { + constexpr uint32_t REG_PTR = 5; + constexpr uint32_t REG_MCAUSE = 6; + constexpr uint32_t REG_MEPC = 7; + constexpr uint32_t REG_MTVAL = 28; + constexpr uint32_t REG_TMP = 29; + constexpr uint32_t REG_SLOT_ADDR = 30; + + std::vector<uint32_t> prog; + emit_load_imm64(prog, REG_SLOT_ADDR, MACHINE_LOG_PTR_ADDR); + prog.push_back(LD(REG_PTR, REG_SLOT_ADDR, 0)); + emit_nops(prog, 12); + prog.push_back(CSRRS(REG_MCAUSE, CSR_MCAUSE, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_MCAUSE, REG_PTR, 0)); + prog.push_back(CSRRS(REG_MEPC, CSR_MEPC, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_MEPC, REG_PTR, 8)); + prog.push_back(CSRRS(REG_MTVAL, CSR_MTVAL, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_MTVAL, REG_PTR, 16)); + prog.push_back(ADDI(REG_PTR, REG_PTR, static_cast<int32_t>(LOG_ENTRY_STRIDE))); + prog.push_back(SD(REG_PTR, REG_SLOT_ADDR, 0)); + prog.push_back(CSRRS(REG_TMP, CSR_MEPC, 0)); + prog.push_back(ADDI(REG_TMP, REG_TMP, 4)); + emit_nops(prog, 12); + prog.push_back(CSRRW(0, CSR_MEPC, REG_TMP)); + prog.push_back(MRET()); + prog.push_back(NOP()); + prog.push_back(NOP()); + return prog; +} + +static std::vector<uint32_t> make_supervisor_trap_handler_program() { + constexpr uint32_t REG_PTR = 5; + constexpr uint32_t REG_SCAUSE = 6; + constexpr uint32_t REG_SEPC = 7; + constexpr uint32_t REG_STVAL = 28; + constexpr uint32_t REG_TMP = 29; + constexpr uint32_t REG_SLOT_ADDR = 30; + + std::vector<uint32_t> prog; + emit_load_imm64(prog, REG_SLOT_ADDR, SUPERVISOR_LOG_PTR_ADDR); + prog.push_back(LD(REG_PTR, REG_SLOT_ADDR, 0)); + emit_nops(prog, 12); + prog.push_back(CSRRS(REG_SCAUSE, CSR_SCAUSE, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_SCAUSE, REG_PTR, 0)); + prog.push_back(CSRRS(REG_SEPC, CSR_SEPC, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_SEPC, REG_PTR, 8)); + prog.push_back(CSRRS(REG_STVAL, CSR_STVAL, 0)); + emit_nops(prog, 4); + prog.push_back(SD(REG_STVAL, REG_PTR, 16)); + prog.push_back(ADDI(REG_PTR, REG_PTR, static_cast<int32_t>(LOG_ENTRY_STRIDE))); + prog.push_back(SD(REG_PTR, REG_SLOT_ADDR, 0)); + prog.push_back(CSRRS(REG_TMP, CSR_SEPC, 0)); + prog.push_back(ADDI(REG_TMP, REG_TMP, 4)); + emit_nops(prog, 12); + prog.push_back(CSRRW(0, CSR_SEPC, REG_TMP)); + prog.push_back(SRET()); + prog.push_back(NOP()); + prog.push_back(NOP()); + return prog; +} + +static void install_trap_handlers(MemoryModel& mem) { + mem.load_program(MACHINE_TRAP_VECTOR, make_machine_trap_handler_program()); + mem.load_program(SUPERVISOR_TRAP_VECTOR, make_supervisor_trap_handler_program()); +} + +struct TrapRecord { + uint64_t mcause; + uint64_t mepc; + uint64_t mtval; +}; + +static TrapRecord fetch_trap_record(const MemoryModel& mem, uint64_t base, int index) { + const uint64_t addr = base + static_cast<uint64_t>(index) * LOG_ENTRY_STRIDE; + TrapRecord rec; + rec.mcause = mem.read_data(addr + 0); + rec.mepc = mem.read_data(addr + 8); + rec.mtval = mem.read_data(addr + 16); + return rec; +} + // ----------------------- // Core Simulation Wrapper // ----------------------- @@ -170,11 +352,17 @@ public: Vrichard_core* top; MemoryModel mem; uint64_t sim_time; + bool ext_timer_int_level; + bool ext_software_int_level; + bool ext_external_int_level; #if VM_TRACE VerilatedVcdC* tfp; #endif - CoreSim() : sim_time(0) { + CoreSim() : sim_time(0), + ext_timer_int_level(false), + ext_software_int_level(false), + ext_external_int_level(false) { top = new Vrichard_core; #if VM_TRACE Verilated::traceEverOn(true); @@ -195,6 +383,7 @@ public: top->rst = 1; top->imem_rdata = NOP(); top->dmem_rdata = 0; + clear_interrupts(); for (int i = 0; i < cycles; i++) { tick(); } @@ -212,6 +401,10 @@ public: top->dmem_rdata = 0; } + top->ext_timer_int = ext_timer_int_level; + top->ext_software_int = ext_software_int_level; + top->ext_external_int = ext_external_int_level; + // Rising edge top->clk = 0; top->eval(); @@ -237,8 +430,28 @@ public: tick(); } } + + void set_interrupt_lines(bool timer, bool software, bool external) { + ext_timer_int_level = timer; + ext_software_int_level = software; + ext_external_int_level = external; + } + + void clear_interrupts() { + set_interrupt_lines(false, false, false); + } }; +static std::vector<uint64_t> collect_pc_trace(CoreSim& sim, int cycles) { + std::vector<uint64_t> trace; + trace.reserve(static_cast<size_t>(cycles)); + for (int i = 0; i < cycles; ++i) { + sim.tick(); + trace.push_back(sim.top->imem_addr); + } + return trace; +} + // ═══════════════════════════════════════════════════════════════ // Test Cases // ═══════════════════════════════════════════════════════════════ @@ -631,16 +844,209 @@ void test_fibonacci(CoreSim& sim) { } // ═══════════════════════════════════════════════════════════════ +// Targeted Trap/Interrupt Scenarios (ctest-selectable) +// ═══════════════════════════════════════════════════════════════ + +static void run_mem_fault_pipeline_case() { + std::cout << "[CASE] mem_fault_pipeline" << std::endl; + CoreSim sim; + install_trap_handlers(sim.mem); + + const uint64_t boot_pc = 0x80000000ULL; + const uint64_t load_addr = 0x0000000000001003ULL; + const uint64_t store_addr = 0x0000000000002005ULL; + const uint64_t log_base = 0x0000000000009000ULL; + sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base); + ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base, "machine log pointer seeded"); + + std::vector<uint32_t> program; + emit_nops(program, 8); + emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR); + emit_nops(program, 8); + emit_load_imm64(program, 3, load_addr); + program.push_back(LD(5, 3, 0)); + program.push_back(JAL(0, -4)); + + sim.mem.load_program(boot_pc, program); + sim.reset(); + sim.run(80); + ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base + LOG_ENTRY_STRIDE, "machine log pointer advanced"); + + const TrapRecord load_trap = fetch_trap_record(sim.mem, log_base, 0); + ASSERT_EQ(load_trap.mcause, EXC_LOAD_FAULT, "load fault cause code"); + ASSERT_EQ(load_trap.mtval, load_addr, "load fault address latched"); + ASSERT_EQ(sim.mem.read_data(store_addr & ~0x7ULL), 0ULL, "misaligned store suppressed"); +} + +static void run_double_exception_flush_case() { + std::cout << "[CASE] double_exception_flush" << std::endl; + CoreSim sim; + install_trap_handlers(sim.mem); + + const uint64_t boot_pc = 0x80000000ULL; + const uint64_t log_base = 0x0000000000009100ULL; + sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base); + std::vector<uint32_t> program; + emit_nops(program, 8); + emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR); + emit_nops(program, 8); + program.push_back(ILLEGAL()); + program.push_back(NOP()); + program.push_back(JAL(0, -4)); + + sim.mem.load_program(boot_pc, program); + sim.reset(); + sim.run(80); + + const TrapRecord illegal_trap = fetch_trap_record(sim.mem, log_base, 0); + ASSERT_EQ(illegal_trap.mcause, EXC_INST_ILLEGAL, "first cause illegal"); + ASSERT_EQ(sim.mem.read_data(MACHINE_LOG_PTR_ADDR), log_base + LOG_ENTRY_STRIDE, "single trap logged"); +} + +static void run_mret_sret_redirect_case() { + std::cout << "[CASE] mret_sret_redirect" << std::endl; + CoreSim sim; + install_trap_handlers(sim.mem); + + const uint64_t boot_pc = 0x80000000ULL; + const uint64_t m_log_base = 0x0000000000009200ULL; + const uint64_t post_store_addr = 0x0000000000009400ULL; + const uint64_t post_trap_addr = 0x0000000000009408ULL; + sim.mem.write_data(MACHINE_LOG_PTR_ADDR, m_log_base); + + std::vector<uint32_t> boot; + emit_nops(boot, 8); + emit_csrrw_imm64(boot, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR); + emit_nops(boot, 8); + emit_load_imm64(boot, 11, post_store_addr); + emit_load_imm64(boot, 12, 0xCAFECAFEULL); + boot.push_back(SD(12, 11, 0)); + boot.push_back(ECALL()); + emit_load_imm64(boot, 13, post_trap_addr); + emit_load_imm64(boot, 14, 0xBEEFBEEFULL); + boot.push_back(SD(14, 13, 0)); + boot.push_back(JAL(0, 0)); + sim.mem.load_program(boot_pc, boot); + + sim.reset(); + sim.run(300); + + const TrapRecord m_trap = fetch_trap_record(sim.mem, m_log_base, 0); + ASSERT_EQ(m_trap.mcause, EXC_ECALL_FROM_M, "ecall from machine traps to machine"); + ASSERT_EQ(sim.mem.read_data(post_store_addr), 0xFFFFFFFFCAFECAFEULL, "post-trap store committed once"); +} + +static void run_branch_interrupt_priority_case() { + std::cout << "[CASE] branch_interrupt_priority" << std::endl; + CoreSim sim; + install_trap_handlers(sim.mem); + + const uint64_t boot_pc = 0x80000000ULL; + const uint64_t log_base = 0x0000000000009600ULL; + sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base); + + std::vector<uint32_t> boot; + emit_nops(boot, 8); + emit_csrrw_imm64(boot, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR); + emit_csrrw_imm64(boot, 2, CSR_MSTATUS, (1ULL << MSTATUS_MIE_BIT)); + emit_csrrw_imm64(boot, 3, CSR_MIE, (1ULL << INT_M_EXTERNAL)); + boot.push_back(JAL(0, 0)); + sim.mem.load_program(boot_pc, boot); + + sim.reset(); + sim.run(10); + sim.set_interrupt_lines(false, false, true); + sim.run(40); + sim.clear_interrupts(); + sim.run(20); + + const TrapRecord interrupt_trap = fetch_trap_record(sim.mem, log_base, 0); + ASSERT_EQ(interrupt_trap.mcause, INTERRUPT_BIT | INT_M_EXTERNAL, "external interrupt cause"); + ASSERT_EQ(interrupt_trap.mtval, 0ULL, "interrupt mtval should be zero"); +} + +static void run_mie_masking_case() { + std::cout << "[CASE] mie_masking_response" << std::endl; + CoreSim sim; + install_trap_handlers(sim.mem); + + const uint64_t boot_pc = 0x80000000ULL; + const uint64_t log_base = 0x0000000000009700ULL; + sim.mem.write_data(MACHINE_LOG_PTR_ADDR, log_base); + for (int i = 0; i < 3; ++i) { + sim.mem.write_data(log_base + static_cast<uint64_t>(i) * 8ULL, 0ULL); + } + + std::vector<uint32_t> program; + emit_nops(program, 8); + emit_csrrw_imm64(program, 1, CSR_MTVEC, MACHINE_TRAP_VECTOR); + emit_csrrw_imm64(program, 2, CSR_MIE, (1ULL << INT_M_EXTERNAL)); + program.push_back(CSRRW(0, CSR_MSTATUS, 0)); + emit_nops(program, 8); + emit_nops(program, 48); + emit_csrrw_imm64(program, 4, CSR_MSTATUS, (1ULL << MSTATUS_MIE_BIT)); + program.push_back(JAL(0, 0)); + + sim.mem.load_program(boot_pc, program); + sim.reset(); + + sim.set_interrupt_lines(false, false, true); + sim.run(20); + ASSERT_EQ(sim.mem.read_data(log_base), 0ULL, "no trap while MIE=0"); + sim.run(100); + const TrapRecord int_trap = fetch_trap_record(sim.mem, log_base, 0); + ASSERT_EQ(int_trap.mcause, INTERRUPT_BIT | INT_M_EXTERNAL, "external interrupt cause"); +} + +// TODO: Add EXPECT_FAIL once trap_load_page_fault/trap_store_page_fault are driven by the MMU. + +struct CaseDispatchEntry { + const char* name; + void (*fn)(); +}; + +static const CaseDispatchEntry kCaseDispatchTable[] = { + {"mem_fault_pipeline", run_mem_fault_pipeline_case}, + {"double_exception_flush", run_double_exception_flush_case}, + {"mret_sret_redirect", run_mret_sret_redirect_case}, + {"branch_interrupt_priority", run_branch_interrupt_priority_case}, + {"mie_masking_response", run_mie_masking_case} +}; + +// ═══════════════════════════════════════════════════════════════ // Main // ═══════════════════════════════════════════════════════════════ int main(int argc, char** argv) { Verilated::commandArgs(argc, argv); + std::string requested_case; + for (int i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "--case" && (i + 1) < argc) { + requested_case = argv[++i]; + } + } + + if (!requested_case.empty()) { + const auto* entry = std::find_if( + std::begin(kCaseDispatchTable), + std::end(kCaseDispatchTable), + [&](const CaseDispatchEntry& item) { return requested_case == item.name; } + ); + if (entry == std::end(kCaseDispatchTable)) { + std::cerr << "Unknown --case value: " << requested_case << std::endl; + return 2; + } + entry->fn(); + std::cout << "================================================================" << std::endl; + std::cout << " Results: " << g_pass_count << "/" << g_test_count << " assertions passed" << std::endl; + std::cout << "================================================================" << std::endl; + return (g_pass_count == g_test_count) ? 0 : 1; + } + std::cout << "================================================================" << std::endl; std::cout << " Richard CPU Core Integration Test" << std::endl; std::cout << "================================================================" << std::endl; - // Each test gets a fresh instance { CoreSim sim; test_reset(sim); @@ -689,12 +1095,17 @@ int main(int argc, char** argv) { CoreSim sim; test_reset_mid_execution(sim); } - { CoreSim sim; test_fibonacci(sim); } + run_mem_fault_pipeline_case(); + run_double_exception_flush_case(); + run_mret_sret_redirect_case(); + run_branch_interrupt_priority_case(); + run_mie_masking_case(); + std::cout << "================================================================" << std::endl; std::cout << " Results: " << g_pass_count << "/" << g_test_count << " assertions passed" << std::endl; std::cout << "================================================================" << std::endl; @@ -702,8 +1113,8 @@ int main(int argc, char** argv) { if (g_pass_count == g_test_count) { std::cout << " ALL TESTS PASSED" << std::endl; return 0; - } else { - std::cout << " SOME TESTS FAILED" << std::endl; - return 1; } + + std::cout << " SOME TESTS FAILED" << std::endl; + return 1; } diff --git a/rv64cpu/tb/unit/tb_csr.cc b/rv64cpu/tb/unit/tb_csr.cc index 95d6be7..9dce96f 100644 --- a/rv64cpu/tb/unit/tb_csr.cc +++ b/rv64cpu/tb/unit/tb_csr.cc @@ -17,6 +17,7 @@ // File path: tb/unit/tb_csr.cc // Description: CSR unit testbench +#include <array> #include <cassert> #include <cstdint> #include <iostream> @@ -61,6 +62,13 @@ constexpr uint16_t CSR_FCSR = 0x003; constexpr uint64_t EXC_INST_ILLEGAL = 2ULL; constexpr uint64_t EXC_ECALL_FROM_U = 8ULL; constexpr uint64_t EXC_ECALL_FROM_S = 9ULL; +constexpr uint64_t EXC_LOAD_FAULT = 5ULL; +constexpr uint64_t EXC_STORE_FAULT = 7ULL; + +constexpr uint64_t INTERRUPT_BIT = 1ULL << 63; +constexpr uint64_t INT_M_SOFTWARE = 3ULL; +constexpr uint64_t INT_M_TIMER = 7ULL; +constexpr uint64_t INT_M_EXTERNAL = 11ULL; constexpr int MSTATUS_MIE_BIT = 3; constexpr int MSTATUS_MPIE_BIT = 7; @@ -91,12 +99,13 @@ void drive_defaults(Vcsr_unit* dut) { dut->csr_op = 0; dut->csr_addr = 0; dut->csr_rs1_data = 0; - dut->trap_illegal_instr = 0; - dut->trap_is_ecall = 0; - dut->trap_is_ebreak = 0; - dut->trap_is_mret = 0; - dut->trap_is_sret = 0; - dut->trap_epc = 0; + dut->trap_ctrl_trigger = 0; + dut->trap_ctrl_to_s_mode = 0; + dut->trap_ctrl_mepc = 0; + dut->trap_ctrl_mcause = 0; + dut->trap_ctrl_mtval = 0; + dut->trap_ctrl_mret_en = 0; + dut->trap_ctrl_sret_en = 0; } void apply_reset(Vcsr_unit* dut) { @@ -107,12 +116,45 @@ void apply_reset(Vcsr_unit* dut) { } void clear_trap_lines(Vcsr_unit* dut) { - dut->trap_illegal_instr = 0; - dut->trap_is_ecall = 0; - dut->trap_is_ebreak = 0; - dut->trap_is_mret = 0; - dut->trap_is_sret = 0; - dut->trap_epc = 0; + dut->trap_ctrl_trigger = 0; + dut->trap_ctrl_to_s_mode = 0; + dut->trap_ctrl_mepc = 0; + dut->trap_ctrl_mcause = 0; + dut->trap_ctrl_mtval = 0; + dut->trap_ctrl_mret_en = 0; + dut->trap_ctrl_sret_en = 0; +} + +void trigger_trap( + Vcsr_unit* dut, + uint64_t mepc, + uint64_t mcause, + uint64_t mtval, + bool to_s_mode +) { + dut->trap_ctrl_trigger = 1; + dut->trap_ctrl_mepc = mepc; + dut->trap_ctrl_mcause = mcause; + dut->trap_ctrl_mtval = mtval; + dut->trap_ctrl_to_s_mode = to_s_mode ? 1 : 0; + tick(dut); + dut->trap_ctrl_trigger = 0; + dut->trap_ctrl_mepc = 0; + dut->trap_ctrl_mcause = 0; + dut->trap_ctrl_mtval = 0; + dut->trap_ctrl_to_s_mode = 0; +} + +void trigger_interrupt(Vcsr_unit* dut, uint64_t mepc, uint64_t int_cause) { + trigger_trap(dut, mepc, (INTERRUPT_BIT | int_cause), 0ULL, false); +} + +template <typename Fn> +void run_csr_scenario(Vcsr_unit* dut, const char* name, Fn&& body) { + clear_trap_lines(dut); + std::cout << "[RUN] CSR scenario: " << name << std::endl; + body(dut); + std::cout << "[PASS] CSR scenario: " << name << std::endl; } uint64_t read_csr(Vcsr_unit* dut, uint16_t addr) { @@ -147,9 +189,33 @@ void force_priv_mode_via_mret(Vcsr_unit* dut, uint64_t encoded_mode) { mstatus &= ~(0x3ULL << MSTATUS_MPP_LO); mstatus |= (encoded_mode << MSTATUS_MPP_LO); csr_op(dut, CSR_MSTATUS, CSR_OP_WRITE, mstatus); - dut->trap_is_mret = 1; + dut->trap_ctrl_mret_en = 1; tick(dut); - dut->trap_is_mret = 0; + dut->trap_ctrl_mret_en = 0; +} + +uint64_t arbitrate_trap_priority( + bool ext_external, + bool ext_software, + bool ext_timer, + bool has_sync_exception, + uint64_t sync_cause, + bool& chose_interrupt +) { + if (ext_external) { + chose_interrupt = true; + return INTERRUPT_BIT | INT_M_EXTERNAL; + } + if (ext_software) { + chose_interrupt = true; + return INTERRUPT_BIT | INT_M_SOFTWARE; + } + if (ext_timer) { + chose_interrupt = true; + return INTERRUPT_BIT | INT_M_TIMER; + } + chose_interrupt = false; + return has_sync_exception ? sync_cause : 0ULL; } int main(int argc, char** argv) { @@ -190,9 +256,9 @@ int main(int argc, char** argv) { std::cout << "[PASS] CSRRS/CSRRC honor masks and ignore zero data" << std::endl; // Read-only CSRs ignore write attempts - const uint64_t misa_before = read_csr(dut, CSR_MISA); + const uint64_t misa_ro_snapshot = read_csr(dut, CSR_MISA); csr_op(dut, CSR_MISA, CSR_OP_WRITE, 0ULL); - assert(read_csr(dut, CSR_MISA) == misa_before); + assert(read_csr(dut, CSR_MISA) == misa_ro_snapshot); std::cout << "[PASS] Read-only CSRs reject writes" << std::endl; // Canonical aliases for cycle/minstret @@ -209,11 +275,7 @@ int main(int argc, char** argv) { uint64_t mstatus_cfg = read_csr(dut, CSR_MSTATUS) | (1ULL << MSTATUS_MIE_BIT); csr_op(dut, CSR_MSTATUS, CSR_OP_WRITE, mstatus_cfg); const uint64_t instret_pre_trap = read_csr(dut, CSR_MINSTRET); - dut->trap_illegal_instr = 1; - dut->trap_epc = 0xDEADULL; - tick(dut); - dut->trap_illegal_instr = 0; - dut->trap_epc = 0; + trigger_trap(dut, 0xDEADULL, EXC_INST_ILLEGAL, 0xDEADULL, false); assert(read_csr(dut, CSR_MINSTRET) == instret_pre_trap); advance_cycles(dut, 1); assert(read_csr(dut, CSR_MINSTRET) == instret_pre_trap + 1); @@ -230,45 +292,37 @@ int main(int argc, char** argv) { std::cout << "[PASS] Illegal trap updates mepc/mcause/mtval/mstatus" << std::endl; // mret restore flow - dut->trap_is_mret = 1; + dut->trap_ctrl_mret_en = 1; tick(dut); - dut->trap_is_mret = 0; - const uint64_t mstatus_after_mret = read_csr(dut, CSR_MSTATUS); - assert(((mstatus_after_mret >> MSTATUS_MIE_BIT) & 1ULL) == 1ULL); - assert(((mstatus_after_mret >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL); - assert(((mstatus_after_mret >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U); + dut->trap_ctrl_mret_en = 0; + const uint64_t mstatus_after_mret_reset = read_csr(dut, CSR_MSTATUS); + assert(((mstatus_after_mret_reset >> MSTATUS_MIE_BIT) & 1ULL) == 1ULL); + assert(((mstatus_after_mret_reset >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL); + assert(((mstatus_after_mret_reset >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U); std::cout << "[PASS] mret restores interrupt enable bits" << std::endl; // sret restore flow uint64_t sstatus_cfg = (1ULL << SSTATUS_SIE_BIT) | (1ULL << SSTATUS_SPP_BIT); csr_op(dut, CSR_SSTATUS, CSR_OP_WRITE, sstatus_cfg); - dut->trap_is_sret = 1; + dut->trap_ctrl_sret_en = 1; tick(dut); - dut->trap_is_sret = 0; - const uint64_t sstatus_after_sret = read_csr(dut, CSR_SSTATUS); - assert(((sstatus_after_sret >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL); - assert(((sstatus_after_sret >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL); - assert(((sstatus_after_sret >> SSTATUS_SPP_BIT) & 1ULL) == 0ULL); + dut->trap_ctrl_sret_en = 0; + const uint64_t sstatus_after_sret_reset = read_csr(dut, CSR_SSTATUS); + assert(((sstatus_after_sret_reset >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL); + assert(((sstatus_after_sret_reset >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL); + assert(((sstatus_after_sret_reset >> SSTATUS_SPP_BIT) & 1ULL) == 0ULL); std::cout << "[PASS] sret updates supervisor status bits" << std::endl; // Ecall causes depend on current privilege mode force_priv_mode_via_mret(dut, PRIV_ENCODE_S); - dut->trap_is_ecall = 1; - dut->trap_epc = 0x600ULL; - tick(dut); - dut->trap_is_ecall = 0; - dut->trap_epc = 0; + trigger_trap(dut, 0x600ULL, EXC_ECALL_FROM_S, 0ULL, false); assert(read_csr(dut, CSR_MCAUSE) == EXC_ECALL_FROM_S); assert(read_csr(dut, CSR_MTVAL) == 0ULL); assert(read_csr(dut, CSR_MEPC) == 0x600ULL); assert(((read_csr(dut, CSR_MSTATUS) >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_S); force_priv_mode_via_mret(dut, PRIV_ENCODE_U); - dut->trap_is_ecall = 1; - dut->trap_epc = 0x700ULL; - tick(dut); - dut->trap_is_ecall = 0; - dut->trap_epc = 0; + trigger_trap(dut, 0x700ULL, EXC_ECALL_FROM_U, 0ULL, false); assert(read_csr(dut, CSR_MCAUSE) == EXC_ECALL_FROM_U); assert(read_csr(dut, CSR_MTVAL) == 0ULL); assert(read_csr(dut, CSR_MEPC) == 0x700ULL); @@ -279,18 +333,16 @@ int main(int argc, char** argv) { csr_op(dut, CSR_MEDELEG, CSR_OP_WRITE, 0ULL); csr_op(dut, CSR_MEDELEG, CSR_OP_SET, (1ULL << EXC_ECALL_FROM_U)); csr_op(dut, CSR_STVEC, CSR_OP_WRITE, 0x4000ULL); + assert(read_csr(dut, CSR_MEDELEG) & (1ULL << EXC_ECALL_FROM_U)); force_priv_mode_via_mret(dut, PRIV_ENCODE_U); - dut->trap_is_ecall = 1; - dut->trap_epc = 0x900ULL; - tick(dut); - dut->trap_is_ecall = 0; - dut->trap_epc = 0; + assert(dut->csr_priv_mode == PRIV_ENCODE_U); + trigger_trap(dut, 0x900ULL, EXC_ECALL_FROM_U, 0ULL, true); + assert(dut->csr_trap_to_s_mode); + assert(dut->csr_trap_vector == 0x4000ULL); assert(read_csr(dut, CSR_SEPC) == 0x900ULL); assert(read_csr(dut, CSR_SCAUSE) == EXC_ECALL_FROM_U); assert(read_csr(dut, CSR_STVAL) == 0ULL); assert(dut->csr_priv_mode == PRIV_ENCODE_S); - assert(dut->csr_trap_to_s_mode); - assert(dut->csr_trap_vector == 0x4000ULL); std::cout << "[PASS] medeleg routes ecall to supervisor" << std::endl; force_priv_mode_via_mret(dut, PRIV_ENCODE_M); @@ -326,6 +378,208 @@ int main(int argc, char** argv) { assert((read_csr(dut, CSR_FRM) & 0x7ULL) == ((0xABULL >> 5) & 0x7ULL)); std::cout << "[PASS] FCSR/FM/FFLAGS read/write" << std::endl; + std::cout << "[INFO] Extending CSR trap coverage" << std::endl; + + run_csr_scenario(dut, "interrupt_priority_matrix", [&](Vcsr_unit* target) { + struct PriorityScenario { + const char* name; + bool ext_external; + bool ext_software; + bool ext_timer; + bool sync_illegal; + uint64_t mepc; + uint64_t mtval; + uint64_t expected_cause; + }; + + const std::array<PriorityScenario, 5> suite = {{ + {"external_over_others", true, true, true, true, 0xA000ULL, 0x1111ULL, INTERRUPT_BIT | INT_M_EXTERNAL}, + {"software_over_timer", false, true, true, true, 0xA040ULL, 0x2222ULL, INTERRUPT_BIT | INT_M_SOFTWARE}, + {"timer_over_sync", false, false, true, true, 0xA080ULL, 0x3333ULL, INTERRUPT_BIT | INT_M_TIMER}, + {"interrupt_only", true, false, false, false, 0xA0C0ULL, 0x0ULL, INTERRUPT_BIT | INT_M_EXTERNAL}, + {"sync_fallback", false, false, false, true, 0xA100ULL, 0x4444ULL, EXC_INST_ILLEGAL} + }}; + + for (const auto& scenario : suite) { + bool chose_interrupt = false; + const uint64_t chosen_cause = arbitrate_trap_priority( + scenario.ext_external, + scenario.ext_software, + scenario.ext_timer, + scenario.sync_illegal, + EXC_INST_ILLEGAL, + chose_interrupt + ); + assert(chosen_cause == scenario.expected_cause); + trigger_trap( + target, + scenario.mepc, + chosen_cause, + scenario.sync_illegal ? scenario.mtval : 0ULL, + false + ); + const uint64_t mcause = read_csr(target, CSR_MCAUSE); + assert(mcause == scenario.expected_cause); + assert(read_csr(target, CSR_MEPC) == scenario.mepc); + const bool mcause_is_interrupt = (mcause & INTERRUPT_BIT) != 0ULL; + assert(mcause_is_interrupt == chose_interrupt); + if (scenario.sync_illegal && !mcause_is_interrupt) { + assert(read_csr(target, CSR_MTVAL) == scenario.mtval); + } + assert(target->csr_priv_mode == PRIV_ENCODE_M); + } + }); + + run_csr_scenario(dut, "delegated_multitrap_context", [&](Vcsr_unit* target) { + csr_op(target, CSR_MEDELEG, CSR_OP_WRITE, 0ULL); + csr_op(target, CSR_MEDELEG, CSR_OP_SET, (1ULL << EXC_ECALL_FROM_U)); + csr_op(target, CSR_STVEC, CSR_OP_WRITE, 0x4000ULL); + csr_op(target, CSR_MTVEC, CSR_OP_WRITE, 0x8000ULL); + + force_priv_mode_via_mret(target, PRIV_ENCODE_S); + assert(target->csr_priv_mode == PRIV_ENCODE_S); + + const uint64_t delegated_pc = 0x900ULL; + trigger_trap(target, delegated_pc, EXC_ECALL_FROM_U, 0ULL, true); + assert(target->csr_trap_to_s_mode); + assert(target->csr_trap_vector == 0x4000ULL); + assert(read_csr(target, CSR_SEPC) == delegated_pc); + assert(read_csr(target, CSR_SCAUSE) == EXC_ECALL_FROM_U); + assert(target->csr_priv_mode == PRIV_ENCODE_S); + + const uint64_t machine_pc = 0xA00ULL; + trigger_trap(target, machine_pc, EXC_INST_ILLEGAL, 0ULL, false); + assert(!target->csr_trap_to_s_mode); + assert(target->csr_trap_vector == 0x8000ULL); + assert(read_csr(target, CSR_MEPC) == machine_pc); + assert(read_csr(target, CSR_SEPC) == delegated_pc); + assert(target->csr_priv_mode == PRIV_ENCODE_M); + }); + + run_csr_scenario(dut, "nested_return_sequence", [&](Vcsr_unit* target) { + force_priv_mode_via_mret(target, PRIV_ENCODE_U); + csr_op(target, CSR_SSTATUS, CSR_OP_WRITE, (1ULL << SSTATUS_SIE_BIT)); + + const uint64_t first_trap_pc = 0xB00ULL; + trigger_trap(target, first_trap_pc, EXC_ECALL_FROM_U, 0ULL, true); + uint64_t sstatus_snapshot = read_csr(target, CSR_SSTATUS); + assert(((sstatus_snapshot >> SSTATUS_SIE_BIT) & 1ULL) == 0ULL); + assert(((sstatus_snapshot >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL); + assert(((sstatus_snapshot >> SSTATUS_SPP_BIT) & 1ULL) == 0ULL); + assert(read_csr(target, CSR_SEPC) == first_trap_pc); + + target->trap_ctrl_sret_en = 1; + tick(target); + target->trap_ctrl_sret_en = 0; + uint64_t sstatus_after_sret = read_csr(target, CSR_SSTATUS); + assert(((sstatus_after_sret >> SSTATUS_SIE_BIT) & 1ULL) == 1ULL); + assert(((sstatus_after_sret >> SSTATUS_SPIE_BIT) & 1ULL) == 1ULL); + assert(target->csr_priv_mode == PRIV_ENCODE_U); + + // Enable machine interrupts so the subsequent machine trap captures pre-trap MIE=1 + csr_op(target, CSR_MSTATUS, CSR_OP_SET, (1ULL << MSTATUS_MIE_BIT)); + const uint64_t second_trap_pc = 0xB80ULL; + trigger_trap(target, second_trap_pc, EXC_INST_ILLEGAL, 0ULL, false); + uint64_t mstatus_snapshot = read_csr(target, CSR_MSTATUS); + assert(((mstatus_snapshot >> MSTATUS_MIE_BIT) & 1ULL) == 0ULL); + assert(((mstatus_snapshot >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL); + assert(((mstatus_snapshot >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U); + assert(read_csr(target, CSR_MEPC) == second_trap_pc); + + target->trap_ctrl_mret_en = 1; + tick(target); + target->trap_ctrl_mret_en = 0; + uint64_t mstatus_after_mret = read_csr(target, CSR_MSTATUS); + assert(((mstatus_after_mret >> MSTATUS_MIE_BIT) & 1ULL) == 1ULL); + assert(((mstatus_after_mret >> MSTATUS_MPIE_BIT) & 1ULL) == 1ULL); + assert(((mstatus_after_mret >> MSTATUS_MPP_LO) & 0x3ULL) == PRIV_ENCODE_U); + assert(target->csr_priv_mode == PRIV_ENCODE_U); + }); + + run_csr_scenario(dut, "illegal_csr_access_guard", [&](Vcsr_unit* target) { + force_priv_mode_via_mret(target, PRIV_ENCODE_M); + assert(target->csr_priv_mode == PRIV_ENCODE_M); + const uint64_t misa_snapshot = read_csr(target, CSR_MISA); + target->csr_addr = CSR_MISA; + target->csr_op = CSR_OP_WRITE; + target->csr_rs1_data = 0x1234ULL; + target->csr_req = 1; + tick(target); + target->csr_req = 0; + target->csr_op = 0; + target->csr_rs1_data = 0; + target->csr_addr = 0; + assert(target->csr_illegal_access); + assert(read_csr(target, CSR_MISA) == misa_snapshot); + assert(!target->csr_trap_to_s_mode); + assert(target->csr_priv_mode == PRIV_ENCODE_M); + assert(target->csr_trap_vector_next == read_csr(target, CSR_MTVEC)); + tick(target); + assert(!target->csr_illegal_access); + }); + + run_csr_scenario(dut, "delegation_cross_over", [&](Vcsr_unit* target) { + csr_op(target, CSR_MTVEC, CSR_OP_WRITE, 0x1000ULL); + csr_op(target, CSR_STVEC, CSR_OP_WRITE, 0x2000ULL); + csr_op(target, CSR_MEDELEG, CSR_OP_WRITE, 0ULL); + csr_op(target, CSR_MIDELEG, CSR_OP_WRITE, 0ULL); + + struct DelegationProbe { + const char* name; + bool is_interrupt; + uint64_t cause; + bool delegate_to_s; + uint64_t pc; + }; + + const std::array<DelegationProbe, 4> probes = {{ + {"medeleg_set", false, EXC_ECALL_FROM_U, true, 0xD000ULL}, + {"medeleg_clear", false, EXC_ECALL_FROM_U, false, 0xD040ULL}, + {"mideleg_set", true, INT_M_TIMER, true, 0xD080ULL}, + {"mideleg_clear", true, INT_M_TIMER, false, 0xD0C0ULL} + }}; + + for (const auto& probe : probes) { + if (probe.is_interrupt) { + const uint64_t mask = (1ULL << probe.cause); + if (probe.delegate_to_s) { + csr_op(target, CSR_MIDELEG, CSR_OP_SET, mask); + } else { + csr_op(target, CSR_MIDELEG, CSR_OP_CLEAR, mask); + } + force_priv_mode_via_mret(target, PRIV_ENCODE_S); + trigger_trap(target, probe.pc, (INTERRUPT_BIT | probe.cause), 0ULL, probe.delegate_to_s); + if (probe.delegate_to_s) { + assert(target->csr_trap_to_s_mode); + assert(read_csr(target, CSR_SEPC) == probe.pc); + } else { + assert(!target->csr_trap_to_s_mode); + assert(read_csr(target, CSR_MEPC) == probe.pc); + } + } else { + const uint64_t mask = (1ULL << probe.cause); + if (probe.delegate_to_s) { + csr_op(target, CSR_MEDELEG, CSR_OP_SET, mask); + } else { + csr_op(target, CSR_MEDELEG, CSR_OP_CLEAR, mask); + } + force_priv_mode_via_mret(target, PRIV_ENCODE_U); + trigger_trap(target, probe.pc, probe.cause, 0ULL, probe.delegate_to_s); + if (probe.delegate_to_s) { + assert(target->csr_trap_to_s_mode); + assert(read_csr(target, CSR_SEPC) == probe.pc); + } else { + assert(!target->csr_trap_to_s_mode); + assert(read_csr(target, CSR_MEPC) == probe.pc); + } + } + + const uint64_t expected_vector = probe.delegate_to_s ? read_csr(target, CSR_STVEC) + : read_csr(target, CSR_MTVEC); + assert(target->csr_trap_vector == expected_vector); + } + }); + std::cout << "----------------------------------\n"; std::cout << "All CSR unit tests passed!\n"; std::cout << "----------------------------------\n"; diff --git a/rv64cpu/tb/unit/tb_decoder.cc b/rv64cpu/tb/unit/tb_decoder.cc index aa6788c..25a3d69 100644 --- a/rv64cpu/tb/unit/tb_decoder.cc +++ b/rv64cpu/tb/unit/tb_decoder.cc @@ -39,7 +39,8 @@ static int fail_count = 0; } \ } while(0) -void decode(Vdecoder* dut, uint32_t instr, const char* name) { +void decode(Vdecoder* dut, uint32_t instr, const char* name, uint8_t priv_mode = 3) { + dut->priv_mode = priv_mode & 0x3; dut->instr = instr; dut->eval(); test_count++; @@ -233,6 +234,10 @@ int main(int argc, char** argv) { CHECK(dut, reg_write_en, 1, "CSRRW"); CHECK(dut, illegal_instr,0, "CSRRW"); + decode(dut, 0x300110F3, "CSRRW x1, mstatus, x2 (U-mode)", 0); + CHECK(dut, illegal_instr,1, "CSRRW U-mode illegal"); + CHECK(dut, is_csr, 0, "CSRRW U-mode is_csr cleared"); + // 20. ECALL // all-zero fields + opcode=1110011 decode(dut, 0x00000073, "ECALL"); @@ -256,7 +261,7 @@ int main(int argc, char** argv) { // 23. SRET // funct7=0001000 rs2=00010 rs1=00000 funct3=000 rd=00000 op=1110011 - decode(dut, 0x10200073, "SRET"); + decode(dut, 0x10200073, "SRET", 1); CHECK(dut, is_sret, 1, "SRET"); CHECK(dut, reg_write_en, 0, "SRET"); diff --git a/rv64cpu/tb/unit/tb_hazard_unit.cc b/rv64cpu/tb/unit/tb_hazard_unit.cc index 69ebab9..d43ccc5 100644 --- a/rv64cpu/tb/unit/tb_hazard_unit.cc +++ b/rv64cpu/tb/unit/tb_hazard_unit.cc @@ -42,9 +42,13 @@ void reset(Vhazard_unit* dut) { dut->ex_reg_write_en = 0; dut->ex_is_load = 0; dut->ex_branch_taken = 0; + dut->ex_branch_target = 0; dut->mem_rd_addr = 0; dut->mem_reg_write_en = 0; dut->mem_stall_req = 0; + dut->trap_flush_req = 0; + dut->trap_pc_sel = 0; + dut->trap_target_pc = 0; dut->wb_rd_addr = 0; dut->wb_reg_write_en = 0; dut->eval(); @@ -69,6 +73,8 @@ int main(int argc, char** argv) { check(dut->id_ex_flush_en == 0, "NoHazard_NoIDEXFlush"); check(dut->id_ex_stall_en == 0, "NoHazard_NoIDEXStall"); check(dut->ex_mem_stall_en == 0, "NoHazard_NoEXMEMStall"); + check(dut->pc_redirect_sel == 0, "NoHazard_NoRedirect"); + check(dut->pc_redirect_is_trap == 0, "NoHazard_NoTrapRedirect"); // ----------------------- // 2. Forward A from MEM stage @@ -231,11 +237,15 @@ int main(int argc, char** argv) { // ----------------------- reset(dut); dut->ex_branch_taken = 1; + dut->ex_branch_target = 0x400; dut->eval(); check(dut->if_id_flush_en == 1, "Branch_IFID_Flush"); check(dut->id_ex_flush_en == 1, "Branch_IDEX_Flush"); check(dut->if_stall_en == 0, "Branch_IF_NoStall"); check(dut->if_id_stall_en == 0, "Branch_IFID_NoStall"); + check(dut->pc_redirect_sel == 1, "Branch_Redirect_Valid"); + check(dut->pc_redirect_is_trap == 0, "Branch_Redirect_NotTrap"); + check(dut->pc_redirect_target == 0x400, "Branch_Redirect_Target"); // ----------------------- // 15. Branch overrides load-use stall @@ -282,12 +292,48 @@ int main(int argc, char** argv) { // ----------------------- reset(dut); dut->ex_branch_taken = 1; + dut->ex_branch_target = 0x410; dut->mem_stall_req = 1; dut->eval(); check(dut->if_id_flush_en == 1, "Branch_MemStall_IFID_Flush"); check(dut->id_ex_flush_en == 1, "Branch_MemStall_IDEX_Flush"); check(dut->if_stall_en == 0, "Branch_MemStall_IF_NoStall"); + // ----------------------- + // 19. Trap redirect overrides branch and stalls + // ----------------------- + reset(dut); + dut->trap_pc_sel = 1; + dut->trap_flush_req = 1; + dut->trap_target_pc = 0x800; + dut->ex_branch_taken = 1; + dut->ex_branch_target = 0x900; + dut->mem_stall_req = 1; + dut->eval(); + check(dut->pc_redirect_sel == 1, "Trap_Redirect_Valid"); + check(dut->pc_redirect_is_trap == 1, "Trap_Redirect_IsTrap"); + check(dut->pc_redirect_target == 0x800, "Trap_Redirect_Target"); + check(dut->if_id_flush_en == 1, "Trap_Flush_IFID"); + check(dut->id_ex_flush_en == 1, "Trap_Flush_IDEX"); + check(dut->if_stall_en == 0, "Trap_Clears_IF_Stall"); + check(dut->if_id_stall_en == 0, "Trap_Clears_IFID_Stall"); + check(dut->id_ex_stall_en == 0, "Trap_Clears_IDEX_Stall"); + check(dut->ex_mem_stall_en == 0, "Trap_Clears_EXMEM_Stall"); + + // ----------------------- + // 20. Trap flush overrides load-use stall even without redirect + // ----------------------- + reset(dut); + dut->trap_flush_req = 1; + dut->ex_is_load = 1; + dut->ex_rd_addr = 4; + dut->id_rs1_addr = 4; + dut->eval(); + check(dut->if_id_flush_en == 1, "TrapFlush_IFID"); + check(dut->id_ex_flush_en == 1, "TrapFlush_IDEX"); + check(dut->if_stall_en == 0, "TrapFlush_NoIFStall"); + check(dut->if_id_stall_en == 0, "TrapFlush_NoIFIDStall"); + std::cout << "----------------------------------" << std::endl; std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl; std::cout << "----------------------------------" << std::endl; diff --git a/rv64cpu/tb/unit/tb_if_stage.cc b/rv64cpu/tb/unit/tb_if_stage.cc index ebb888a..f0110c9 100644 --- a/rv64cpu/tb/unit/tb_if_stage.cc +++ b/rv64cpu/tb/unit/tb_if_stage.cc @@ -51,8 +51,9 @@ int main(int argc, char** argv) { // --- 1. Init / Reset --- dut->stall_en = 0; - dut->ex_branch_taken = 0; - dut->ex_branch_target = 0; + dut->pc_redirect_sel = 0; + dut->pc_redirect_target = 0; + dut->pc_redirect_is_trap = 0; dut->rst = 1; tick(dut); @@ -78,13 +79,14 @@ int main(int argc, char** argv) { check_pc(0x8000000CULL, dut->if_pc, "Release_Stall_Normal_Increment"); // --- 4. Branch Taken --- - dut->ex_branch_taken = 1; - dut->ex_branch_target = 0x80000100ULL; + dut->pc_redirect_sel = 1; + dut->pc_redirect_is_trap = 0; + dut->pc_redirect_target = 0x80000100ULL; tick(dut); check_pc(0x80000100ULL, dut->if_pc, "Branch_Taken_Updates_PC"); // Return to normal - dut->ex_branch_taken = 0; + dut->pc_redirect_sel = 0; tick(dut); check_pc(0x80000104ULL, dut->if_pc, "Post_Branch_Normal_Increment"); @@ -92,8 +94,9 @@ int main(int argc, char** argv) { // If a branch is taken but the pipeline is stalled, the PC should freeze. // However, if the stall clears later, the branch target should theoretically be taken. // In our RV64 simple pipeline design, stall_en freezes the PC reg completely. - dut->ex_branch_taken = 1; - dut->ex_branch_target = 0x80000200ULL; + dut->pc_redirect_sel = 1; + dut->pc_redirect_is_trap = 0; + dut->pc_redirect_target = 0x80000200ULL; dut->stall_en = 1; tick(dut); check_pc(0x80000104ULL, dut->if_pc, "Stall_Overrides_Branch_Target_Load"); @@ -102,6 +105,24 @@ int main(int argc, char** argv) { tick(dut); check_pc(0x80000200ULL, dut->if_pc, "Branch_Target_Loaded_After_Stall_Clears"); + // --- 6. Trap redirect overrides stall --- + dut->pc_redirect_sel = 0; + dut->pc_redirect_is_trap = 0; + dut->pc_redirect_target = 0; + + dut->stall_en = 1; + dut->pc_redirect_sel = 1; + dut->pc_redirect_is_trap = 1; + dut->pc_redirect_target = 0x80000300ULL; + tick(dut); + check_pc(0x80000300ULL, dut->if_pc, "Trap_Overrides_Stall"); + + dut->stall_en = 0; + dut->pc_redirect_sel = 0; + dut->pc_redirect_is_trap = 0; + tick(dut); + check_pc(0x80000304ULL, dut->if_pc, "Resume_After_Trap"); + std::cout << "----------------------------------" << std::endl; std::cout << "\033[32mAll tests passed!\033[0m" << std::endl; std::cout << "----------------------------------" << std::endl; diff --git a/rv64cpu/tb/unit/tb_trap_ctrl.cc b/rv64cpu/tb/unit/tb_trap_ctrl.cc index 142d743..1708598 100644 --- a/rv64cpu/tb/unit/tb_trap_ctrl.cc +++ b/rv64cpu/tb/unit/tb_trap_ctrl.cc @@ -28,6 +28,8 @@ namespace { constexpr uint64_t EXC_INST_ILLEGAL = 2ULL; constexpr uint64_t EXC_ECALL_FROM_S = 9ULL; constexpr uint64_t EXC_LOAD_PAGE_FAULT= 13ULL; +constexpr uint64_t EXC_LOAD_FAULT = 5ULL; +constexpr uint64_t EXC_STORE_FAULT = 7ULL; constexpr uint64_t INT_M_SOFTWARE = 3ULL; constexpr uint64_t INT_M_TIMER = 7ULL; @@ -125,6 +127,20 @@ int main(int argc, char** argv) { check(dut->trap_mcause == EXC_LOAD_PAGE_FAULT, "PageFault_Mcause"); check(dut->trap_mtval == 0xBADC0FFEE0ULL, "PageFault_Mtval"); + drive_defaults(dut); + dut->trap_load_fault = 1; + dut->trap_bad_addr = 0xABCDEF0010ULL; + dut->eval(); + check(dut->trap_mcause == EXC_LOAD_FAULT, "LoadFault_Mcause"); + check(dut->trap_mtval == 0xABCDEF0010ULL, "LoadFault_Mtval"); + + drive_defaults(dut); + dut->trap_store_fault = 1; + dut->trap_bad_addr = 0xABCDEF0020ULL; + dut->eval(); + check(dut->trap_mcause == EXC_STORE_FAULT, "StoreFault_Mcause"); + check(dut->trap_mtval == 0xABCDEF0020ULL, "StoreFault_Mtval"); + // --------------------------------------------------------------------- // 2) Interrupt priority + delegation to supervisor // --------------------------------------------------------------------- diff --git a/rv64cpu/tb/unit/tb_wb_stage.cc b/rv64cpu/tb/unit/tb_wb_stage.cc index 0f228ee..3f6bedb 100644 --- a/rv64cpu/tb/unit/tb_wb_stage.cc +++ b/rv64cpu/tb/unit/tb_wb_stage.cc @@ -58,12 +58,17 @@ int main(int argc, char** argv) { dut->mem_wb_rs1_rdata = 0x99; dut->mem_wb_rs1_addr = 1; dut->mem_wb_imm = 0x123; + dut->mem_wb_instr = 0xDEADBEEFu; dut->mem_wb_illegal_instr = 0; dut->mem_wb_is_ecall = 0; dut->mem_wb_is_ebreak = 0; dut->mem_wb_is_mret = 0; dut->mem_wb_is_sret = 0; + dut->mem_wb_load_fault = 0; + dut->mem_wb_store_fault = 0; + dut->mem_wb_fault_addr = 0; dut->csr_rdata = 0x0; + dut->csr_illegal_access = 0; tick(dut); @@ -118,9 +123,37 @@ int main(int argc, char** argv) { check(dut->trap_is_mret == 1, "Trap_MRET"); check(dut->trap_is_sret == 1, "Trap_SRET"); check(dut->trap_epc == 0x80000000, "Trap_EPC"); + check(dut->trap_bad_instr == 0xDEADBEEFu, "Trap_BadInstr_Payload"); check(dut->wb_reg_write_en == 0, "Trap_Gates_RegWriteEn"); check(dut->csr_req == 0, "Trap_Gates_CSR_Req"); + // 7. CSR illegal access should raise an illegal-instr trap + dut->mem_wb_illegal_instr = 0; + dut->mem_wb_is_ecall = 0; + dut->mem_wb_is_ebreak = 0; + dut->csr_illegal_access = 1; + tick(dut); + check(dut->trap_illegal_instr == 1, "CSR_Illegal_Raises_Trap"); + check(dut->wb_reg_write_en == 0, "CSR_Illegal_Kills_Write"); + + // 8. Load/store faults propagate to trap outputs and block writeback + dut->csr_illegal_access = 0; + dut->mem_wb_load_fault = 1; + dut->mem_wb_fault_addr = 0x80000010ULL; + tick(dut); + check(dut->trap_load_fault == 1, "LoadFault_AssertsTrap"); + check(dut->trap_bad_addr == 0x80000010ULL, "LoadFault_PassesAddress"); + check(dut->wb_reg_write_en == 0, "LoadFault_KillsWrite"); + + dut->mem_wb_load_fault = 0; + dut->mem_wb_store_fault = 1; + dut->mem_wb_fault_addr = 0x80000020ULL; + tick(dut); + check(dut->trap_store_fault == 1, "StoreFault_AssertsTrap"); + check(dut->trap_bad_addr == 0x80000020ULL, "StoreFault_PassesAddress"); + + dut->mem_wb_store_fault = 0; + std::cout << "----------------------------------" << std::endl; std::cout << "\033[32mAll " << pass_count << " tests passed!\033[0m" << std::endl; std::cout << "----------------------------------" << std::endl; |
