Add a double_fault detector to core_ibex uvm environment

Add a new scoreboard component to the core_ibex uvm environment, which contains
a double_fault detector task. This uses the top-level output
'double_fault_seen_o' to count the number of total and consecutive double_faults
seen with a test. A helper task allows the base_test to wait upon each of these
counters reaching the configured thresholds, and then to end the test early with
a passing result.

The default thresholds are 100 for consecutive faults, and 1000 for total faults.

The double_fault detector is disabled by default.
A plusarg '+enable_double_fault_detector=1' enables the checker.

This commit enables it for only the 'pmp_full_random_test', as that is a useful
test candidate to begin with.
This commit is contained in:
Harry Callahan 2022-10-12 20:02:29 +01:00
parent 083fe2a54f
commit b214fa1c72
8 changed files with 167 additions and 16 deletions

View file

@ -20,6 +20,7 @@ interface core_ibex_dut_probe_if(input logic clk);
ibex_pkg::priv_lvl_e priv_mode;
ibex_pkg::ctrl_fsm_e ctrl_fsm_cs;
logic debug_mode;
logic double_fault_seen;
clocking dut_cb @(posedge clk);
output fetch_enable;
@ -38,6 +39,7 @@ interface core_ibex_dut_probe_if(input logic clk);
input priv_mode;
input ctrl_fsm_cs;
input debug_mode;
input double_fault_seen;
endclocking
initial begin

View file

@ -14,6 +14,7 @@ class core_ibex_env extends uvm_env;
core_ibex_vseqr vseqr;
core_ibex_env_cfg cfg;
scrambling_key_agent scrambling_key_agent_h;
core_ibex_scoreboard scoreboard;
`uvm_component_utils(core_ibex_env)
`uvm_component_new
@ -23,6 +24,14 @@ class core_ibex_env extends uvm_env;
if (!uvm_config_db#(core_ibex_env_cfg)::get(this, "", "cfg", cfg)) begin
`uvm_fatal(get_full_name(), "Cannot get cfg")
end
if (!uvm_config_db#(virtual clk_rst_if)::get(this, "", "clk_if",
cfg.ibex_clk_vif)) begin
`uvm_fatal(`gfn, "failed to get ibex clk_if from uvm_config_db")
end
if (!uvm_config_db#(virtual core_ibex_dut_probe_if)::get(this, "", "dut_if",
cfg.ibex_dut_vif)) begin
`uvm_fatal(`gfn, "failed to get ibex dut_if from uvm_config_db")
end
data_if_response_agent = ibex_mem_intf_response_agent::type_id::
create("data_if_response_agent", this);
instr_if_response_agent = ibex_mem_intf_response_agent::type_id::
@ -37,6 +46,8 @@ class core_ibex_env extends uvm_env;
cfg.scrambling_key_cfg.if_mode = dv_utils_pkg::Device;
// Create virtual sequencer
vseqr = core_ibex_vseqr::type_id::create("vseqr", this);
// Create scoreboard
scoreboard = core_ibex_scoreboard::type_id::create("scoreboard", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
@ -48,6 +59,10 @@ class core_ibex_env extends uvm_env;
cosim_agent.dmem_port);
instr_if_response_agent.monitor.item_collected_port.connect(
cosim_agent.imem_port);
if (cfg.enable_double_fault_detector) begin
cosim_agent.rvfi_monitor.item_collected_port.connect(
scoreboard.rvfi_port.analysis_export);
end
endfunction : connect_phase
function void reset();

View file

@ -4,6 +4,9 @@
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_irq_single_seq;
bit enable_irq_multiple_seq;
bit enable_irq_nmi_seq;
@ -17,7 +20,13 @@ class core_ibex_env_cfg extends uvm_object;
bit[31:0] signature_addr;
rand scrambling_key_agent_cfg scrambling_key_cfg;
// Double-Fault detection in scoreboard
bit enable_double_fault_detector = 0;
int unsigned double_fault_threshold_consecutive = 100;
int unsigned double_fault_threshold_total = 1000;
`uvm_object_utils_begin(core_ibex_env_cfg)
`uvm_field_int(enable_double_fault_detector, 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)
@ -33,6 +42,7 @@ class core_ibex_env_cfg extends uvm_object;
function new(string name = "");
super.new(name);
void'($value$plusargs("enable_double_fault_detector=%0d", enable_double_fault_detector));
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));

View file

@ -23,6 +23,7 @@ package core_ibex_env_pkg;
`include "core_ibex_vseqr.sv"
`include "core_ibex_env_cfg.sv"
`include "core_ibex_scoreboard.sv"
`include "core_ibex_env.sv"
endpackage

View file

@ -0,0 +1,108 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class core_ibex_scoreboard extends uvm_scoreboard;
uvm_tlm_analysis_fifo #(ibex_rvfi_seq_item) rvfi_port;
core_ibex_env_cfg cfg;
// Events for Double-Fault detection
uvm_event fault_threshold_consecutive_reached, fault_threshold_total_reached;
`uvm_component_utils(core_ibex_scoreboard)
`uvm_component_new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(core_ibex_env_cfg)::get(this, "", "cfg", cfg)) begin
`uvm_fatal(get_full_name(), "Cannot get cfg")
end
rvfi_port = new("rvfi_port_scoreboard", this);
endfunction : build_phase
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
double_fault_detector();
join_none
endtask
task double_fault_detector();
int unsigned double_fault_cnt_total = 0;
int unsigned double_fault_cnt_consecutive = 0;
bit double_fault_pulse_seen = 1'b0;
ibex_rvfi_seq_item rvfi_instr;
fault_threshold_consecutive_reached = new();
fault_threshold_total_reached = new();
// There are two observable side-effects of a double fault in the Ibex:
// - CPUCTRL.double_fault_seen is set to '1
// - The top-level output double_fault_seen_o is asserted for one cycle
fork
// Increment a counter whenever a double_fault was indicated for the last
// rvfi_seq_item created. When the counter reaches a threshold, create an event.
begin
forever begin
rvfi_port.get(rvfi_instr);
if (double_fault_pulse_seen) begin
// There must have been a double_fault during the previous retired insn.
double_fault_pulse_seen = 1'b0;
double_fault_cnt_total++;
double_fault_cnt_consecutive++;
end else begin
// Reset the consecutive counter.
double_fault_cnt_consecutive = 0;
end
// Create an event if either counter reaches its threshold value, then reset the counter.
if (double_fault_cnt_consecutive == cfg.double_fault_threshold_consecutive) begin
fault_threshold_consecutive_reached.trigger();
double_fault_cnt_consecutive = 0;
end
if (double_fault_cnt_total == cfg.double_fault_threshold_total) begin
fault_threshold_total_reached.trigger();
double_fault_cnt_total = 0;
end
end
end
// Latch a signal to show that we have seen a double_fault.
// The pulse may be receieved sometime before the rvfi_seq_item.
begin
forever begin
@(posedge cfg.ibex_dut_vif.double_fault_seen);
double_fault_pulse_seen = 1'b1;
cfg.ibex_clk_vif.wait_clks(1);
end
end
join_none
endtask // double_fault_detector
// Helper method which returns if either of the counter thresholds are reached.
virtual task dfd_wait_for_pass_events();
fork
begin
fault_threshold_total_reached.wait_trigger();
`uvm_info(`gfn,
$sformatf({"double_fault detector : reached threshold [%0d] ",
"for total double faults seen."}, cfg.double_fault_threshold_total),
UVM_LOW)
end
begin
fault_threshold_consecutive_reached.wait_trigger();
`uvm_info(`gfn,
$sformatf({"double_fault detector : reached threshold [%0d] ",
"for consecutive double faults seen."}, cfg.double_fault_threshold_consecutive),
UVM_LOW)
end
join_any
disable fork;
endtask
endclass

View file

@ -744,7 +744,9 @@
+instr_cnt=6000
+pmp_max_offset=00024000
+pmp_randomize=1
+pmp_allow_addr_overlap=1
+pmp_allow_illegal_tor=1
sim_opts: >
+enable_double_fault_detector=1
rtl_test: core_ibex_base_test
rtl_params:
PMPEnable: 1

View file

@ -148,7 +148,7 @@ module core_ibex_tb_top;
.debug_req_i (dut_if.debug_req ),
.crash_dump_o ( ),
.double_fault_seen_o ( ),
.double_fault_seen_o (dut_if.double_fault_seen ),
.fetch_enable_i (dut_if.fetch_enable ),
.alert_minor_o (dut_if.alert_minor ),

View file

@ -211,38 +211,51 @@ class core_ibex_base_test extends uvm_test;
end
endfunction
// Use a RISCV_DV handshake signature to end the test.
// This process uses a different signature address (cfg.signature_addr - 0x4)
// Watch for all of the different critera for test pass/failure here
virtual task wait_for_test_done();
bit result;
// Make use of the 'test_done_port' which subscribes to all memory interface items.
// We can then watch for the correct message in isolation.
fork
// - Use a RISCV_DV handshake signature to end the test.
// This process uses a different signature address (cfg.signature_addr - 0x4)
// Make use of the 'test_done_port' which subscribes to all memory interface items.
// We can then watch for the correct message in isolation.
begin
wait_for_mem_txn((cfg.signature_addr - 4'h4), TEST_RESULT, test_done_port);
result = signature_data_q.pop_front();
if (result == TEST_PASS) begin
test_done = 1'b1;
`uvm_info(`gfn, "Test PASSED!", UVM_LOW)
vseq.stop();
check_perf_stats();
// De-assert fetch enable to finish the test
clk_vif.wait_clks(10);
dut_vif.dut_cb.fetch_enable <= ibex_pkg::FetchEnableOff;
// Wait some time for the remaining instruction to finish
clk_vif.wait_clks(3000);
`uvm_info(`gfn, "Test done due to RISCV-DV handshake (payload=TEST_PASS)", UVM_LOW)
end else if (result == TEST_FAIL) begin
`uvm_fatal(`gfn, "Test FAILED!")
`uvm_fatal(`gfn, "Test failed due to RISCV-DV handshake (payload=TEST_FAIL)")
end else begin
`uvm_fatal(`gfn, "Incorrectly formed handshake received at test-control address.")
end
end
// - End the test if we see too many of the following...
// - double_faults
begin
if (cfg.enable_double_fault_detector) begin
env.scoreboard.dfd_wait_for_pass_events();
`uvm_info(`gfn, "Test done due to double_fault detector.", UVM_LOW)
end else begin
wait (test_done == 1'b1);
end
end
// - End the test by timeout if it doesn't terminate within a reasonable time.
begin
clk_vif.wait_clks(timeout_in_cycles);
`uvm_fatal(`gfn, "TEST TIMEOUT!!")
end
join_any
test_done = 1'b1;
vseq.stop();
check_perf_stats();
// De-assert fetch enable to finish the test
clk_vif.wait_clks(10);
dut_vif.dut_cb.fetch_enable <= ibex_pkg::FetchEnableOff;
// Wait some time for the remaining instruction to finish
clk_vif.wait_clks(3000);
endtask