[dv/alert_handler] Support shadow register sequence
This PR supports alert_handler shadow reg sequence. The alert_handler
does not have any outgoing alerts, so the checking is slightly different
from common sequence.
Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__shadow_reg_errors.svh b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__shadow_reg_errors.svh
index 6b8c8e1..25b95b6 100644
--- a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__shadow_reg_errors.svh
+++ b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__shadow_reg_errors.svh
@@ -32,12 +32,15 @@
end
begin
string alert_name = csr.get_update_err_alert_name();
- `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert()) begin
- cfg.clk_rst_vif.wait_clks(1);
- end,
- $sformatf("%0s update_err alert not detected", csr.get_name()))
- `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
- $sformatf("timeout for alert:%0s", alert_name))
+ // This logic gates alert_handler testbench because it does not have outgoing alert.
+ if (cfg.m_alert_agent_cfg.exists(alert_name)) begin
+ `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert()) begin
+ cfg.clk_rst_vif.wait_clks(1);
+ end,
+ $sformatf("%0s update_err alert not detected", csr.get_name()))
+ `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
+ $sformatf("timeout for alert:%0s", alert_name))
+ end
end
join
endtask
@@ -81,7 +84,9 @@
csr_wr(.ptr(shadowed_csr), .value(wdata), .en_shadow_wr(0), .predict(1));
shadow_reg_wr(shadowed_csr, err_wdata);
- predict_shadow_reg_status(.predict_update_err(1));
+ // If the shadow register is external register, writing two different value might not actually
+ // trigger update error. So we trigger dv_base_reg function to double check.
+ predict_shadow_reg_status(.predict_update_err(shadowed_csr.get_shadow_update_err()));
read_check_shadow_reg_status("Write_and_check_update_error task");
csr_rd_check(.ptr(shadowed_csr), .compare_vs_ral(1));
@@ -104,8 +109,10 @@
wdata = get_shadow_reg_diff_val(shadowed_csr, wdata);
csr_wr(.ptr(shadowed_csr), .value(wdata), .en_shadow_wr(1), .predict(1));
- `DV_CHECK_EQ(cfg.m_alert_agent_cfg[alert_name].vif.get_alert(), 0,
- $sformatf("Unexpected alert: %s fired", alert_name))
+ if (cfg.m_alert_agent_cfg.exists(alert_name)) begin
+ `DV_CHECK_EQ(cfg.m_alert_agent_cfg[alert_name].vif.get_alert(), 0,
+ $sformatf("Unexpected alert: %s fired", alert_name))
+ end
read_check_shadow_reg_status("Check_csr_read_clear_staged_val task");
csr_rd_check(.ptr(shadowed_csr), .compare_vs_ral(1));
@@ -127,12 +134,12 @@
err_val = get_shadow_reg_diff_val(shadowed_csr, origin_val);
csr_poke(.ptr(shadowed_csr), .value(err_val), .kind(kind), .predict(1));
- predict_shadow_reg_status(.predict_storage_err(1));
+ predict_shadow_reg_status(.predict_storage_err(shadowed_csr.get_shadow_storage_err()));
`uvm_info(`gfn, $sformatf("backdoor write %s through %s with value 0x%0h",
shadowed_csr.`gfn, kind.name, err_val), UVM_HIGH);
// This non-blocking task checks if the alert is continuously firing until reset is issued.
- check_fatal_alert_nonblocking(alert_name);
+ if (cfg.m_alert_agent_cfg.exists(alert_name)) check_fatal_alert_nonblocking(alert_name);
// Wait random clock cycles and ensure the fatal alert is continuously firing.
cfg.clk_rst_vif.wait_clks($urandom_range(10, 100));
diff --git a/hw/dv/sv/dv_base_reg/dv_base_reg.sv b/hw/dv/sv/dv_base_reg/dv_base_reg.sv
index a4aca6e..a3fda0f 100644
--- a/hw/dv/sv/dv_base_reg/dv_base_reg.sv
+++ b/hw/dv/sv/dv_base_reg/dv_base_reg.sv
@@ -26,6 +26,7 @@
int has_coverage);
super.new(name, n_bits, has_coverage);
atomic_en_shadow_wr = new(1);
+ shadowed_val = ~committed_val;
endfunction : new
function void get_dv_base_reg_fields(ref dv_base_reg_field dv_fields[$]);
@@ -159,11 +160,11 @@
endfunction
function bit get_shadow_storage_err();
- uvm_reg_data_t mask = (1'b1 << (get_msb_pos() + 1)) - 1;
+ uvm_reg_data_t mask = get_reg_mask();
uvm_reg_data_t shadowed_val_temp = (~shadowed_val) & mask;
uvm_reg_data_t committed_val_temp = committed_val & mask;
- `uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val_temp,
- committed_val_temp), UVM_DEBUG)
+ `uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val & mask,
+ committed_val_temp), UVM_HIGH)
return shadowed_val_temp != committed_val_temp;
endfunction
@@ -182,19 +183,20 @@
// no need to update shadow value or access type if access is not OK, as access is aborted
// no need to update if not write
- if (rw.status != UVM_IS_OK || kind != UVM_PREDICT_WRITE) return;
+ // no need to update staged value if it triggers a storage error
+ if (rw.status != UVM_IS_OK || kind != UVM_PREDICT_WRITE || get_shadow_storage_err()) return;
if (is_shadowed && !shadow_fatal_lock) begin
// first write
if (!shadow_wr_staged) begin
shadow_wr_staged = 1;
// rw.value is a dynamic array
- staged_shadow_val = rw.value[0];
+ staged_shadow_val = rw.value[0] & get_reg_mask();
return;
end begin
// second write
shadow_wr_staged = 0;
- if (staged_shadow_val != rw.value[0]) begin
+ if (staged_shadow_val != (rw.value[0] & get_reg_mask())) begin
dv_base_reg_field dv_fields[$];
get_dv_base_reg_fields(dv_fields);
@@ -212,7 +214,7 @@
// If there is no shadow_update error, update the entire committed value.
committed_val = staged_shadow_val;
end
- shadowed_val = ~committed_val;
+ shadowed_val = ~committed_val;
end
lock_lockable_flds(committed_val);
end else begin
diff --git a/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson b/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
index b25cc23..d967bce 100644
--- a/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
+++ b/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
@@ -27,6 +27,7 @@
"{proj_root}/hw/dv/tools/dvsim/tests/csr_tests.hjson",
"{proj_root}/hw/dv/tools/dvsim/tests/intr_test.hjson",
"{proj_root}/hw/dv/tools/dvsim/tests/tl_access_tests.hjson",
+ "{proj_root}/hw/dv/tools/dvsim/tests/shadow_reg_errors_tests.hjson",
"{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"]
// Add additional tops for simulation.
diff --git a/hw/ip/alert_handler/dv/env/alert_handler_env_cfg.sv b/hw/ip/alert_handler/dv/env/alert_handler_env_cfg.sv
index dc6d390..c63b991 100644
--- a/hw/ip/alert_handler/dv/env/alert_handler_env_cfg.sv
+++ b/hw/ip/alert_handler/dv/env/alert_handler_env_cfg.sv
@@ -20,6 +20,8 @@
virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
has_edn = 1;
super.initialize(csr_base_addr);
+ shadow_update_err_status_fields[ral.loc_alert_cause[LocalShadowRegUpdateErr].la] = 1;
+ shadow_storage_err_status_fields[ral.loc_alert_cause[LocalShadowRegStorageErr].la] = 1;
// set num_interrupts & num_alerts
begin
diff --git a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_common_vseq.sv b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_common_vseq.sv
index d023523..3095e6d 100644
--- a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_common_vseq.sv
+++ b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_common_vseq.sv
@@ -29,4 +29,22 @@
csr_rd_check(.ptr(ral.loc_alert_cause[LocalBusIntgFail]), .compare_value(exp_val));
endtask
+ virtual function void predict_shadow_reg_status(bit predict_update_err = 0,
+ bit predict_storage_err = 0);
+ if (predict_update_err) begin
+ foreach (cfg.shadow_update_err_status_fields[status_field]) begin
+ if (`gmv(ral.loc_alert_en_shadowed[LocalShadowRegUpdateErr])) begin
+ void'(status_field.predict(cfg.shadow_update_err_status_fields[status_field]));
+ end
+ end
+ end
+ if (predict_storage_err) begin
+ foreach (cfg.shadow_storage_err_status_fields[status_field]) begin
+ if (`gmv(ral.loc_alert_en_shadowed[LocalShadowRegStorageErr])) begin
+ void'(status_field.predict(cfg.shadow_storage_err_status_fields[status_field]));
+ end
+ end
+ end
+ endfunction
+
endclass