diff --git a/ci/vars.yml b/ci/vars.yml index b5cfb2976e..0cc5bbefd4 100644 --- a/ci/vars.yml +++ b/ci/vars.yml @@ -7,7 +7,7 @@ # end up as float otherwise). variables: VERILATOR_VERSION: "v4.104" - IBEX_COSIM_VERSION: "0dc2de5d" + IBEX_COSIM_VERSION: "6272327d" RISCV_TOOLCHAIN_TAR_VERSION: "20220210-1" RISCV_TOOLCHAIN_TAR_VARIANT: "lowrisc-toolchain-gcc-rv32imcb" RISCV_COMPLIANCE_GIT_VERSION: "844c6660ef3f0d9b96957991109dfd80cc4938e2" diff --git a/dv/cosim/cosim.h b/dv/cosim/cosim.h index 1245f6c415..cbb0b01ba9 100644 --- a/dv/cosim/cosim.h +++ b/dv/cosim/cosim.h @@ -95,6 +95,12 @@ class Cosim { // When an NMI is due to be taken that will occur at the next call of `step`. virtual void set_nmi(bool nmi) = 0; + // Set the state of the internal NMI (non-maskable interrupt) line. + // Behaviour wise this is almost as same as external NMI case explained at + // set_nmi method. Difference is that this one is a response from Ibex rather + // than an input. + virtual void set_nmi_int(bool nmi_int) = 0; + // Set the debug request. // // When set to true the core will enter debug mode at the next step diff --git a/dv/cosim/cosim_dpi.cc b/dv/cosim/cosim_dpi.cc index 2d628f0c83..f2fe48c1cb 100644 --- a/dv/cosim/cosim_dpi.cc +++ b/dv/cosim/cosim_dpi.cc @@ -28,6 +28,11 @@ void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi) { cosim->set_nmi(nmi); } +void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int) { + assert(cosim); + + cosim->set_nmi_int(nmi_int); +} void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req) { assert(cosim); diff --git a/dv/cosim/cosim_dpi.h b/dv/cosim/cosim_dpi.h index df7600bfd3..29a7e25c3a 100644 --- a/dv/cosim/cosim_dpi.h +++ b/dv/cosim/cosim_dpi.h @@ -17,6 +17,7 @@ int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg, svBit sync_trap); void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip); void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi); +void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int); void riscv_cosim_set_debug_req(Cosim *cosim, svBit debug_req); void riscv_cosim_set_mcycle(Cosim *cosim, svBitVecVal *mcycle); void riscv_cosim_set_csr(Cosim *cosim, const int csr_id, diff --git a/dv/cosim/cosim_dpi.svh b/dv/cosim/cosim_dpi.svh index 6a6f1a5814..44361cb557 100644 --- a/dv/cosim/cosim_dpi.svh +++ b/dv/cosim/cosim_dpi.svh @@ -14,6 +14,7 @@ import "DPI-C" function int riscv_cosim_step(chandle cosim_handle, bit [4:0] wri bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap); import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] mip); import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi); +import "DPI-C" function void riscv_cosim_set_nmi_int(chandle cosim_handle, bit nmi_int); import "DPI-C" function void riscv_cosim_set_debug_req(chandle cosim_handle, bit debug_req); import "DPI-C" function void riscv_cosim_set_mcycle(chandle cosim_handle, bit [63:0] mcycle); import "DPI-C" function void riscv_cosim_set_csr(chandle cosim_handle, int csr_id, diff --git a/dv/cosim/spike_cosim.cc b/dv/cosim/spike_cosim.cc index a5509c52fb..7711498a34 100644 --- a/dv/cosim/spike_cosim.cc +++ b/dv/cosim/spike_cosim.cc @@ -200,7 +200,8 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, if (processor->get_state()->last_inst_pc == PC_INVALID) { if (!(processor->get_state()->mcause->read() & 0x80000000) || - processor->get_state()->debug_mode) { // (Async-Traps are disabled in debug mode) + processor->get_state() + ->debug_mode) { // (Async-Traps are disabled in debug mode) // Spike encountered a synchronous trap pending_sync_exception = true; @@ -358,6 +359,12 @@ bool SpikeCosim::check_sync_trap(uint32_t write_reg, return false; } + // If we see an internal NMI, that means we receive an extra memory intf item. + // Deleting that is necessary since next Load/Store would fail otherwise. + if (processor->get_state()->mcause->read() == 0xFFFFFFE0) { + pending_dside_accesses.erase(pending_dside_accesses.begin()); + } + // Errors may have been generated outside of step() (e.g. in // check_mem_access()), return false if there are any. if (errors.size() != 0) { @@ -480,6 +487,20 @@ void SpikeCosim::set_nmi(bool nmi) { } } +void SpikeCosim::set_nmi_int(bool nmi_int) { + if (nmi_int && !nmi_mode && !processor->get_state()->debug_mode) { + processor->get_state()->nmi_int = true; + nmi_mode = true; + + // When NMI is set it is guaranteed NMI trap will be taken at the next step + // so save CSR state for recoverable NMI to mstack now. + mstack.mpp = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPP); + mstack.mpie = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPIE); + mstack.epc = processor->get_csr(CSR_MEPC); + mstack.cause = processor->get_csr(CSR_MCAUSE); + } +} + void SpikeCosim::set_debug_req(bool debug_req) { processor->halt_request = debug_req ? processor_t::HR_REGULAR : processor_t::HR_NONE; diff --git a/dv/cosim/spike_cosim.h b/dv/cosim/spike_cosim.h index 53f03f999f..76f8066a4f 100644 --- a/dv/cosim/spike_cosim.h +++ b/dv/cosim/spike_cosim.h @@ -102,6 +102,7 @@ class SpikeCosim : public simif_t, public Cosim { uint32_t initial_spike_pc); void set_mip(uint32_t mip) override; void set_nmi(bool nmi) override; + void set_nmi_int(bool nmi_int) override; void set_debug_req(bool debug_req) override; void set_mcycle(uint64_t mcycle) override; void set_csr(const int csr_num, const uint32_t new_val) override; diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv index dd45ba040c..8ff2be4936 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv @@ -122,6 +122,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard; end riscv_cosim_set_nmi(cosim_handle, rvfi_instr.nmi); + riscv_cosim_set_nmi_int(cosim_handle, rvfi_instr.nmi_int); riscv_cosim_set_mip(cosim_handle, rvfi_instr.mip); riscv_cosim_set_debug_req(cosim_handle, rvfi_instr.debug_req); riscv_cosim_set_mcycle(cosim_handle, rvfi_instr.mcycle); diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv index 76fa36f9f4..83c99afeb4 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv @@ -38,6 +38,7 @@ class ibex_rvfi_monitor extends uvm_monitor; trans_collected.order = vif.monitor_cb.order; trans_collected.mip = vif.monitor_cb.ext_mip; trans_collected.nmi = vif.monitor_cb.ext_nmi; + trans_collected.nmi_int = vif.monitor_cb.ext_nmi_int; trans_collected.debug_req = vif.monitor_cb.ext_debug_req; trans_collected.mcycle = vif.monitor_cb.ext_mcycle; trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid; diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv index 9532ee346a..5356023248 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv @@ -10,6 +10,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item; bit [63:0] order; bit [31:0] mip; bit nmi; + bit nmi_int; bit debug_req; bit [63:0] mcycle; @@ -25,6 +26,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item; `uvm_field_int (order, UVM_DEFAULT) `uvm_field_int (mip, UVM_DEFAULT) `uvm_field_int (nmi, UVM_DEFAULT) + `uvm_field_int (nmi_int, UVM_DEFAULT) `uvm_field_int (debug_req, UVM_DEFAULT) `uvm_field_int (mcycle, UVM_DEFAULT) `uvm_field_sarray_int (mhpmcounters, UVM_DEFAULT) diff --git a/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_response_seq_lib.sv b/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_response_seq_lib.sv index d5f5a0dc0a..deacdf73a2 100644 --- a/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_response_seq_lib.sv +++ b/dv/uvm/core_ibex/common/ibex_mem_intf_agent/ibex_mem_intf_response_seq_lib.sv @@ -10,6 +10,7 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item); ibex_mem_intf_seq_item item; mem_model m_mem; + bit enable_intg_error = 1'b0; bit enable_error = 1'b0; // Used to ensure that whenever inject_error() is called, the very next transaction will inject an // error, and that enable_error will not be flipped back to 0 immediately @@ -60,6 +61,12 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item); enable_error = 1'b0; error_synch = 1'b1; aligned_addr = {req.addr[DATA_WIDTH-1:2], 2'b0}; + // Do not inject any error to the handshake test_control_addr + // TODO: Parametrize this. Until then, this needs to be changed manually. + if (aligned_addr inside {32'h8ffffff8, 32'h8ffffffc}) begin + req.error = 1'b0; + enable_intg_error = 1'b0; + end if (req.error) begin `DV_CHECK_STD_RANDOMIZE_FATAL(rand_data) req.data = rand_data; @@ -85,7 +92,10 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item); // If data_was_uninitialized is true then we want to force bad integrity bits: invert the // correct ones, which we know will break things for the codes we use. - if (data_was_uninitialized) req.intg = ~req.intg; + if (data_was_uninitialized || enable_intg_error) begin + req.intg = ~req.intg; + enable_intg_error = 1'b0; + end `uvm_info(get_full_name(), $sformatf("Response transfer:\n%0s", req.sprint()), UVM_HIGH) start_item(req); @@ -106,6 +116,10 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item); this.enable_error = 1'b1; endfunction + virtual function void inject_intg_error(); + this.enable_intg_error = 1'b1; + endfunction + virtual function bit get_error_synch(); return this.error_synch; endfunction diff --git a/dv/uvm/core_ibex/env/core_ibex_env_cfg.sv b/dv/uvm/core_ibex/env/core_ibex_env_cfg.sv index a717c8a7c7..fba3b1bb49 100644 --- a/dv/uvm/core_ibex/env/core_ibex_env_cfg.sv +++ b/dv/uvm/core_ibex/env/core_ibex_env_cfg.sv @@ -7,6 +7,7 @@ class core_ibex_env_cfg extends uvm_object; virtual clk_rst_if ibex_clk_vif; virtual core_ibex_dut_probe_if ibex_dut_vif; + bit enable_mem_intg_err; bit enable_irq_single_seq; bit enable_irq_multiple_seq; bit enable_irq_nmi_seq; @@ -31,6 +32,7 @@ class core_ibex_env_cfg extends uvm_object; `uvm_object_utils_begin(core_ibex_env_cfg) `uvm_field_int(enable_double_fault_detector, UVM_DEFAULT) `uvm_field_int(is_double_fault_detected_fatal, UVM_DEFAULT) + `uvm_field_int(enable_mem_intg_err, UVM_DEFAULT) `uvm_field_int(enable_irq_single_seq, UVM_DEFAULT) `uvm_field_int(enable_irq_multiple_seq, UVM_DEFAULT) `uvm_field_int(enable_irq_nmi_seq, UVM_DEFAULT) @@ -48,6 +50,7 @@ class core_ibex_env_cfg extends uvm_object; super.new(name); void'($value$plusargs("enable_double_fault_detector=%0d", enable_double_fault_detector)); void'($value$plusargs("is_double_fault_detected_fatal=%0d", is_double_fault_detected_fatal)); + void'($value$plusargs("enable_mem_intg_err=%0d", enable_mem_intg_err)); void'($value$plusargs("enable_irq_single_seq=%0d", enable_irq_single_seq)); void'($value$plusargs("enable_irq_multiple_seq=%0d", enable_irq_multiple_seq)); void'($value$plusargs("enable_irq_nmi_seq=%0d", enable_irq_nmi_seq)); diff --git a/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv b/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv index 7e58df4379..c6ec6ea621 100644 --- a/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv +++ b/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv @@ -28,6 +28,7 @@ interface core_ibex_rvfi_if(input logic clk); logic [31:0] mem_wdata; logic [31:0] ext_mip; logic ext_nmi; + logic ext_nmi_int; logic [31:0] ext_debug_req; logic [63:0] ext_mcycle; @@ -61,6 +62,7 @@ interface core_ibex_rvfi_if(input logic clk); input mem_wdata; input ext_mip; input ext_nmi; + input ext_nmi_int; input ext_debug_req; input ext_mcycle; input ext_mhpmcounters; diff --git a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml index 4d126337b5..792d196141 100644 --- a/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml @@ -549,6 +549,27 @@ +suppress_pmp_setup=1 rtl_test: core_ibex_mem_error_test sim_opts: > + +enable_mem_intg_err=0 + +enable_double_fault_detector=0 + +require_signature_addr=1 + compare_opts: + compare_final_value_only: 1 + +- test: riscv_mem_intg_error_test + description: > + Normal random instruction test, but randomly insert memory load/store integrity errors + iterations: 15 + gen_test: riscv_rand_instr_test + gen_opts: > + +require_signature_addr=1 + +instr_cnt=10000 + +randomize_csr=1 + +enable_unaligned_load_store=1 + +suppress_pmp_setup=1 + rtl_test: core_ibex_mem_error_test + sim_opts: > + +enable_mem_intg_err=1 + +enable_double_fault_detector=0 +require_signature_addr=1 compare_opts: compare_final_value_only: 1 diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index 3962609ca0..6d39534816 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -158,8 +158,9 @@ module core_ibex_tb_top; ); // We should never see any alerts triggered in normal testing - `ASSERT(NoAlertsTriggered, + `ASSERT(NoAlertsTriggered_A, !dut_if.alert_minor && !dut_if.alert_major_internal && !dut_if.alert_major_bus, clk, !rst_n) + `DV_ASSERT_CTRL("NoAlertsTriggered", core_ibex_tb_top.NoAlertsTriggered_A) // Data load/store vif connection assign data_mem_vif.reset = ~rst_n; @@ -184,13 +185,14 @@ module core_ibex_tb_top; assign rvfi_if.rd_addr = dut.rvfi_rd_addr; assign rvfi_if.rd_wdata = dut.rvfi_rd_wdata; assign rvfi_if.pc_rdata = dut.rvfi_pc_rdata; - assign rvfi_if_pc_wdata = dut.rvfi_pc_wdata; + assign rvfi_if.pc_wdata = dut.rvfi_pc_wdata; assign rvfi_if.mem_addr = dut.rvfi_mem_addr; assign rvfi_if.mem_rmask = dut.rvfi_mem_rmask; assign rvfi_if.mem_rdata = dut.rvfi_mem_rdata; assign rvfi_if.mem_wdata = dut.rvfi_mem_wdata; assign rvfi_if.ext_mip = dut.rvfi_ext_mip; assign rvfi_if.ext_nmi = dut.rvfi_ext_nmi; + assign rvfi_if.ext_nmi_int = dut.rvfi_ext_nmi_int; assign rvfi_if.ext_debug_req = dut.rvfi_ext_debug_req; assign rvfi_if.ext_mcycle = dut.rvfi_ext_mcycle; assign rvfi_if.ext_mhpmcounters = dut.rvfi_ext_mhpmcounters; diff --git a/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv index 6b372ea5d3..ed14ad0dcd 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_new_seq_lib.sv @@ -63,6 +63,8 @@ class core_base_new_seq #(type REQ = uvm_sequence_item) extends uvm_sequence #(R `uvm_info(`gfn, $sformatf("Running the \"%s\" schedule for stimulus generation", iteration_modes.name()), UVM_LOW) + stop_seq = 1'b0; + seq_finished = 1'b0; case (iteration_modes) SingleRun: begin drive_stimulus(); @@ -72,6 +74,7 @@ class core_base_new_seq #(type REQ = uvm_sequence_item) extends uvm_sequence #(R `DV_CHECK_FATAL(iteration_cnt != 0) `uvm_info(`gfn, $sformatf("Number of stimulus iterations = %0d", iteration_cnt), UVM_LOW) for (int i = 0; i <= iteration_cnt; i++) begin + `uvm_info(`gfn, $sformatf("Running %0d/%0d", i, iteration_cnt), UVM_LOW) drive_stimulus(); end end @@ -198,30 +201,40 @@ class debug_new_seq extends core_base_new_seq#(irq_seq_item); endclass -class memory_error_seq extends core_base_new_seq#(irq_seq_item); +class memory_error_seq extends core_base_new_seq#(ibex_mem_intf_seq_item); core_ibex_vseq vseq; rand bit choose_side; bit start_seq = 0; // Use this bit to start any unique sequence once rand error_type_e err_type = PickErr; + rand bit inject_intg_err; + // CONTROL_KNOB: Configure the rate between seeing an integrity error versus seeing a bus error. + int unsigned intg_err_pct = 50; + constraint inject_intg_err_c { + inject_intg_err dist {1 :/ intg_err_pct, + 0 :/ 100 - intg_err_pct}; + } `uvm_object_utils(memory_error_seq) `uvm_declare_p_sequencer(core_ibex_vseqr) function new (string name = ""); super.new(name); - vseq = core_ibex_vseq::type_id::create("vseq"); endfunction virtual task send_req(); - case (err_type) - IsideErr: begin + `DV_CHECK_MEMBER_RANDOMIZE_FATAL(inject_intg_err) + // If we expect to see only bus errors, we can enable this assertion. Otherwise + // integrity errors would cause alerts to trigger. + `DV_ASSERT_CTRL_REQ("NoAlertsTriggered", intg_err_pct == 0) + case ({err_type, inject_intg_err}) + {IsideErr, 1'b0}: begin vseq.instr_intf_seq.inject_error(); end - DsideErr: begin + {DsideErr, 1'b0}: begin vseq.data_intf_seq.inject_error(); end - PickErr: begin + {PickErr, 1'b0}: begin `DV_CHECK_STD_RANDOMIZE_FATAL(choose_side) if (choose_side) begin vseq.instr_intf_seq.inject_error(); @@ -229,14 +242,24 @@ class memory_error_seq extends core_base_new_seq#(irq_seq_item); vseq.data_intf_seq.inject_error(); end end + {IsideErr, 1'b1}: begin + vseq.instr_intf_seq.inject_intg_error(); + end + {DsideErr, 1'b1}: begin + vseq.data_intf_seq.inject_intg_error(); + end + {PickErr, 1'b1}: begin + `DV_CHECK_STD_RANDOMIZE_FATAL(choose_side) + if (choose_side) begin + vseq.instr_intf_seq.inject_intg_error(); + end else begin + vseq.data_intf_seq.inject_intg_error(); + end + end default: begin // DO nothing end endcase - if(!start_seq) begin - vseq.start(p_sequencer); - start_seq = 1; - end endtask endclass: memory_error_seq diff --git a/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv b/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv index d4dc0a1311..c9e98e11d2 100644 --- a/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv +++ b/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv @@ -1167,93 +1167,32 @@ class core_ibex_mem_error_test extends core_ibex_directed_test; `uvm_component_utils(core_ibex_mem_error_test) `uvm_component_new - int err_delay; - - // check memory error inputs and verify that core jumps to correct exception handler virtual task check_stimulus(); - forever begin - while (!vseq.data_intf_seq.get_error_synch()) begin - clk_vif.wait_clks(1); + memory_error_seq memory_error_seq_h; + memory_error_seq_h = memory_error_seq::type_id::create("memory_error_seq_h", this); + + `uvm_info(`gfn, "Running core_ibex_mem_error_test", UVM_LOW) + memory_error_seq_h.vseq = vseq; + memory_error_seq_h.iteration_modes = InfiniteRuns; + memory_error_seq_h.stimulus_delay_cycles_min = 800; // Interval between injected errors + memory_error_seq_h.stimulus_delay_cycles_max = 5000; + memory_error_seq_h.intg_err_pct = cfg.enable_mem_intg_err ? 75 : 0; + fork + begin + forever begin + memory_error_seq_h.start(env.vseqr); + // Wait until we are out of IRQ handler to the inject errors + wait_ret("mret", 20000); + end end - vseq.data_intf_seq.inject_error(); - `uvm_info(`gfn, "Injected dmem error", UVM_LOW) - // Dmem interface error could be either a load or store operation - check_dmem_fault(); - // Random delay before injecting instruction fetch fault - `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(err_delay, err_delay inside { [50:200] };) - clk_vif.wait_clks(err_delay); - inject_imem_error(); - check_imem_fault(); - // Random delay before injecting this series of errors again - `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(err_delay, err_delay inside { [250:750] };) - clk_vif.wait_clks(err_delay); - end - endtask - - virtual task inject_imem_error(); - while (!vseq.instr_intf_seq.get_error_synch()) begin - clk_vif.wait_clks(1); - end - `uvm_info(`gfn, "Injecting imem fault", UVM_LOW) - vseq.instr_intf_seq.inject_error(); - endtask - - virtual task check_dmem_fault(); - bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mcause; - core_status_t mem_status; - ibex_pkg::exc_cause_t exc_type; - // Don't impose a timeout period for dmem check, since dmem errors injected by the sequence are - // not guaranteed to be reflected in RTL state until the next memory instruction is executed, - // and the frequency of which is not controllable by the testbench - check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to exception handler"); - check_priv_mode(PRIV_LVL_M); - // Next write of CORE_STATUS will be the load/store fault type - wait_for_mem_txn(cfg.signature_addr, CORE_STATUS); - mem_status = core_status_t'(signature_data_q.pop_front()); - if (mem_status == LOAD_FAULT_EXCEPTION) begin - exc_type = ExcCauseLoadAccessFault; - end else if (mem_status == STORE_FAULT_EXCEPTION) begin - exc_type = ExcCauseStoreAccessFault; - end - check_mcause(1'b0, exc_type.lower_cause); - wait (dut_vif.dut_cb.mret === 1'b1); - `uvm_info(`gfn, "exiting mem fault checker", UVM_LOW) - endtask - - virtual task check_imem_fault(); - bit latched_imem_err = 1'b0; - core_status_t mem_status; - ibex_pkg::exc_cause_t exc_type; - // Need to account for case where imem_error is asserted during an instruction fetch that gets - // killed - due to jumps and control flow changes - do begin - fork - begin - fork : imem_fork - begin - check_next_core_status(HANDLING_EXCEPTION, "Core did not jump to exception handler"); - check_priv_mode(PRIV_LVL_M); - latched_imem_err = 1'b1; - `uvm_info(`gfn, $sformatf("latched_imem_err: 0x%0x", latched_imem_err), UVM_LOW) - end - begin - clk_vif.wait_clks(5000); - end - join_any - disable fork; + begin + forever begin + wait_for_core_status(HANDLING_IRQ); + // Do not allow error injection while we are handling IRQ + memory_error_seq_h.stop(); end - join - if (latched_imem_err === 1'b0) begin - cur_run_phase.drop_objection(this); - inject_imem_error(); end - end while (latched_imem_err === 1'b0); - check_next_core_status(INSTR_FAULT_EXCEPTION, - "Core did not register correct memory fault type", 5000); - exc_type = ExcCauseInstrAccessFault; - check_mcause(1'b0, exc_type.lower_cause); - wait (dut_vif.dut_cb.mret === 1'b1); - `uvm_info(`gfn, "exiting mem fault checker", UVM_LOW) + join_none endtask endclass diff --git a/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv b/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv index b65834b1e9..362f548b06 100644 --- a/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv +++ b/dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv @@ -43,6 +43,7 @@ module ibex_simple_system_cosim_checker #( always @(posedge clk_i) begin if (u_top.rvfi_valid) begin riscv_cosim_set_nmi(cosim_handle, u_top.rvfi_ext_nmi); + riscv_cosim_set_nmi_int(cosim_handle, u_top.rvfi_ext_nmi_int); riscv_cosim_set_mip(cosim_handle, u_top.rvfi_ext_mip); riscv_cosim_set_debug_req(cosim_handle, u_top.rvfi_ext_debug_req); riscv_cosim_set_mcycle(cosim_handle, u_top.rvfi_ext_mcycle); diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 895aad651a..092c6fe805 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -138,6 +138,7 @@ module ibex_core import ibex_pkg::*; #( output logic [31:0] rvfi_mem_wdata, output logic [31:0] rvfi_ext_mip, output logic rvfi_ext_nmi, + output logic rvfi_ext_nmi_int, output logic rvfi_ext_debug_req, output logic [63:0] rvfi_ext_mcycle, output logic [31:0] rvfi_ext_mhpmcounters [10], @@ -1204,6 +1205,7 @@ module ibex_core import ibex_pkg::*; #( logic new_debug_req; logic new_nmi; + logic new_nmi_int; logic new_irq; ibex_pkg::irqs_t captured_mip; logic captured_nmi; @@ -1214,6 +1216,7 @@ module ibex_core import ibex_pkg::*; #( // debug_req and MIP captured at IF -> ID transition so one extra stage ibex_pkg::irqs_t rvfi_ext_stage_mip [RVFI_STAGES+1]; logic rvfi_ext_stage_nmi [RVFI_STAGES+1]; + logic rvfi_ext_stage_nmi_int [RVFI_STAGES]; logic rvfi_ext_stage_debug_req [RVFI_STAGES+1]; logic [63:0] rvfi_ext_stage_mcycle [RVFI_STAGES]; logic [31:0] rvfi_ext_stage_mhpmcounters [RVFI_STAGES][10]; @@ -1262,6 +1265,7 @@ module ibex_core import ibex_pkg::*; #( end assign rvfi_ext_nmi = rvfi_ext_stage_nmi [RVFI_STAGES]; + assign rvfi_ext_nmi_int = rvfi_ext_stage_nmi_int [RVFI_STAGES-1]; assign rvfi_ext_debug_req = rvfi_ext_stage_debug_req [RVFI_STAGES]; assign rvfi_ext_mcycle = rvfi_ext_stage_mcycle [RVFI_STAGES-1]; assign rvfi_ext_mhpmcounters = rvfi_ext_stage_mhpmcounters [RVFI_STAGES-1]; @@ -1309,7 +1313,7 @@ module ibex_core import ibex_pkg::*; #( end assign rvfi_trap_id = id_stage_i.controller_i.id_exception_o; - assign rvfi_trap_wb = id_stage_i.controller_i.exc_req_lsu; + assign rvfi_trap_wb = id_stage_i.controller_i.exc_req_lsu | new_nmi_int; // WB is instantly done in the tracking pipeline when a trap is progress through the pipeline assign rvfi_wb_done = instr_done_wb | (rvfi_stage_valid[0] & rvfi_stage_trap[0]); end else begin : gen_rvfi_no_wb_stage @@ -1349,6 +1353,7 @@ module ibex_core import ibex_pkg::*; #( // appropriately. assign new_debug_req = (debug_req_i & ~debug_mode); assign new_nmi = irq_nm_i & ~nmi_mode & ~debug_mode; + assign new_nmi_int = id_stage_i.mem_resp_intg_err & ~nmi_mode & ~debug_mode; assign new_irq = irq_pending_o & csr_mstatus_mie & ~nmi_mode & ~debug_mode; always_ff @(posedge clk_i or negedge rst_ni) begin @@ -1423,6 +1428,7 @@ module ibex_core import ibex_pkg::*; #( rvfi_stage_mem_addr[i] <= '0; rvfi_ext_stage_mip[i+1] <= '0; rvfi_ext_stage_nmi[i+1] <= '0; + rvfi_ext_stage_nmi_int[i] <= '0; rvfi_ext_stage_debug_req[i+1] <= '0; rvfi_ext_stage_mcycle[i] <= '0; rvfi_ext_stage_mhpmcounters[i] <= '{10{'0}}; @@ -1500,6 +1506,8 @@ module ibex_core import ibex_pkg::*; #( rvfi_ext_stage_mip[i+1] <= rvfi_ext_stage_mip[i]; rvfi_ext_stage_nmi[i+1] <= rvfi_ext_stage_nmi[i]; + rvfi_ext_stage_nmi_int[i] <= rvfi_ext_stage_nmi_int[i-1]; + rvfi_ext_stage_nmi_int[i-1] <= new_nmi_int; rvfi_ext_stage_debug_req[i+1] <= rvfi_ext_stage_debug_req[i]; rvfi_ext_stage_mcycle[i] <= rvfi_ext_stage_mcycle[i-1]; rvfi_ext_stage_ic_scr_key_valid[i] <= rvfi_ext_stage_ic_scr_key_valid[i-1]; diff --git a/rtl/ibex_load_store_unit.sv b/rtl/ibex_load_store_unit.sv index 6dc86a901d..1c1a329f6e 100644 --- a/rtl/ibex_load_store_unit.sv +++ b/rtl/ibex_load_store_unit.sv @@ -508,7 +508,7 @@ module ibex_load_store_unit #( assign data_or_pmp_err = lsu_err_q | data_bus_err_i | pmp_err_q; assign lsu_resp_valid_o = (data_rvalid_i | pmp_err_q) & (ls_fsm_cs == IDLE); assign lsu_rdata_valid_o = - (ls_fsm_cs == IDLE) & data_rvalid_i & ~data_or_pmp_err & ~data_we_q & ~data_intg_err; + (ls_fsm_cs == IDLE) & data_rvalid_i & ~data_or_pmp_err & ~data_we_q; // output to register file assign lsu_rdata_o = data_rdata_ext; diff --git a/rtl/ibex_lockstep.sv b/rtl/ibex_lockstep.sv index e6ab5fd0c7..f70172bba9 100644 --- a/rtl/ibex_lockstep.sv +++ b/rtl/ibex_lockstep.sv @@ -425,6 +425,7 @@ module ibex_lockstep import ibex_pkg::*; #( .rvfi_mem_wdata (), .rvfi_ext_mip (), .rvfi_ext_nmi (), + .rvfi_ext_nmi_int (), .rvfi_ext_debug_req (), .rvfi_ext_mcycle (), .rvfi_ext_mhpmcounters (), diff --git a/rtl/ibex_top.sv b/rtl/ibex_top.sv index e951eb76bf..8b29aa06fd 100644 --- a/rtl/ibex_top.sv +++ b/rtl/ibex_top.sv @@ -118,6 +118,7 @@ module ibex_top import ibex_pkg::*; #( output logic [31:0] rvfi_mem_wdata, output logic [31:0] rvfi_ext_mip, output logic rvfi_ext_nmi, + output logic rvfi_ext_nmi_int, output logic rvfi_ext_debug_req, output logic [63:0] rvfi_ext_mcycle, output logic [31:0] rvfi_ext_mhpmcounters [10], @@ -366,6 +367,7 @@ module ibex_top import ibex_pkg::*; #( .rvfi_mem_wdata, .rvfi_ext_mip, .rvfi_ext_nmi, + .rvfi_ext_nmi_int, .rvfi_ext_debug_req, .rvfi_ext_mcycle, .rvfi_ext_mhpmcounters, diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index b830bc14f0..ca866ae741 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -121,6 +121,7 @@ module ibex_top_tracing import ibex_pkg::*; #( logic [31:0] rvfi_mem_wdata; logic [31:0] rvfi_ext_mip; logic rvfi_ext_nmi; + logic rvfi_ext_nmi_int; logic rvfi_ext_debug_req; logic [63:0] rvfi_ext_mcycle; @@ -134,6 +135,7 @@ module ibex_top_tracing import ibex_pkg::*; #( logic [31:0] unused_rvfi_ext_mip; logic unused_rvfi_ext_nmi; + logic unused_rvfi_ext_nmi_int; logic unused_rvfi_ext_debug_req; logic [63:0] unused_rvfi_ext_mcycle; logic unused_rvfi_ext_ic_scr_key_valid; @@ -142,6 +144,7 @@ module ibex_top_tracing import ibex_pkg::*; #( // them. assign unused_rvfi_ext_mip = rvfi_ext_mip; assign unused_rvfi_ext_nmi = rvfi_ext_nmi; + assign unused_rvfi_ext_nmi_int = rvfi_ext_nmi_int; assign unused_rvfi_ext_debug_req = rvfi_ext_debug_req; assign unused_rvfi_ext_mcycle = rvfi_ext_mcycle; assign unused_perf_regs = rvfi_ext_mhpmcounters; @@ -242,6 +245,7 @@ module ibex_top_tracing import ibex_pkg::*; #( .rvfi_mem_wdata, .rvfi_ext_mip, .rvfi_ext_nmi, + .rvfi_ext_nmi_int, .rvfi_ext_debug_req, .rvfi_ext_mcycle, .rvfi_ext_mhpmcounters,