[ entropy_src/dv ] SB Handling for interrupts, disables and resets
- Health test alerts can now be cleared by interrupt handlers for longer
simulations
- Restructured scoreboard to properly support rest and disable events
- Scoreboard bug-fix: it turns out that in ES_TYPE == BYPASS MODE the
DUT FSM effectively stays in BOOT mode
Signed-off-by: Martin Lueker-Boden <martin.lueker-boden@wdc.com>
Co-authored-by: weicaiyang <49293026+weicaiyang@users.noreply.github.com>
diff --git a/hw/ip/entropy_src/dv/entropy_src_sim_cfg.hjson b/hw/ip/entropy_src/dv/entropy_src_sim_cfg.hjson
index b3ea325..95caac2 100644
--- a/hw/ip/entropy_src/dv/entropy_src_sim_cfg.hjson
+++ b/hw/ip/entropy_src/dv/entropy_src_sim_cfg.hjson
@@ -74,7 +74,7 @@
verbosity_a: UVM_FULL
phase_a: run
- component_b: "uvm_test_top.env.m_rng_agent.sequencer"
+ component_b: "uvm_test_top.env.virtual_sequencer"
id_b : _ALL_
verbosity_b: UVM_HIGH
phase_b: run
diff --git a/hw/ip/entropy_src/dv/env/entropy_src_env_cfg.sv b/hw/ip/entropy_src/dv/env/entropy_src_env_cfg.sv
index 060b3ab..bbc5270 100644
--- a/hw/ip/entropy_src/dv/env/entropy_src_env_cfg.sv
+++ b/hw/ip/entropy_src/dv/env/entropy_src_env_cfg.sv
@@ -71,6 +71,15 @@
rand prim_mubi_pkg::mubi8_t otp_en_es_fw_read, otp_en_es_fw_over;
+ //
+ // Implementation-specific constants related to the DUT
+ // (Needed for accurate prediction, no randomization required)
+ //
+
+ // Number of clock cycles between a TLUL disable signal, and deassertion
+ // of enable on the RNG bus.
+ int tlul2rng_disable_delay = 2;
+
// Constraints
constraint c_regwen {regwen dist {
1 :/ regwen_pct,
diff --git a/hw/ip/entropy_src/dv/env/entropy_src_env_pkg.sv b/hw/ip/entropy_src/dv/env/entropy_src_env_pkg.sv
index 6ad299f..bebfb41 100644
--- a/hw/ip/entropy_src/dv/env/entropy_src_env_pkg.sv
+++ b/hw/ip/entropy_src/dv/env/entropy_src_env_pkg.sv
@@ -27,11 +27,12 @@
// types
typedef enum int {
- EntropyValid = 0,
- HealthTestFailed = 1,
- EBusChkFailed = 2,
- ObserveFifoReady = 3,
- FatalErr = 4
+ EntropyValid = 0,
+ HealthTestFailed = 1,
+ EBusChkFailed = 2,
+ ObserveFifoReady = 3,
+ FatalErr = 4,
+ NumEntropySrcIntr = 5
} entropy_src_intr_e;
typedef enum { BOOT, STARTUP, CONTINUOUS } entropy_phase_e;
@@ -50,8 +51,13 @@
// as the DUT's FSM will reset to idle in this case, meaning that it will have to again
// satisfy both the startup and (optional) boot phases.
//
- function automatic entropy_phase_e convert_seed_idx_to_phase(int seed_idx, bit boot_disable);
- if (!boot_disable) begin
+ function automatic entropy_phase_e convert_seed_idx_to_phase(int seed_idx,
+ bit conditioning_bypass,
+ bit boot_disable);
+
+ if (conditioning_bypass) begin
+ return BOOT;
+ end else if (!boot_disable) begin
if (seed_idx == 0) begin
return BOOT;
end else if (seed_idx == 1) begin
@@ -84,9 +90,9 @@
// Counts the number of seeds that have been successfully generated
// in any post-boot phase.
- phase = convert_seed_idx_to_phase(seed_idx, boot_disable);
+ phase = convert_seed_idx_to_phase(seed_idx, bypass, boot_disable);
- return (bypass || phase == BOOT) ? entropy_src_pkg::CSRNG_BUS_WIDTH : fips_window_size;
+ return (phase == BOOT) ? entropy_src_pkg::CSRNG_BUS_WIDTH : fips_window_size;
endfunction
diff --git a/hw/ip/entropy_src/dv/env/entropy_src_scoreboard.sv b/hw/ip/entropy_src/dv/env/entropy_src_scoreboard.sv
index 8a95df2..0315987 100644
--- a/hw/ip/entropy_src/dv/env/entropy_src_scoreboard.sv
+++ b/hw/ip/entropy_src/dv/env/entropy_src_scoreboard.sv
@@ -15,11 +15,13 @@
virtual entropy_src_cov_if cov_vif;
// used by collect_entropy to determine the FSMs phase
- int seed_idx = 0;
- int entropy_data_seeds = 0;
- int entropy_data_drops = 0;
- int csrng_seeds = 0;
- int csrng_drops = 0;
+ int seed_idx = 0;
+ int entropy_data_seeds = 0;
+ int entropy_data_drops = 0;
+ int csrng_seeds = 0;
+ int csrng_drops = 0;
+
+ bit dut_pipeline_enabled = 0;
// Queue of seeds for predicting reads to entropy_data CSR
bit [CSRNG_BUS_WIDTH - 1:0] entropy_data_q[$];
@@ -55,6 +57,12 @@
uvm_tlm_analysis_fifo#(push_pull_item#(.HostDataWidth(RNG_BUS_WIDTH)))
rng_fifo;
+ // Clearing the enable is a soft form of reset.
+ typedef enum int {
+ HardReset,
+ Disable
+ } reset_event_e;
+
`uvm_component_new
function void build_phase(uvm_phase phase);
@@ -77,7 +85,6 @@
super.run_phase(phase);
if (cfg.en_scb) begin
fork
- collect_entropy();
process_csrng();
join_none
end
@@ -321,9 +328,8 @@
msg = $sformatf(fmt, alert_cnt_reg.get_mirrored_value());
`uvm_info(`gfn, msg, UVM_FULL)
-
- `DV_CHECK_FATAL(total_fail_field.predict(.value(fail_total), .kind(UVM_PREDICT_READ)))
- `DV_CHECK_FATAL( alert_cnt_field.predict(.value( alert_cnt), .kind(UVM_PREDICT_READ)))
+ `DV_CHECK_FATAL(total_fail_field.predict(.value(fail_total), .kind(UVM_PREDICT_DIRECT)))
+ `DV_CHECK_FATAL( alert_cnt_field.predict(.value( alert_cnt), .kind(UVM_PREDICT_DIRECT)))
fmt = "Predicted alert cnt for \"%s\" test (FIPS? %d): %04h";
msg = $sformatf(fmt, test, fips_mode, alert_cnt_field.get_mirrored_value());
@@ -460,28 +466,33 @@
any_fail_count_regval++;
if (any_fail_count_regval >= alert_threshold) begin
if(!threshold_alert_active) begin
- fmt = "Alert anticpated! Fail count (%01d) >= threshold (%01d)";
- `uvm_info(`gfn, $sformatf(fmt, any_fail_count_regval, alert_threshold), UVM_HIGH)
- set_exp_alert(.alert_name("recov_alert"), .is_fatal(0), .max_delay(cfg.alert_max_delay));
+ fmt = "New alert anticpated! Fail count (%01d) >= threshold (%01d)";
threshold_alert_active = 1;
+ set_exp_alert(.alert_name("recov_alert"), .is_fatal(0), .max_delay(cfg.alert_max_delay));
+ end else begin
+ fmt = "Alert already signalled: Fail count (%01d) >= threshold (%01d)";
end
+ `uvm_info(`gfn, $sformatf(fmt, any_fail_count_regval, alert_threshold), UVM_HIGH)
end else begin
fmt = "No alert anticpated. fail count (%01d) < threshold (%01d)";
- `uvm_info(`gfn, $sformatf(fmt, any_fail_count_regval, alert_threshold), UVM_FULL)
+ `uvm_info(`gfn, $sformatf(fmt, any_fail_count_regval, alert_threshold), UVM_HIGH)
end
end else begin : no_test_failure
- any_fail_count_regval = 0;
if (!threshold_alert_active) begin
+ any_fail_count_regval = 0;
// Now we know that all tests have passed we can clear the failure counts
- `DV_CHECK_FATAL(alert_fail_reg.predict(.value({TL_DW{1'b0}}), .kind(UVM_PREDICT_READ)))
- `DV_CHECK_FATAL(extht_fail_reg.predict(.value({TL_DW{1'b0}}), .kind(UVM_PREDICT_READ)))
+ `DV_CHECK_FATAL(alert_fail_reg.predict(.value({TL_DW{1'b0}}), .kind(UVM_PREDICT_DIRECT)))
+ end else begin
+ fmt = "Alert state persists: Fail count (%01d) >= threshold (%01d)";
+ `uvm_info(`gfn, $sformatf(fmt, any_fail_count_regval, alert_threshold), UVM_HIGH)
end
end : no_test_failure
+ `DV_CHECK_FATAL(alert_summary_field.predict(.value(any_fail_count_regval),
+ .kind(UVM_PREDICT_DIRECT)))
+
fmt = "Predicted alert cnt for all tests (FIPS? %d): %04h";
`uvm_info(`gfn, $sformatf(fmt, fips_mode, any_fail_count_regval), UVM_HIGH)
- `DV_CHECK_FATAL(alert_summary_field.predict(.value(any_fail_count_regval),
- .kind(UVM_PREDICT_READ)))
return failure;
endfunction
@@ -574,6 +585,48 @@
"All candidate seeds have been checked. ENTROPY_DATA does not match")
endtask
+ function void clear_ht_stat_predictions();
+ string stat_regs [] = '{
+ "repcnt_hi_watermarks", "repcnts_hi_watermarks", "adaptp_hi_watermarks",
+ "adaptp_lo_watermarks", "extht_hi_watermarks", "extht_lo_watermarks",
+ "bucket_hi_watermarks", "markov_hi_watermarks", "markov_lo_watermarks",
+ "repcnt_total_fails", "repcnts_total_fails", "adaptp_hi_total_fails",
+ "adaptp_lo_total_fails", "bucket_total_fails", "markov_hi_total_fails",
+ "markov_lo_total_fails", "extht_hi_total_fails", "extht_lo_total_fails",
+ "alert_summary_fail_counts", "alert_fail_counts", "extht_fail_counts"
+ };
+ foreach (stat_regs[i]) begin
+ uvm_reg csr = ral.get_reg_by_name(stat_regs[i]);
+ void'(csr.predict(.value(csr.get_reset()), .kind(UVM_PREDICT_READ)));
+ end
+ endfunction
+
+ // Clear all relevant prediction variables for
+ // Reset and disable events.
+ function void handle_disable_reset(reset_event_e rst_type);
+ threshold_alert_active = 0;
+ clear_ht_stat_predictions();
+ seed_idx = 0;
+ seed_tl_read_cnt = 0;
+ if( rst_type == HardReset ) begin
+ entropy_data_q.delete();
+ fips_csrng_q.delete();
+ end
+ for (int i = 0; i < RNG_BUS_WIDTH; i++) begin
+ repcnt[i] = 0;
+ end
+ repcnt_symbol = 0;
+ max_repcnt = 0;
+ max_repcnt_symbol = 0;
+ rng_fifo.flush();
+ // TODO: should we flush the CSRNG fifo?
+ //csrng_fifo.flush();
+
+ // Communicate this event to the process_entropy process
+ // with a possible delay.
+ `uvm_info(`gfn, $sformatf("%s Detected", rst_type.name), UVM_MEDIUM)
+ endfunction
+
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name);
uvm_reg csr;
// TODO: Add conditioning prediction, still TBD in design
@@ -593,9 +646,42 @@
// if incoming access is a write to a valid csr, then make updates right away
if (write) begin
void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
+ // Special handling for registers with broader impacts
+ case (csr.get_name())
+ "conf": begin
+ bit do_disable, do_enable;
+ uvm_reg_field enable_field = csr.get_field_by_name("enable");
+ prim_mubi_pkg::mubi4_t enable_mubi = enable_field.get_mirrored_value();
+ // TODO: integrate this with invalid MuBi checks
+ do_disable = (enable_mubi == prim_mubi_pkg::MuBi4False);
+ do_enable = (enable_mubi == prim_mubi_pkg::MuBi4True);
+ if (do_enable && !dut_pipeline_enabled) begin
+ dut_pipeline_enabled = 1;
+ fork
+ begin
+ collect_entropy();
+ handle_disable_reset(Disable);
+ end
+ join_none
+ end
+ if (do_disable && dut_pipeline_enabled) begin
+ fork : background_process
+ begin
+ // The DUT does not immediately turn off the RNG input
+ // We wait a few cycles to let any last couple RNG
+ // samples come into the dut (so we know to delete them
+ // from our model of the DUT);
+ cfg.clk_rst_vif.wait_clks(cfg.tlul2rng_disable_delay);
+ dut_pipeline_enabled = 0;
+ end
+ join_none : background_process
+ end
+ end
+ default: begin
+ end
+ endcase
end
end
-
// process the csr req
// for write, update local variable and fifo at address phase
// for read, update predication at address phase and compare at data phase
@@ -679,6 +765,8 @@
end
"alert_threshold": begin
end
+ "alert_summary_fail_counts": begin
+ end
"alert_fail_counts": begin
end
"extht_fail_counts": begin
@@ -742,6 +830,7 @@
int pass_cnt;
dut_phase = convert_seed_idx_to_phase(seed_idx,
+ cfg.type_bypass == prim_mubi_pkg::MuBi4True,
cfg.boot_bypass_disable == prim_mubi_pkg::MuBi4True);
sample_rng_frames = sample.size();
@@ -810,21 +899,59 @@
return fips_csrng_data;
endfunction
+ // Wait on the RNG queue for rng sequence items
+ //
+ // If bit selection is enabled, wait for RNG_BUS_WIDTH items. Otherwise, return after one item.
+ // If at any point dut_pipeline_enabled is deasserted, halt and assert disable_detected.
+ task wait_rng_queue(output rng_val_t val, output bit disable_detected);
+ push_pull_item#(.HostDataWidth(RNG_BUS_WIDTH)) rng_item;
+ bit bit_sel_enable = (cfg.rng_bit_enable == prim_mubi_pkg::MuBi4True);
+ int n_items = bit_sel_enable ? RNG_BUS_WIDTH : 1;
+ disable_detected = 0;
+
+ if (!dut_pipeline_enabled) begin
+ wait(dut_pipeline_enabled);
+ `uvm_info(`gfn, "Enable detected", UVM_LOW);
+ end
+ for (int i = 0; i < n_items; i++) begin : rng_loop
+ fork : isolation_fork
+ begin
+ fork
+ rng_fifo.get(rng_item);
+ begin
+ wait(!dut_pipeline_enabled);
+ `uvm_info(`gfn, "Disable detected", UVM_LOW);
+ end
+ join_any
+ disable fork;
+ end
+ join : isolation_fork
+ if (!dut_pipeline_enabled) begin
+ `uvm_info(`gfn, "Flushing data on disable", UVM_MEDIUM)
+ disable_detected = 1;
+ break;
+ end
+ if (bit_sel_enable) begin
+ val[i] = rng_item.h_data[cfg.rng_bit_sel];
+ end else begin
+ val = rng_item.h_data;
+ end
+ end : rng_loop
+ endtask
+
task collect_entropy();
bit [15:0] window_size;
entropy_phase_e dut_fsm_phase;
- push_pull_item#(.HostDataWidth(RNG_BUS_WIDTH)) rng_item;
rng_val_t rng_val;
// TODO rename window to "sample"
queue_of_rng_val_t window;
queue_of_rng_val_t sample;
int window_rng_frames;
int pass_requirement, pass_cnt;
- int retry_limit, retry_cnt;
bit ht_fips_mode;
+ bit disable_detected;
- retry_cnt = 0;
pass_cnt = 0;
window.delete();
@@ -832,24 +959,23 @@
forever begin : collect_entropy_loop
- dut_fsm_phase = convert_seed_idx_to_phase(seed_idx,
- cfg.boot_bypass_disable == prim_mubi_pkg::MuBi4True);
+ `uvm_info(`gfn, $sformatf("SEED_IDX: %01d", seed_idx), UVM_FULL)
+ dut_fsm_phase = convert_seed_idx_to_phase(seed_idx,
+ cfg.type_bypass == prim_mubi_pkg::MuBi4True,
+ cfg.boot_bypass_disable == prim_mubi_pkg::MuBi4True);
case (dut_fsm_phase)
BOOT: begin
pass_requirement = 1;
- retry_limit = cfg.boot_mode_retry_limit;
ht_fips_mode = 0;
end
STARTUP: begin
pass_requirement = 2;
- retry_limit = 2;
ht_fips_mode = 1;
end
CONTINUOUS: begin
- pass_requirement = 0;
- retry_limit = 2;
+ pass_requirement = 1;
ht_fips_mode = 1;
end
default: begin
@@ -876,32 +1002,30 @@
window_rng_frames = window_size / RNG_BUS_WIDTH;
window.delete();
- if(dut_fsm_phase != STARTUP) begin
+ // Should the next window be added to the previous SHA3 message?
+ // In boot or bypass mode the answer is "no"
+ if(dut_fsm_phase == BOOT || cfg.type_bypass != prim_mubi_pkg::MuBi4True) begin
sample.delete();
end
while (window.size() < window_rng_frames) begin
- if (cfg.rng_bit_enable == prim_mubi_pkg::MuBi4True) begin
- for (int i = 0; i < RNG_BUS_WIDTH; i++) begin
- rng_fifo.get(rng_item);
- rng_val[i] = rng_item.h_data[cfg.rng_bit_sel];
- end
+ wait_rng_queue(rng_val, disable_detected);
+ if (disable_detected) begin
+ // Exit this task.
+ return;
end else begin
- rng_fifo.get(rng_item);
- rng_val = rng_item.h_data;
+ window.push_back(rng_val);
+ // The repetition count is updated continuously.
+ // The other health checks only operate on complete windows, and are processed later.
+ // TODO: Confirm how repcnt is applied in bit-select mode
+ update_repcnts(rng_val);
end
- window.push_back(rng_val);
- // The repetition count is updated continuously.
- // The other health checks only operate on complete windows, and are processed later.
- update_repcnts(rng_val);
end
- `uvm_info(`gfn, "FULL_WINDOW", UVM_FULL)
+ `uvm_info(`gfn, "FULL_WINDOW", UVM_FULL)
if (health_check_rng_data(window, ht_fips_mode)) begin
- retry_cnt++;
pass_cnt = 0;
end else begin
- retry_cnt = 0;
pass_cnt++;
end
@@ -910,24 +1034,18 @@
sample.push_back(window.pop_front());
end
- `uvm_info(`gfn, $sformatf("pass_cnt: %01d, retry_cnt: %01d", pass_cnt, retry_cnt), UVM_HIGH)
`uvm_info(`gfn, $sformatf("pass_requirement: %01d", pass_requirement), UVM_HIGH)
- `uvm_info(`gfn, $sformatf("retry_limit: %01d", retry_limit), UVM_HIGH)
`uvm_info(`gfn, $sformatf("sample.size: %01d", sample.size()), UVM_HIGH)
- // TODO: Update health check stats.
- if (retry_cnt >= retry_limit) begin
- // TODO: Alert state
- `uvm_info(`gfn, "TODO: manage alerts", UVM_FULL)
- end else if (pass_cnt >= pass_requirement) begin
+ // Health check alert stats and alert handling managed in
+ // health_check_rng_data
+ if (pass_cnt >= pass_requirement && !threshold_alert_active) begin
bit [FIPS_CSRNG_BUS_WIDTH - 1:0] fips_csrng;
bit [CSRNG_BUS_WIDTH - 1:0] csrng_seed;
fips_csrng = predict_fips_csrng(sample);
`uvm_info(`gfn, $sformatf("sample.size(): %01d", sample.size()), UVM_FULL)
-
// update counters for processing next seed:
- retry_cnt = 0;
pass_cnt = 0;
seed_idx++;
@@ -943,10 +1061,7 @@
prev_csrng_seed = csrng_seed;
- end else begin
- // Inconsequential health check failure
end
-
end : collect_entropy_loop
endtask
@@ -980,7 +1095,13 @@
virtual function void reset(string kind = "HARD");
super.reset(kind);
- // reset local fifos queues and variables
+ if(kind == "HARD") begin
+ // reset local fifos queues and variables
+ handle_disable_reset(HardReset);
+ // Immediately inform the process_entropy process
+ // that the IP is disabled
+ dut_pipeline_enabled = 0;
+ end
endfunction
function void check_phase(uvm_phase phase);
diff --git a/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_base_vseq.sv b/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_base_vseq.sv
index 5d68e39..a02c2ef 100644
--- a/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_base_vseq.sv
+++ b/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_base_vseq.sv
@@ -10,10 +10,12 @@
);
`uvm_object_utils(entropy_src_base_vseq)
- rand bit [3:0] rng_val;
+ rand bit [3:0] rng_val;
+ rand bit [NumEntropySrcIntr - 1:0] en_intr;
// various knobs to enable certain routines
bit do_entropy_src_init = 1'b1;
+ bit do_interrupt = 1'b1;
virtual entropy_src_cov_if cov_vif;
@@ -43,29 +45,21 @@
//
task check_ht_diagnostics();
int val;
+ string stat_regs [] = '{
+ "repcnt_hi_watermarks", "repcnts_hi_watermarks", "adaptp_hi_watermarks",
+ "adaptp_lo_watermarks", "extht_hi_watermarks", "extht_lo_watermarks",
+ "bucket_hi_watermarks", "markov_hi_watermarks", "markov_lo_watermarks",
+ "repcnt_total_fails", "repcnts_total_fails", "adaptp_hi_total_fails",
+ "adaptp_lo_total_fails", "bucket_total_fails", "markov_hi_total_fails",
+ "markov_lo_total_fails", "extht_hi_total_fails", "extht_lo_total_fails",
+ "alert_summary_fail_counts", "alert_fail_counts", "extht_fail_counts"
+ };
+ foreach (stat_regs[i]) begin
+ int val;
+ uvm_reg csr = ral.get_reg_by_name(stat_regs[i]);
+ csr_rd(.ptr(csr), .value(val));
+ end
- csr_rd(.ptr( ral.repcnt_hi_watermarks), .value(val));
- csr_rd(.ptr(ral.repcnts_hi_watermarks), .value(val));
- csr_rd(.ptr( ral.adaptp_hi_watermarks), .value(val));
- csr_rd(.ptr( ral.adaptp_lo_watermarks), .value(val));
- csr_rd(.ptr( ral.extht_hi_watermarks), .value(val));
- csr_rd(.ptr( ral.extht_lo_watermarks), .value(val));
- csr_rd(.ptr( ral.bucket_hi_watermarks), .value(val));
- csr_rd(.ptr( ral.markov_hi_watermarks), .value(val));
- csr_rd(.ptr( ral.markov_lo_watermarks), .value(val));
-
- csr_rd(.ptr( ral.repcnt_total_fails), .value(val));
- csr_rd(.ptr( ral.repcnts_total_fails), .value(val));
- csr_rd(.ptr(ral.adaptp_hi_total_fails), .value(val));
- csr_rd(.ptr(ral.adaptp_lo_total_fails), .value(val));
- csr_rd(.ptr( ral.extht_hi_total_fails), .value(val));
- csr_rd(.ptr( ral.extht_lo_total_fails), .value(val));
- csr_rd(.ptr( ral.bucket_total_fails), .value(val));
- csr_rd(.ptr(ral.markov_hi_total_fails), .value(val));
- csr_rd(.ptr(ral.markov_lo_total_fails), .value(val));
-
- csr_rd(.ptr( ral.alert_fail_counts), .value(val));
- csr_rd(.ptr( ral.extht_fail_counts), .value(val));
endtask
virtual task apply_reset(string kind = "HARD");
@@ -121,6 +115,11 @@
ral.conf.rng_bit_sel.set(cfg.rng_bit_sel);
csr_update(.csr(ral.conf));
+ if (do_interrupt) begin
+ ral.intr_enable.set(en_intr);
+ csr_update(ral.intr_enable);
+ end
+
// Register write enable lock is on be default
// Setting this to zero will lock future writes
// TODO Do we need to check main_sm_idle before writing DUT registers?
diff --git a/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_rng_vseq.sv b/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_rng_vseq.sv
index 67fa3c4..21622be 100644
--- a/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_rng_vseq.sv
+++ b/hw/ip/entropy_src/dv/env/seq_lib/entropy_src_rng_vseq.sv
@@ -9,11 +9,41 @@
`uvm_object_utils(entropy_src_rng_vseq)
- `uvm_object_new
-
push_pull_indefinite_host_seq#(entropy_src_pkg::FIPS_CSRNG_BUS_WIDTH) m_csrng_pull_seq;
entropy_src_base_rng_seq m_rng_push_seq;
+ rand uint dly_to_access_intr;
+ rand uint dly_to_access_alert_sts;
+ rand bit do_check_ht_diag;
+ rand bit do_clear_ht_alert;
+
+ int do_interrupts;
+
+ constraint dly_to_access_intr_c {
+ dly_to_access_intr dist {
+ 0 :/ 1,
+ [1 :100] :/ 5,
+ [101 :10_000] :/ 3
+ };
+ }
+
+ constraint dly_to_access_alert_sts_c {
+ dly_to_access_alert_sts dist {
+ 0 :/ 1,
+ [1 :100] :/ 5,
+ [101 :10_000] :/ 3
+ };
+ }
+
+ constraint do_check_ht_diag_c {
+ do_check_ht_diag dist {
+ 0 :/ cfg.do_check_ht_diag_pct,
+ 1 :/ 100 - cfg.do_check_ht_diag_pct
+ };
+ }
+
+ `uvm_object_new
+
task software_read_seed();
int seeds_found;
`uvm_info(`gfn, "CSR Thread: Reading seed via SW", UVM_FULL)
@@ -109,9 +139,54 @@
m_rng_push_seq.hard_mtbf = cfg.hard_mtbf;
m_rng_push_seq.soft_mtbf = cfg.soft_mtbf;
+ do_interrupts = 1'b1;
+
super.pre_start();
endtask
+ task clear_ht_alert();
+ bit alert_sts;
+ // Health check failures are coincident with an alert, which needs to also be cleared
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_intr);
+ cfg.clk_rst_vif.wait_clks(dly_to_access_intr);
+ csr_rd(.ptr(ral.recov_alert_sts.es_main_sm_alert), .value(alert_sts));
+ if (alert_sts) begin
+ `uvm_info(`gfn, "Identified main_sm alert", UVM_HIGH)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(do_check_ht_diag)
+ if (do_check_ht_diag) begin
+ // read all health check values
+ `uvm_info(`gfn, "Checking_ht_values", UVM_HIGH)
+ check_ht_diagnostics();
+ `uvm_info(`gfn, "ht value check complete", UVM_HIGH)
+ end
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_alert_sts)
+ cfg.clk_rst_vif.wait_clks(dly_to_access_alert_sts);
+ `uvm_info(`gfn, "Clearing ES SM Alerts", UVM_HIGH)
+ csr_wr(.ptr(ral.conf.enable), .value(prim_mubi_pkg::MuBi4False));
+ `uvm_info(`gfn, "DUT disabled", UVM_HIGH)
+ csr_wr(.ptr(ral.recov_alert_sts.es_main_sm_alert), .value(1'b1));
+ csr_wr(.ptr(ral.conf.enable), .value(prim_mubi_pkg::MuBi4True));
+ end
+ endtask
+
+ task process_interrupts();
+ bit [TL_DW - 1:0] intr_status, clear_intr;
+ `uvm_info(`gfn, "process interrupts", UVM_HIGH)
+
+ // avoid zero delay loop during reset
+ wait(!cfg.under_reset);
+ // read interrupt
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_intr)
+ cfg.clk_rst_vif.wait_clks(dly_to_access_intr);
+ csr_rd(.ptr(ral.intr_state), .value(intr_status));
+ if (intr_status[HealthTestFailed]) begin
+ // TODO: I think there is a distinction between health test failure and alert.
+ // We may be able to economize (or diversify) this test by only doing this on alerts.
+ `uvm_info(`gfn, "Health test failure detected", UVM_HIGH)
+ clear_ht_alert();
+ end
+ endtask
+
task body();
super.body();
// Start sequences
@@ -119,6 +194,11 @@
m_rng_push_seq.start(p_sequencer.rng_sequencer_h);
m_csrng_pull_seq.start(p_sequencer.csrng_sequencer_h);
begin
+ `uvm_info(`gfn, "Starting interrupt loop", UVM_HIGH)
+ while (do_interrupts) process_interrupts();
+ `uvm_info(`gfn, "Exiting interrupt loop", UVM_HIGH)
+ end
+ begin
csr_access_seq();
// Once the CSR access is done, we can shut down everything else
// Note: the CSRNG agent needs to be completely shut down before
@@ -131,6 +211,7 @@
m_rng_push_seq.stop(.hard(0));
m_rng_push_seq.wait_for_sequence_state(UVM_FINISHED);
`uvm_info(`gfn, "Exiting test body.", UVM_LOW)
+ do_interrupts = 1'b0;
end
join
endtask : body
diff --git a/hw/ip/entropy_src/dv/tests/entropy_src_rng_test.sv b/hw/ip/entropy_src/dv/tests/entropy_src_rng_test.sv
index 4df9a0a..4f553f1 100644
--- a/hw/ip/entropy_src/dv/tests/entropy_src_rng_test.sv
+++ b/hw/ip/entropy_src/dv/tests/entropy_src_rng_test.sv
@@ -17,8 +17,8 @@
cfg.bypass_window_size = 384;
cfg.boot_mode_retry_limit = 10;
cfg.entropy_data_reg_enable_pct = 100;
- cfg.sim_duration = 100us;
- cfg.hard_mtbf = 100ms;
+ cfg.sim_duration = 10ms;
+ cfg.hard_mtbf = 100s;
cfg.soft_mtbf = 7500us;
cfg.adaptp_sigma_min = 1.0;
cfg.adaptp_sigma_max = 2.0;