Add memory error testing (#330)

This commit is contained in:
udinator 2019-09-18 13:26:56 -07:00 committed by GitHub
parent 76f4db5155
commit e694fa05b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 11 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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