diff --git a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf.sv b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf.sv index 8285e566..6d0cf08f 100644 --- a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf.sv +++ b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf.sv @@ -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 diff --git a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_seq_item.sv b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_seq_item.sv index ae1ff192..fc218c06 100644 --- a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_seq_item.sv +++ b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_seq_item.sv @@ -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 diff --git a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv index 5a6b7e4c..49c76e0f 100644 --- a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv +++ b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_driver.sv @@ -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 diff --git a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_seq_lib.sv b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_seq_lib.sv index 7188c8a2..1dc80ef4 100644 --- a/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_seq_lib.sv +++ b/dv/uvm/common/ibex_mem_intf_agent/ibex_mem_intf_slave_seq_lib.sv @@ -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 diff --git a/dv/uvm/riscv_dv_extension/testlist.yaml b/dv/uvm/riscv_dv_extension/testlist.yaml index b02597ed..69f38e7d 100644 --- a/dv/uvm/riscv_dv_extension/testlist.yaml +++ b/dv/uvm/riscv_dv_extension/testlist.yaml @@ -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 diff --git a/dv/uvm/tb/core_ibex_tb_top.sv b/dv/uvm/tb/core_ibex_tb_top.sv index d1e78f8d..25959b00 100644 --- a/dv/uvm/tb/core_ibex_tb_top.sv +++ b/dv/uvm/tb/core_ibex_tb_top.sv @@ -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 diff --git a/dv/uvm/tests/core_ibex_test_lib.sv b/dv/uvm/tests/core_ibex_test_lib.sv index a66f5e84..c862792c 100644 --- a/dv/uvm/tests/core_ibex_test_lib.sv +++ b/dv/uvm/tests/core_ibex_test_lib.sv @@ -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