[keymgr/dv] Fix a few issues in scb for LC disable

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/ip/keymgr/dv/env/keymgr_if.sv b/hw/ip/keymgr/dv/env/keymgr_if.sv
index 85675d2..c55f977 100644
--- a/hw/ip/keymgr/dv/env/keymgr_if.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_if.sv
@@ -288,6 +288,16 @@
     return keymgr_en_sync2 === lc_ctrl_pkg::On;
   endfunction
 
+  function automatic void wipe_sideload_keys();
+    aes_key_exp.valid  <= 0;
+    kmac_key_exp.valid <= 0;
+    otbn_key_exp.valid <= 0;
+
+    aes_sideload_status  <= SideLoadClear;
+    kmac_sideload_status <= SideLoadClear;
+    otbn_sideload_status <= SideLoadClear;
+  endfunction
+
   task automatic force_cmd_err();
     @(posedge clk);
     randcase
@@ -341,6 +351,7 @@
           is_kmac_key_good <= 1;
         end else begin
           kmac_key_exp.valid <= 0;
+          is_kmac_key_good   <= 0;
         end
       end // kmac_data_rsp.done
     end // forever
diff --git a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
index c30d65f..c5efcda 100644
--- a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
@@ -54,6 +54,7 @@
   keymgr_pkg::keymgr_op_status_e     current_op_status;
   bit                                is_kmac_rsp_err;
   bit                                is_kmac_invalid_data;
+  bit                                invalid_status_err;
   bit                                is_sw_share_corrupted;
 
   // HW internal key, used for OP in current state
@@ -120,12 +121,13 @@
   virtual function void process_kmac_data_req(kmac_app_item item);
     keymgr_pkg::keymgr_ops_e op = get_operation();
 
-    // there must be a OP which causes the KMAC data req
-    `DV_CHECK_EQ(current_op_status, keymgr_pkg::OpWip)
 
     if (!cfg.keymgr_vif.get_keymgr_en()) begin
       compare_invalid_data(item.byte_data_q);
       return;
+    end else begin
+      // there must be a OP which causes the KMAC data req
+      `DV_CHECK_EQ(current_op_status, keymgr_pkg::OpWip)
     end
 
     case (op)
@@ -191,18 +193,19 @@
              current_internal_key[current_cdi], current_state.name, current_cdi.name), UVM_MEDIUM)
       end
       UpdateSwOut: begin
-        bit [keymgr_pkg::Shares-1:0][DIGEST_SHARE_WORD_NUM-1:0][TL_DW-1:0] sw_share_output;
+        if (!get_fault_err) begin
+          bit [keymgr_pkg::Shares-1:0][DIGEST_SHARE_WORD_NUM-1:0][TL_DW-1:0] sw_share_output;
+          // digest is 384 bits wide while SW output is only 256, need to truncate it
+          sw_share_output = {item.rsp_digest_share1[keymgr_pkg::KeyWidth-1:0],
+                             item.rsp_digest_share0[keymgr_pkg::KeyWidth-1:0]};
+          foreach (sw_share_output[i, j]) begin
+            string csr_name = $sformatf("sw_share%0d_output_%0d", i, j);
+            uvm_reg csr = ral.get_reg_by_name(csr_name);
 
-        // digest is 384 bits wide while SW output is only 256, need to truncate it
-        sw_share_output = {item.rsp_digest_share1[keymgr_pkg::KeyWidth-1:0],
-                           item.rsp_digest_share0[keymgr_pkg::KeyWidth-1:0]};
-        foreach (sw_share_output[i, j]) begin
-          string csr_name = $sformatf("sw_share%0d_output_%0d", i, j);
-          uvm_reg csr = ral.get_reg_by_name(csr_name);
-
-          void'(csr.predict(.value(sw_share_output[i][j]), .kind(UVM_PREDICT_DIRECT)));
-          `uvm_info(`gfn, $sformatf("Predict %0s = 0x%0h", csr_name, sw_share_output[i][j]),
-                    UVM_MEDIUM)
+            void'(csr.predict(.value(sw_share_output[i][j]), .kind(UVM_PREDICT_READ)));
+            `uvm_info(`gfn, $sformatf("Predict %0s = 0x%0h", csr_name, sw_share_output[i][j]),
+                      UVM_MEDIUM)
+          end
         end
       end
       UpdateHwOut: begin
@@ -210,7 +213,7 @@
         keymgr_pkg::keymgr_key_dest_e dest = keymgr_pkg::keymgr_key_dest_e'(
             `gmv(ral.control.dest_sel));
 
-        if (dest != keymgr_pkg::None) begin
+        if (dest != keymgr_pkg::None && !get_fault_err()) begin
           cfg.keymgr_vif.update_sideload_key(key_shares, current_state, current_cdi, dest);
           `uvm_info(`gfn, $sformatf("Update sideload key 0x%0h for %s", key_shares, dest.name),
                     UVM_MEDIUM)
@@ -219,8 +222,6 @@
       default: `uvm_info(`gfn, "KMAC result isn't updated to any output", UVM_MEDIUM)
     endcase
 
-    if (!cfg.keymgr_vif.get_keymgr_en()) current_state = keymgr_pkg::StInvalid;
-
     if (!(current_state inside {keymgr_pkg::StReset, keymgr_pkg::StInvalid}) &&
         get_operation() inside {keymgr_pkg::OpAdvance, keymgr_pkg::OpDisable}) begin
       current_cdi = get_adv_cdi_type();
@@ -257,7 +258,7 @@
       end
     join_none
 
-    if (is_final_kdf) process_error_n_alert();
+    if (is_final_kdf && cfg.keymgr_vif.get_keymgr_en()) process_error_n_alert();
     // IntrOpDone occurs after every KDF
     void'(ral.intr_state.predict(.value(1 << int'(IntrOpDone))));
 
@@ -267,25 +268,22 @@
 
         case (op)
           keymgr_pkg::OpAdvance: begin
-            if (get_fault_err()) begin
-              current_state = keymgr_pkg::StDisabled;
-              update_result = NotUpdate;
-            end else if (get_op_err()) begin
+            // if it's StOwnerKey, it advacens to OpDisable. Key is just random value
+            if (current_state == keymgr_pkg::StOwnerKey || get_fault_err()) begin
+              if (get_fault_err()) current_state = keymgr_pkg::StDisabled;
               update_result = NotUpdate;
             end else begin
-              // if it's StOwnerKey, it advacens to OpDisable. Key is just random value
-              if (current_state == keymgr_pkg::StOwnerKey) update_result = NotUpdate;
-              else                                         update_result = UpdateInternalKey;
+              update_result = UpdateInternalKey;
+            end
 
-              if (adv_cnt != keymgr_pkg::CDIs - 1) begin
-                adv_cnt++;
-              end else begin
-                adv_cnt = 0;
-                update_state(get_next_state(current_state));
-                // set sw_binding_regwen after advance OP
-                void'(ral.sw_binding_regwen.predict(.value(1)));
-                ral.sw_binding_regwen.en.set_lockable_flds_access(.lock(0));
-              end
+            if (adv_cnt != keymgr_pkg::CDIs - 1) begin
+              adv_cnt++;
+            end else begin
+              adv_cnt = 0;
+              update_state(get_next_state(current_state));
+              // set sw_binding_regwen after advance OP
+              void'(ral.sw_binding_regwen.predict(.value(1)));
+              ral.sw_binding_regwen.en.set_lockable_flds_access(.lock(0));
             end
           end
           keymgr_pkg::OpDisable: begin
@@ -467,7 +465,7 @@
                   current_op_status = keymgr_pkg::OpDoneFail;
                   // No KDF issued, done interrupt/alert is triggered in next cycle
                   void'(ral.intr_state.predict(.value(1 << int'(IntrOpDone))));
-                  fork
+                  if (cfg.keymgr_vif.get_keymgr_en()) fork
                     begin
                       cfg.clk_rst_vif.wait_clks(1);
                       process_error_n_alert();
@@ -601,7 +599,11 @@
     bit [TL_DW-1:0] err = get_err_code();
     void'(ral.err_code.predict(err));
 
-    if (get_fault_err()) set_exp_alert("fatal_fault_err", .is_fatal(1));
+    if (get_fault_err()) begin
+      set_exp_alert("fatal_fault_err", .is_fatal(1));
+      is_sw_share_corrupted = 1;
+      cfg.keymgr_vif.wipe_sideload_keys();
+    end
     if (get_op_err()) set_exp_alert("recov_operation_err");
 
     `uvm_info(`gfn, $sformatf("at %s, %s is issued and error code is 'b%0b",
@@ -621,25 +623,29 @@
   virtual function bit [TL_DW-1:0] get_err_code();
     bit [TL_DW-1:0] err_code;
 
+    // TODO, clean up later
     // if keymgr_en is off during an OP, suppress op err
-    if (cfg.keymgr_vif.get_keymgr_en() || current_state == keymgr_pkg::StInvalid) begin
+    //if (cfg.keymgr_vif.get_keymgr_en() && current_state != keymgr_pkg::StInvalid) begin
       err_code[keymgr_pkg::ErrInvalidOp]  = get_op_error();
-    end
+    //end
 
-    err_code[keymgr_pkg::ErrInvalidStates] = is_kmac_rsp_err | is_kmac_invalid_data;
+    // this fault error is sticky, should preserve the value until reset
+    if (!invalid_status_err) invalid_status_err = is_kmac_rsp_err | is_kmac_invalid_data;
+    err_code[keymgr_pkg::ErrInvalidStates] = invalid_status_err;
 
     if (cfg.keymgr_vif.keymgr_en_sync2 == lc_ctrl_pkg::On) begin
       err_code[keymgr_pkg::ErrInvalidIn]  = get_hw_invalid_input() | get_sw_invalid_input();
     end
 
-    `uvm_info(`gfn, $sformatf("op_err = %0d, rsp_err = %0d, kmac_invalid =%0d, hw_invalid = %0d \
-              sw_invalid = %0d, kmac_invalid_data = %0d",
-              get_op_error(), is_kmac_rsp_err, is_kmac_invalid_data, get_hw_invalid_input(),
-              get_sw_invalid_input(), is_kmac_invalid_data), UVM_MEDIUM)
+    `uvm_info(`gfn, $sformatf({"op_err = %0d, rsp_err = %0d, hw_invalid = %0d, sw_invalid = %0d, ",
+              "kmac_invalid_data = %0d"},
+              get_op_error(), is_kmac_rsp_err, get_hw_invalid_input(), get_sw_invalid_input(),
+              is_kmac_invalid_data), UVM_MEDIUM)
     return err_code;
   endfunction
 
   virtual function bit get_op_error();
+    `uvm_info(`gfn, $sformatf("current_state: %s", current_state), UVM_MEDIUM)
     case (current_state)
       keymgr_pkg::StReset: begin
         if (get_operation() != keymgr_pkg::OpAdvance) begin
@@ -973,13 +979,26 @@
       // design takes 2 cycle to update state
       update_state(keymgr_pkg::StInvalid, .cyc_dly(2));
       `uvm_info(`gfn, "Keymgr_en is Off, wipe secret and move state to Invalid", UVM_LOW)
+      cfg.keymgr_vif.wipe_sideload_keys();
     end
     fork
       begin
         // it takes 2 cycle to wipe sw_share. add one more negedge to avoid race condition
         cfg.clk_rst_vif.wait_n_clks(3);
-        update_state(keymgr_pkg::StInvalid);
+        if (current_op_status == keymgr_pkg::OpWip) begin
+          current_state = keymgr_pkg::StInvalid;
+          `uvm_info(`gfn, "operation WIP but Keymgr_en is Off, update err_code and move to Invalid",
+                    UVM_LOW)
+          process_error_n_alert();
+          current_op_status = keymgr_pkg::OpDoneFail;
+        end else begin
+          // corner case, keymgr_en is changed while OP is almost done. OP will finish successfully
+          // delay update_state in 1 cycle
+          update_state(keymgr_pkg::StInvalid);
+          `uvm_info(`gfn, "operation WIP but Keymgr_en is Off, move to Invalid", UVM_LOW)
+        end
         is_sw_share_corrupted = 1;
+        cfg.keymgr_vif.wipe_sideload_keys();
       end
     join_none
   endfunction
@@ -991,6 +1010,7 @@
     current_op_status     = keymgr_pkg::OpIdle;
     is_kmac_rsp_err       = 0;
     is_kmac_invalid_data  = 0;
+    invalid_status_err    = 0;
     is_sw_share_corrupted = 0;
     req_fifo.flush();
     rsp_fifo.flush();