mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Add memory error testing (#330)
This commit is contained in:
parent
76f4db5155
commit
e694fa05b9
7 changed files with 105 additions and 11 deletions
|
@ -14,5 +14,6 @@ interface ibex_mem_intf#(ADDR_WIDTH = 32, DATA_WIDTH = 32);
|
|||
logic rvalid;
|
||||
logic [DATA_WIDTH-1:0] wdata;
|
||||
logic [DATA_WIDTH-1:0] rdata;
|
||||
logic error;
|
||||
|
||||
endinterface : ibex_mem_intf
|
||||
|
|
|
@ -15,6 +15,7 @@ class ibex_mem_intf_seq_item extends uvm_sequence_item;
|
|||
rand bit [3:0] gnt_delay;
|
||||
rand bit [3:0] req_delay;
|
||||
rand bit [5:0] rvalid_delay;
|
||||
rand bit error;
|
||||
|
||||
`uvm_object_utils_begin(ibex_mem_intf_seq_item)
|
||||
`uvm_field_int (addr, UVM_DEFAULT)
|
||||
|
@ -23,6 +24,7 @@ class ibex_mem_intf_seq_item extends uvm_sequence_item;
|
|||
`uvm_field_int (data, UVM_DEFAULT)
|
||||
`uvm_field_int (gnt_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (rvalid_delay, UVM_DEFAULT)
|
||||
`uvm_field_int (error, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
|
|
@ -16,12 +16,10 @@ class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
`uvm_component_utils(ibex_mem_intf_slave_driver)
|
||||
`uvm_component_new
|
||||
|
||||
mailbox #(ibex_mem_intf_seq_item) grant_queue;
|
||||
mailbox #(ibex_mem_intf_seq_item) rdata_queue;
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
grant_queue = new();
|
||||
rdata_queue = new();
|
||||
if(!uvm_config_db#(virtual ibex_mem_intf)::get(this, "", "vif", vif))
|
||||
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
|
||||
|
@ -39,6 +37,7 @@ class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
vif.rvalid <= 1'b0;
|
||||
vif.grant <= 1'b0;
|
||||
vif.rdata <= 'b0;
|
||||
vif.error <= 1'b0;
|
||||
endtask : reset_signals
|
||||
|
||||
virtual protected task get_and_drive();
|
||||
|
@ -88,11 +87,13 @@ class ibex_mem_intf_slave_driver extends uvm_driver #(ibex_mem_intf_seq_item);
|
|||
@(posedge vif.clock);
|
||||
vif.rvalid <= 1'b0;
|
||||
vif.rdata <= 'x;
|
||||
vif.error <= 1'b0;
|
||||
rdata_queue.get(tr);
|
||||
if(vif.reset) continue;
|
||||
repeat(tr.rvalid_delay) @(posedge vif.clock);
|
||||
if(~vif.reset) begin
|
||||
vif.rvalid <= 1'b1;
|
||||
vif.error <= tr.error;
|
||||
vif.rdata <= tr.data;
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,10 @@ class ibex_mem_intf_slave_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
int min_rvalid_delay = 0;
|
||||
ibex_mem_intf_seq_item item;
|
||||
mem_model m_mem;
|
||||
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;
|
||||
|
||||
`uvm_object_utils(ibex_mem_intf_slave_seq)
|
||||
`uvm_declare_p_sequencer(ibex_mem_intf_slave_sequencer)
|
||||
|
@ -23,8 +27,10 @@ class ibex_mem_intf_slave_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
forever
|
||||
begin
|
||||
bit [ADDR_WIDTH-1:0] aligned_addr;
|
||||
bit [DATA_WIDTH-1:0] rand_data;
|
||||
p_sequencer.addr_ph_port.get(item);
|
||||
req = ibex_mem_intf_seq_item::type_id::create("req");
|
||||
error_synch = 1'b0;
|
||||
if (!req.randomize() with {
|
||||
addr == item.addr;
|
||||
read_write == item.read_write;
|
||||
|
@ -36,12 +42,20 @@ class ibex_mem_intf_slave_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
[max_rvalid_delay/2 : max_rvalid_delay-1] :/ 1,
|
||||
max_rvalid_delay :/ 1
|
||||
};
|
||||
error == enable_error;
|
||||
}) begin
|
||||
`uvm_fatal(`gfn, "Cannot randomize slave request")
|
||||
end
|
||||
enable_error = 1'b0;
|
||||
error_synch = 1'b1;
|
||||
aligned_addr = {req.addr[DATA_WIDTH-1:2], 2'b0};
|
||||
if(req.read_write == READ) begin : READ_block
|
||||
req.data = m_mem.read(aligned_addr);
|
||||
if (req.error) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(rand_data)
|
||||
req.data = rand_data;
|
||||
end else begin
|
||||
if(req.read_write == READ) begin : READ_block
|
||||
req.data = m_mem.read(aligned_addr);
|
||||
end
|
||||
end
|
||||
`uvm_info(get_full_name(), $sformatf("Response transfer:\n%0s", req.sprint()), UVM_HIGH)
|
||||
start_item(req);
|
||||
|
@ -58,4 +72,12 @@ class ibex_mem_intf_slave_seq extends uvm_sequence #(ibex_mem_intf_seq_item);
|
|||
end
|
||||
endtask : body
|
||||
|
||||
virtual function inject_error();
|
||||
this.enable_error = 1'b1;
|
||||
endfunction
|
||||
|
||||
virtual function bit get_error_synch();
|
||||
return this.error_synch;
|
||||
endfunction
|
||||
|
||||
endclass : ibex_mem_intf_slave_seq
|
||||
|
|
|
@ -35,11 +35,6 @@
|
|||
gen_opts: >
|
||||
+instr_cnt=10000
|
||||
+num_of_sub_program=5
|
||||
+directed_instr_0=riscv_load_store_rand_instr_stream,4
|
||||
+directed_instr_1=riscv_loop_instr,4
|
||||
+directed_instr_2=riscv_hazard_instr_stream,4
|
||||
+directed_instr_3=riscv_load_store_hazard_instr_stream,4
|
||||
+directed_instr_4=riscv_multi_page_load_store_instr_stream,4
|
||||
rtl_test: core_ibex_base_test
|
||||
|
||||
- test: riscv_rand_jump_test
|
||||
|
@ -282,3 +277,17 @@
|
|||
+directed_instr_2=riscv_multi_page_load_store_instr_stream,20
|
||||
+enable_unaligned_load_store=1
|
||||
rtl_test: core_ibex_base_test
|
||||
|
||||
- test: riscv_mem_error_test
|
||||
description: >
|
||||
Normal random instruction test, but randomly insert instruction fetch or memory load/store errors
|
||||
iterations: 5
|
||||
gen_test: riscv_rand_instr_test
|
||||
gen_opts: >
|
||||
+require_signature_addr=1
|
||||
+instr_cnt=10000
|
||||
rtl_test: core_ibex_mem_error_test
|
||||
sim_opts: >
|
||||
+require_signature_addr=1
|
||||
compare_opts:
|
||||
compare_final_value_only: 1
|
||||
|
|
|
@ -38,11 +38,11 @@ module core_ibex_tb_top;
|
|||
.data_gnt_i(data_mem_vif.grant),
|
||||
.data_rvalid_i(data_mem_vif.rvalid),
|
||||
.data_rdata_i(data_mem_vif.rdata),
|
||||
.data_err_i(0),
|
||||
.data_err_i(data_mem_vif.error),
|
||||
.instr_gnt_i(instr_mem_vif.grant),
|
||||
.instr_rvalid_i(instr_mem_vif.rvalid),
|
||||
.instr_rdata_i(instr_mem_vif.rdata),
|
||||
.instr_err_i(0)
|
||||
.instr_err_i(instr_mem_vif.error)
|
||||
);
|
||||
|
||||
// Data load/store vif connection
|
||||
|
|
|
@ -378,3 +378,62 @@ class core_ibex_debug_ebreakm_test extends core_ibex_directed_test;
|
|||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
// Memory interface error test class
|
||||
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);
|
||||
end
|
||||
vseq.data_intf_seq.inject_error();
|
||||
// Dmem interface error could be either a load or store operation
|
||||
fork
|
||||
begin
|
||||
fork
|
||||
check_mem_fault(LOAD_FAULT_EXCEPTION, EXC_CAUSE_LOAD_ACCESS_FAULT);
|
||||
check_mem_fault(STORE_FAULT_EXCEPTION, EXC_CAUSE_STORE_ACCESS_FAULT);
|
||||
join_any
|
||||
disable fork;
|
||||
end
|
||||
join
|
||||
// Random delay before injecting instruction fetch fault
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(err_delay, err_delay inside { [5:100] };)
|
||||
clk_vif.wait_clks(err_delay);
|
||||
while (!vseq.instr_intf_seq.get_error_synch()) begin
|
||||
clk_vif.wait_clks(1);
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("vseq.instr_intf_seq.error_synch: 0x%0x", vseq.instr_intf_seq.get_error_synch()), UVM_LOW)
|
||||
vseq.instr_intf_seq.inject_error();
|
||||
`uvm_info(`gfn, $sformatf("vseq.instr_intf_seq.enable_error: 0x%0x", vseq.instr_intf_seq.enable_error), UVM_LOW)
|
||||
check_mem_fault(INSTR_FAULT_EXCEPTION, EXC_CAUSE_INSTR_ACCESS_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 check_mem_fault(core_status_t fault_type, ibex_pkg::exc_cause_e exc_type);
|
||||
bit[ibex_mem_intf_agent_pkg::DATA_WIDTH-1:0] mcause;
|
||||
forever begin
|
||||
wait_for_core_status(HANDLING_EXCEPTION);
|
||||
wait_for_core_status(fault_type);
|
||||
wait_for_csr_write(CSR_MCAUSE);
|
||||
mcause = signature_data;
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-1], 1'b0,
|
||||
"mcause interrupt is not set to 1'b0")
|
||||
`DV_CHECK_EQ_FATAL(mcause[ibex_mem_intf_agent_pkg::DATA_WIDTH-2:0],
|
||||
exc_type,
|
||||
"mcause.exception_code is encoding the wrong exception type")
|
||||
wait(dut_vif.mret === 1'b1);
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue