[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();