[dv/shadow_reg] Fix aes shadow reg error
This PR support aes shadow reg with the new feature reported in
PR #4895 :
AES shadow reg fatal error will lock this register's write access.
This PR updates the testbench regarding this feature:
1. Add a `shadow_fatal_lock` local variable to indicate if the shadow
reg is locked due to fatal error.
2. Temp unlock the `shadow_fatal_lock` if a backdoor write is issued.
Because tesbench can still update the shadow reg value.
Signed-off-by: Cindy Chen <chencindy@google.com>
diff --git a/hw/dv/sv/cip_lib/cip_base_vseq.sv b/hw/dv/sv/cip_lib/cip_base_vseq.sv
index 72af65a..012d29c 100644
--- a/hw/dv/sv/cip_lib/cip_base_vseq.sv
+++ b/hw/dv/sv/cip_lib/cip_base_vseq.sv
@@ -9,10 +9,14 @@
extends dv_base_vseq #(RAL_T, CFG_T, COV_T, VIRTUAL_SEQUENCER_T);
`uvm_object_new
// knobs to disable post_start clear interrupt routine
- bit do_clear_all_interrupts = 1'b1;
+ bit do_clear_all_interrupts = 1'b1;
+
// knobs to enable alert auto reponse, once disabled it won't be able to enable again unless
// dut_init is issued
- bit en_auto_alerts_response = 1'b1;
+ bit en_auto_alerts_response = 1'b1;
+
+ // knobs to lock shadow register write access if fatal storage error occurred
+ bit do_lock_shadow_reg = 1'b1;
// csr queue for intr test/enable/state
dv_base_reg intr_test_csrs[$];
@@ -703,6 +707,10 @@
test_csrs[i].get_full_name()), UVM_MEDIUM)
continue;
end
+
+ // skip alert_test register because it will trigger alerts
+ if (test_csrs[i].get_name() == "alert_test") continue;
+
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest, csr_excl);
@@ -738,37 +746,42 @@
bkdr_reg_path_e kind;
int shadow_reg_width = shadowed_csrs[index].get_msb_pos() + 1;
- `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(
- kind, kind inside {BkdrRegPathRtlCommitted, BkdrRegPathRtlShadow};)
- csr_peek(.ptr(shadowed_csrs[index]), .value(origin_val), .kind(kind));
- rand_val = gen_storage_err_val(shadowed_csrs[index], origin_val);
+ if (shadowed_csrs[index].shadow_reg_is_locked() == 0) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(
+ kind, kind inside {BkdrRegPathRtlCommitted, BkdrRegPathRtlShadow};)
+ csr_peek(.ptr(shadowed_csrs[index]), .value(origin_val), .kind(kind));
+ rand_val = gen_storage_err_val(shadowed_csrs[index], origin_val);
- csr_poke(.csr(shadowed_csrs[index]), .value(rand_val), .kind(kind), .predict(1));
- `uvm_info(`gfn, $sformatf("backdoor write %0s with value %0h", kind.name, rand_val),
- UVM_HIGH);
+ csr_poke(.csr(shadowed_csrs[index]), .value(rand_val), .kind(kind), .predict(1));
+ `uvm_info(`gfn, $sformatf("backdoor write %0s with value %0h", kind.name, rand_val),
+ UVM_HIGH);
- // check shadow_reg storage error
- if ((origin_val ^ rand_val) & ((1 << shadow_reg_width) - 1)) begin
- string alert_name = shadowed_csrs[index].get_storage_err_alert_name();
- bit has_storage_error;
+ // check shadow_reg storage error
+ if ((origin_val ^ rand_val) & ((1 << shadow_reg_width) - 1)) begin
+ string alert_name = shadowed_csrs[index].get_storage_err_alert_name();
+ bit has_storage_error;
+ shadow_reg_storage_err_post_write();
+ has_storage_error = shadowed_csrs[index].get_shadow_storage_err();
- shadow_reg_storage_err_post_write();
- has_storage_error = shadowed_csrs[index].get_shadow_storage_err();
+ if (has_storage_error && do_lock_shadow_reg) begin
+ shadowed_csrs[index].lock_shadow_reg();
+ end
- `DV_CHECK_EQ(has_storage_error, 1,
- "dv_base_reg did not predict shadow storage error");
- `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert())
- cfg.clk_rst_vif.wait_clks(1);,
- $sformatf("%0s shadow_reg storage_err alert not detected",
- shadowed_csrs[index].get_name()));
+ `DV_CHECK_EQ(has_storage_error, 1,
+ "dv_base_reg did not predict shadow storage error");
+ `DV_SPINWAIT(while (!cfg.m_alert_agent_cfg[alert_name].vif.get_alert())
+ cfg.clk_rst_vif.wait_clks(1);,
+ $sformatf("%0s shadow_reg storage_err alert not detected",
+ shadowed_csrs[index].get_name()));
- // backdoor write back original value to avoid alert keep firing
- csr_poke(.csr(shadowed_csrs[index]), .value(origin_val), .kind(kind), .predict(1));
- `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
- $sformatf("timeout for alert:%0s", alert_name))
+ // backdoor write back original value to avoid alert keep firing
+ csr_poke(.csr(shadowed_csrs[index]), .value(origin_val), .kind(kind), .predict(1));
+ `DV_SPINWAIT(cfg.m_alert_agent_cfg[alert_name].vif.wait_ack_complete();,
+ $sformatf("timeout for alert:%0s", alert_name))
- // wait at least two clock cycle between alert_handshakes
- cfg.clk_rst_vif.wait_clks(2);
+ // wait at least two clock cycle between alert_handshakes
+ cfg.clk_rst_vif.wait_clks(2);
+ end
end
end
end
@@ -795,6 +808,8 @@
end
end
end
+ // Shadow register fatal error will continuously trigger alerts until reset
+ dut_init();
endtask
// test partial mem read with non-blocking random read/write
diff --git a/hw/dv/sv/csr_utils/csr_utils_pkg.sv b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
index 0040180..d3ee3c7 100644
--- a/hw/dv/sv/csr_utils/csr_utils_pkg.sv
+++ b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
@@ -343,7 +343,7 @@
end
// poke always updates predict value, if predict == 0, revert back to old mirrored value
if (!predict || kind == BkdrRegPathRtlShadow) begin
- void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT)));
+ void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT), .path(UVM_BACKDOOR)));
end
endtask
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 2638605..810d002 100644
--- a/hw/dv/sv/dv_base_reg/dv_base_reg.sv
+++ b/hw/dv/sv/dv_base_reg/dv_base_reg.sv
@@ -14,6 +14,8 @@
local bit shadow_wr_staged; // stage the first shadow reg write
local bit shadow_update_err;
local bit en_shadow_wr = 1;
+ // In certain shadow reg (e.g. in AES), fatal error can lock write access
+ local bit shadow_fatal_lock;
local string update_err_alert_name;
local string storage_err_alert_name;
@@ -143,7 +145,7 @@
// no need to update shadow value or access type if access is not OK, as access is aborted
if (rw.status != UVM_IS_OK) return;
- if (is_shadowed) begin
+ if (is_shadowed && !shadow_fatal_lock) begin
// first write
if (!shadow_wr_staged) begin
shadow_wr_staged = 1;
@@ -206,15 +208,20 @@
if (is_shadowed) atomic_shadow_wr.put(1);
endtask
- // override do_predict function to support shadow_reg:
- // skip predict if it is shadow_reg's first write, or second write with an update_err
+ // Override do_predict function to support shadow_reg.
+ // Skip predict in one of the following conditions:
+ // 1). It is shadow_reg's first write.
+ // 2). It is shadow_reg's second write with an update_err.
+ // 2). The shadow_reg is locked due to fatal storage error and it is not a backdoor write.
+
virtual function void do_predict (uvm_reg_item rw,
uvm_predict_e kind = UVM_PREDICT_DIRECT,
uvm_reg_byte_en_t be = -1);
- if (is_shadowed && (shadow_wr_staged || shadow_update_err) && kind != UVM_PREDICT_READ) begin
- `uvm_info(`gfn,
- $sformatf("skip predict csr %s: due to shadow_reg_first_wr=%0b or update_err=%0b",
- get_name(), shadow_wr_staged, shadow_update_err), UVM_HIGH)
+ if (is_shadowed && kind != UVM_PREDICT_READ && (shadow_wr_staged || shadow_update_err ||
+ (shadow_fatal_lock && rw.path != UVM_BACKDOOR))) begin
+ `uvm_info(`gfn, $sformatf(
+ "skip predict %s: due to shadow_reg_first_wr=%0b, update_err=%0b, shadow_fatal_lock=%0b",
+ get_name(), shadow_wr_staged, shadow_update_err, shadow_fatal_lock), UVM_HIGH)
return;
end
super.do_predict(rw, kind, be);
@@ -229,12 +236,15 @@
input int lineno = 0);
if (kind == "BkdrRegPathRtlShadow") shadowed_val = value;
else if (kind == "BkdrRegPathRtlCommitted") committed_val = value;
+
super.poke(status, value, kind, parent, extension, fname, lineno);
endtask
- // callback function to update shadowed values according to specific design
- // should only be called after post-write
+ // Callback function to update shadowed values according to specific design.
+ // Should only be called after post-write.
+ // If a shadow reg is locked due to fatal error, this function will return without updates
virtual function void update_shadowed_val(uvm_reg_data_t val, bit do_predict = 1);
+ if (shadow_fatal_lock) return;
if (shadow_wr_staged) begin
// update value after first write
staged_shadow_val = val;
@@ -256,6 +266,7 @@
if (is_shadowed) begin
shadow_update_err = 0;
shadow_wr_staged = 0;
+ shadow_fatal_lock = 0;
committed_val = get_mirrored_value();
shadowed_val = ~committed_val;
// in case reset is issued during shadowed writes
@@ -284,6 +295,14 @@
return ($sformatf("%0s_%0s", parent_name, update_err_alert_name));
endfunction
+ function void lock_shadow_reg();
+ shadow_fatal_lock = 1;
+ endfunction
+
+ function bit shadow_reg_is_locked();
+ return shadow_fatal_lock;
+ endfunction
+
function string get_storage_err_alert_name();
string parent_name = this.get_parent().get_name();