aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-04-01 00:58:22 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-04-01 00:58:22 +0800
commitcf0476e565c74082f011a6c428095e60a743b822 (patch)
treea5b5feaa2228d4eb1e90213d07e709bd4f12bf97 /rv64cpu
parentab6a21ae1099bff120111b8a9dcfa50c9fcad5a1 (diff)
downloadrcc-project-cf0476e565c74082f011a6c428095e60a743b822.tar.gz
rcc-project-cf0476e565c74082f011a6c428095e60a743b822.tar.xz
feat: Complete CSR/privileged/exception interrupt path and fill in strict acceptance tests
- Refactor trap/CSR data path in the Core, unified with trap_ctrl interface to drive CSR update and return process - Improve critical paths for exception/interrupt handling: ecall, illegal, load fault, external interrupt, mret/sret return - Adjust pipeline stage registers with flush/stall coordination, fix residual control signal issues in trap scenarios - Enhance decode and related pipeline module behavior consistency, fill in missing control signal propagation across registers - Expand core integration testing, add trap/interrupt scenarios and CLI dispatch that can be executed per case - Extend CSR unit test coverage: read/write semantics, privilege switching, delegation and nested returns, illegal access protection, etc. - Register new core scenario cases in CTest to form a regression-capable P2 strict acceptance suite Verification: - Build passed - Full CTest passed (22/22)
Diffstat (limited to 'rv64cpu')
-rw-r--r--rv64cpu/rtl/core/csr_unit.sv118
-rw-r--r--rv64cpu/rtl/core/decoder.sv57
-rw-r--r--rv64cpu/rtl/core/ex_mem_reg.sv5
-rw-r--r--rv64cpu/rtl/core/hazard_unit.sv92
-rw-r--r--rv64cpu/rtl/core/id_ex_reg.sv9
-rw-r--r--rv64cpu/rtl/core/id_stage.sv2
-rw-r--r--rv64cpu/rtl/core/if_stage.sv23
-rw-r--r--rv64cpu/rtl/core/mem_stage.sv33
-rw-r--r--rv64cpu/rtl/core/mem_wb_reg.sv21
-rw-r--r--rv64cpu/rtl/core/richard_core.sv161
-rw-r--r--rv64cpu/rtl/core/wb_stage.sv24
-rw-r--r--rv64cpu/tb/CMakeLists.txt7
-rw-r--r--rv64cpu/tb/core/tb_core.cc425
-rw-r--r--rv64cpu/tb/unit/tb_csr.cc354
-rw-r--r--rv64cpu/tb/unit/tb_decoder.cc9
-rw-r--r--rv64cpu/tb/unit/tb_hazard_unit.cc46
-rw-r--r--rv64cpu/tb/unit/tb_if_stage.cc35
-rw-r--r--rv64cpu/tb/unit/tb_trap_ctrl.cc16
-rw-r--r--rv64cpu/tb/unit/tb_wb_stage.cc33
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;