mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-20 03:47:15 -04:00
[ibex,dv] Add new test for memory integrity errors
This test picks between inserting an integrity error or a bus error to the response in the case of a memory request from Ibex. Introduces a control knob `enable_mem_intg_err` which can control the rate of having integrity errors per request. This commit also disables checking for double fault alerts in the scoreboard because they're expected to be seen while simulating and they don't cause infinite loop problems because every time a memory response is requested the error causing part is just randomized. That means Ibex trying to execute same instruction again would have a chance to succeed this time. Signed-off-by: Canberk Topal <ctopal@lowrisc.org>
This commit is contained in:
parent
bb959c7181
commit
12ae6b2478
5 changed files with 117 additions and 22 deletions
|
@ -11,17 +11,26 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
ibex_mem_intf_seq_item item;
|
||||
mem_model m_mem;
|
||||
ibex_cosim_agent cosim_agent;
|
||||
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
|
||||
bit error_synch = 1'b1;
|
||||
bit is_dmem_seq = 1'b0;
|
||||
bit suppress_error_on_exc = 1'b0;
|
||||
|
||||
`uvm_object_utils(ibex_mem_intf_response_seq)
|
||||
`uvm_declare_p_sequencer(ibex_mem_intf_response_sequencer)
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
virtual core_ibex_dut_probe_if ibex_dut_vif;
|
||||
|
||||
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(null, "", "dut_if",
|
||||
ibex_dut_vif)) begin
|
||||
`uvm_fatal(`gfn, "failed to get ibex dut_if from uvm_config_db")
|
||||
end
|
||||
|
||||
if (m_mem == null) `uvm_fatal(get_full_name(), "Cannot get memory model")
|
||||
`uvm_info(`gfn, $sformatf("is_dmem_seq: 0x%0x", is_dmem_seq), UVM_LOW)
|
||||
forever
|
||||
|
@ -37,6 +46,13 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
|
||||
req = ibex_mem_intf_seq_item::type_id::create("req");
|
||||
error_synch = 1'b0;
|
||||
|
||||
if (suppress_error_on_exc &&
|
||||
(ibex_dut_vif.dut_cb.sync_exc_seen || ibex_dut_vif.dut_cb.irq_exc_seen)) begin
|
||||
enable_error = 1'b0;
|
||||
enable_intg_error = 1'b0;
|
||||
end
|
||||
|
||||
if (!req.randomize() with {
|
||||
addr == item.addr;
|
||||
read_write == item.read_write;
|
||||
|
@ -66,6 +82,7 @@ class ibex_mem_intf_response_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
// 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)
|
||||
|
@ -87,9 +104,9 @@ 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 (p_sequencer.cfg.enable_bad_intg_on_uninit_access &&
|
||||
data_was_uninitialized) begin
|
||||
if ((p_sequencer.cfg.enable_bad_intg_on_uninit_access && 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)
|
||||
|
@ -103,6 +120,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
|
||||
|
|
3
dv/uvm/core_ibex/env/core_ibex_env_cfg.sv
vendored
3
dv/uvm/core_ibex/env/core_ibex_env_cfg.sv
vendored
|
@ -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));
|
||||
|
|
|
@ -588,6 +588,27 @@
|
|||
+disable_pmp_exception_handler=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
|
||||
|
|
|
@ -204,9 +204,17 @@ endclass
|
|||
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
|
||||
// When set skip error injection if Ibex is currently handling an exception (incluing IRQs)
|
||||
bit skip_on_exc = 1'b0;
|
||||
|
||||
rand error_type_e err_type = PickErr;
|
||||
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)
|
||||
|
@ -216,14 +224,21 @@ class memory_error_seq extends core_base_new_seq#(ibex_mem_intf_seq_item);
|
|||
endfunction
|
||||
|
||||
virtual task send_req();
|
||||
case (err_type)
|
||||
IsideErr: begin
|
||||
vseq.instr_intf_seq.suppress_error_on_exc = skip_on_exc;
|
||||
vseq.data_intf_seq.suppress_error_on_exc = skip_on_exc;
|
||||
|
||||
`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("tb_no_alerts_triggered", 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();
|
||||
|
@ -231,6 +246,20 @@ class memory_error_seq extends core_base_new_seq#(ibex_mem_intf_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
|
||||
|
|
|
@ -1630,6 +1630,9 @@ class core_ibex_mem_error_test extends core_ibex_directed_test;
|
|||
`uvm_component_utils(core_ibex_mem_error_test)
|
||||
`uvm_component_new
|
||||
|
||||
int illegal_instruction_threshold = 20;
|
||||
int illegal_instruction_exceptions_seen = 0;
|
||||
|
||||
virtual task check_stimulus();
|
||||
memory_error_seq memory_error_seq_h;
|
||||
memory_error_seq_h = memory_error_seq::type_id::create("memory_error_seq_h", this);
|
||||
|
@ -1639,24 +1642,42 @@ class core_ibex_mem_error_test extends core_ibex_directed_test;
|
|||
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;
|
||||
memory_error_seq_h.skip_on_exc = 1'b1;
|
||||
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
|
||||
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
|
||||
end
|
||||
run_illegal_instr_watcher();
|
||||
memory_error_seq_h.start(env.vseqr);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
task run_illegal_instr_watcher();
|
||||
// When integrity errors are present loads that see them won't write to the register file.
|
||||
// Generated code from RISC-DV may be using the loads to produce known constants in register
|
||||
// that are then used elsewhere, in particular for jump targets. As the register write doesn't
|
||||
// occur this results in jumping to places that weren't intended which in turn can result in
|
||||
// illegal instruction exceptions.
|
||||
//
|
||||
// As a simple fix for this we observe illegal instruction exceptions and terminate the test
|
||||
// with a pass after hitting a certain threshold when the test is generating integrity errors.
|
||||
//
|
||||
// We don't terminate immediately as sometimes the test hits an illegal instruction exception
|
||||
// but finds its way back to generated code and terminates as usual. Sometimes it doesn't. The
|
||||
// treshold allows for normal test termination in cases where that's possible.
|
||||
if (!cfg.enable_mem_intg_err) begin
|
||||
return;
|
||||
end
|
||||
|
||||
forever begin
|
||||
wait_for_core_exception(ibex_pkg::ExcCauseIllegalInsn);
|
||||
++illegal_instruction_exceptions_seen;
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual task wait_for_custom_test_done();
|
||||
wait(illegal_instruction_exceptions_seen == illegal_instruction_threshold);
|
||||
`uvm_info(`gfn, "Terminating test early due to illegal instruction threshold reached", UVM_LOW)
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// U-mode mstatus.tw test class
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue