[keymgr/dv] Support sideload clear

In sequences, randomize `sideload_clear` after gen-hw-out
In checkers, check each sideload interface key not matching if it's
cleared

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/ip/keymgr/data/keymgr_testplan.hjson b/hw/ip/keymgr/data/keymgr_testplan.hjson
index f2bcbfe..435d573 100644
--- a/hw/ip/keymgr/data/keymgr_testplan.hjson
+++ b/hw/ip/keymgr/data/keymgr_testplan.hjson
@@ -85,7 +85,8 @@
             Verify the sideload data and status for correctness.
             '''
       milestone: V2
-      tests: ["keymgr_sideload"]
+      tests: ["keymgr_sideload", "keymgr_sideload_kmac",
+              "keymgr_sideload_aes", "keymgr_sideload_otbn"]
     }
     {
       name: init_n_start
diff --git a/hw/ip/keymgr/dv/env/keymgr_if.sv b/hw/ip/keymgr/dv/env/keymgr_if.sv
index 5b48fde..85675d2 100644
--- a/hw/ip/keymgr/dv/env/keymgr_if.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_if.sv
@@ -8,6 +8,19 @@
   import uvm_pkg::*;
   import keymgr_env_pkg::*;
 
+  // Represents the keymgr sideload state for each sideload interface.
+  //
+  // The initial status is SideLoadNotAvail. After the sideload key is generated, it becomes
+  // SideLoadAvail.
+  // Status can't be directly changed from SideLoadClear to SideLoadAvail.
+  // When status is SideLoadClear due to SIDELOAD_CLEAR programmed, need to write CSR to 0 to reset
+  // it so that status is changed to SideLoadNotAvail, then we may set it to SideLoadAvail again
+  typedef enum bit[1:0] {
+    SideLoadNotAvail,
+    SideLoadAvail,
+    SideLoadClear
+  } keymgr_sideload_status_e;
+
   lc_ctrl_pkg::lc_tx_t            keymgr_en;
   lc_ctrl_pkg::lc_keymgr_div_t    keymgr_div;
   otp_ctrl_pkg::otp_device_id_t   otp_device_id;
@@ -34,13 +47,19 @@
   lc_ctrl_pkg::lc_tx_t keymgr_en_sync1, keymgr_en_sync2;
 
   // indicate if check the key is same as expected or shouldn't match to any meaningful key
+  // when a good KDF is ongoing or kmac sideload key is available, this flag is set to 1
   bit is_kmac_key_good;
-  bit is_aes_key_good;
-  bit is_otbn_key_good;
 
-  // when kmac sideload key is generated, kmac may be used to do other OP, but once the OP is done,
-  // it should automatically switch back to sideload key
-  bit is_kmac_sideload_avail;
+  // sideload status
+  keymgr_sideload_status_e aes_sideload_status;
+  keymgr_sideload_status_e otbn_sideload_status;
+
+  // When kmac sideload key is generated, `kmac_key` becomes valid with the generated digest data.
+  // If SW requests keymgr to do another operation, kmac_key will be updated to the internal key
+  // to perform a KMAC KDF operation.
+  // Once the operation is done, `kmac_key` is expected to switch automatically to the previous KMAC
+  // sideload key.
+  keymgr_sideload_status_e kmac_sideload_status;
   keymgr_env_pkg::key_shares_t kmac_sideload_key_shares;
 
   // use `string` here is to combine both internal key and sideload keys, so it could be "internal"
@@ -81,13 +100,14 @@
 
   // reset local exp variables when reset is issued
   function automatic void reset();
+    keymgr_en = lc_ctrl_pkg::lc_tx_t'($urandom);
     kmac_key_exp = '0;
     aes_key_exp  = '0;
     otbn_key_exp = '0;
     is_kmac_key_good = 0;
-    is_aes_key_good  = 0;
-    is_otbn_key_good = 0;
-    is_kmac_sideload_avail = 0;
+    kmac_sideload_status = SideLoadNotAvail;
+    aes_sideload_status = SideLoadNotAvail;
+    otbn_sideload_status = SideLoadNotAvail;
 
     // edn related
     edn_interval  = 'h100;
@@ -176,7 +196,7 @@
                                          bit good_key = 1);
 
     kmac_key_exp <= '{1'b1, key_shares};
-    is_kmac_key_good = good_key;
+    is_kmac_key_good <= good_key;
   endfunction
 
   // store internal key once it's available and use to compare if future OP is invalid
@@ -198,19 +218,25 @@
                                                     key_shares[0][keymgr_pkg::KeyWidth-1:0]};
     case (dest)
       keymgr_pkg::Kmac: begin
-        kmac_key_exp             <= '{1'b1, trun_key_shares};
-        is_kmac_key_good         <= 1;
-        is_kmac_sideload_avail   <= 1;
-        kmac_sideload_key_shares <= trun_key_shares;
+        if (kmac_sideload_status != SideLoadClear) begin
+          kmac_sideload_status     <= SideLoadAvail;
+          kmac_key_exp             <= '{1'b1, trun_key_shares};
+          is_kmac_key_good         <= 1;
+          kmac_sideload_key_shares <= trun_key_shares;
+        end
       end
       keymgr_pkg::Aes: begin
-        aes_key_exp     <= '{1'b1, trun_key_shares};
-        is_aes_key_good <= 1;
+        if (aes_sideload_status != SideLoadClear) begin
+          aes_key_exp         <= '{1'b1, trun_key_shares};
+          aes_sideload_status <= SideLoadAvail;
+        end
       end
       keymgr_pkg::Otbn: begin
-        // only otbn uses full 384 bits digest data
-        otbn_key_exp     <= '{1'b1, key_shares};
-        is_otbn_key_good <= 1;
+        if (otbn_sideload_status != SideLoadClear) begin
+          // only otbn uses full 384 bits digest data
+          otbn_key_exp         <= '{1'b1, key_shares};
+          otbn_sideload_status <= SideLoadAvail;
+        end
       end
       default: `uvm_fatal("keymgr_if", $sformatf("Unexpect dest type %0s", dest.name))
     endcase
@@ -218,6 +244,46 @@
     keys_a_array[state][cdi_type][dest.name] = trun_key_shares;
   endfunction
 
+  function automatic void clear_sideload_key(bit[2:0] clear_dest);
+    // reset from Clear to NotAvail
+    if (kmac_sideload_status == SideLoadClear) kmac_sideload_status <= SideLoadNotAvail;
+    if (aes_sideload_status == SideLoadClear)  aes_sideload_status  <= SideLoadNotAvail;
+    if (otbn_sideload_status == SideLoadClear) otbn_sideload_status <= SideLoadNotAvail;
+    case (clear_dest)
+      keymgr_pkg::SideLoadClrIdle: ; // do nothing
+      keymgr_pkg::SideLoadClrAes, keymgr_pkg::SideLoadClrKmac, keymgr_pkg::SideLoadClrOtbn: begin
+        clear_one_sideload_key(clear_dest);
+      end
+      // clear all
+      default: begin
+        clear_one_sideload_key(keymgr_pkg::SideLoadClrAes);
+        clear_one_sideload_key(keymgr_pkg::SideLoadClrKmac);
+        clear_one_sideload_key(keymgr_pkg::SideLoadClrOtbn);
+      end
+    endcase
+  endfunction
+
+  function automatic void clear_one_sideload_key(keymgr_pkg::keymgr_sideload_clr_e clear_dest);
+    case (clear_dest)
+      keymgr_pkg::SideLoadClrAes: begin
+        aes_sideload_status <= SideLoadClear;
+        aes_key_exp.valid <= 0;
+      end
+      keymgr_pkg::SideLoadClrKmac: begin
+        is_kmac_key_good <= 0;
+        kmac_key_exp.valid <= 0;
+        kmac_sideload_status <= SideLoadClear;
+      end
+      keymgr_pkg::SideLoadClrOtbn: begin
+        otbn_sideload_status <= SideLoadClear;
+        otbn_key_exp.valid <= 0;
+      end
+      default: begin
+        `uvm_fatal(msg_id, $sformatf("Unexpected clear_dest %0d", clear_dest))
+      end
+    endcase
+  endfunction
+
   function automatic bit get_keymgr_en();
     return keymgr_en_sync2 === lc_ctrl_pkg::On;
   endfunction
@@ -270,7 +336,7 @@
     forever begin
       @(posedge clk);
       if (kmac_data_rsp.done) begin
-        if (is_kmac_sideload_avail) begin
+        if (kmac_sideload_status == SideLoadAvail) begin
           kmac_key_exp <= '{1'b1, kmac_sideload_key_shares};
           is_kmac_key_good <= 1;
         end else begin
@@ -284,52 +350,49 @@
   initial begin
     fork
       forever begin
-        @(posedge clk);
+        @(kmac_key or is_kmac_key_good);
+        #1ps; // avoid race condition
         if (!is_kmac_key_good) check_invalid_key(kmac_key, "KMAC");
       end
       forever begin
-        @(posedge clk);
-        if (!is_aes_key_good) check_invalid_key(aes_key, "AES");
+        @(aes_key or aes_sideload_status);
+        #1ps; // avoid race condition
+        if (aes_sideload_status != SideLoadAvail) check_invalid_key(aes_key, "AES");
+      end
+      forever begin
+        @(otbn_key or otbn_sideload_status);
+        #1ps; // avoid race condition
+        if (otbn_sideload_status != SideLoadAvail) check_invalid_key(otbn_key, "OTBN");
       end
     join
   end
 
-  initial begin
-    forever begin
-      @(posedge rst_n);
-      // This life cycle signal must be stable before
-      // the key manager comes out of reset.
-      // The power/reset manager ensures that
-      // this sequencing is correct.
-      keymgr_en = lc_ctrl_pkg::lc_tx_t'($urandom);
-    end
-  end
-
   function automatic void check_invalid_key(keymgr_pkg::hw_key_req_t act_key, string key_name);
     if (rst_n && act_key.valid && !is_cmd_err && !is_fsm_err) begin
       foreach (keys_a_array[state, cdi, dest]) begin
-        `DV_CHECK_NE({act_key.key[1], act_key.key[0]}, keys_a_array[state][cdi][dest],
+        `DV_CHECK_CASE_NE({act_key.key[1], act_key.key[0]}, keys_a_array[state][cdi][dest],
             $sformatf("%s key at state %s for %s %s", key_name, state.name, cdi.name, dest), ,
             msg_id)
       end
     end
   endfunction
 
-  `define KM_ASSERT(NAME, SEQ) \
+  // Create a macro to skip checking key values when LC is off or fault error occurs
+  `define ASSERT_IFF_KEYMGR_LEGAL(NAME, SEQ) \
     `ASSERT(NAME, SEQ, clk, !rst_n || keymgr_en_sync2 != lc_ctrl_pkg::On || is_cmd_err || \
            is_fsm_err)
 
-  // These asserts are commented out because the advance operation is now 2-pass and will require
-  // updated handling
-  `KM_ASSERT(CheckKmacKey, is_kmac_key_good && kmac_key_exp.valid -> kmac_key == kmac_key_exp)
-  `KM_ASSERT(CheckKmacKeyValid, kmac_key_exp.valid == kmac_key.valid)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckKmacKey, is_kmac_key_good && kmac_key_exp.valid ->
+                           kmac_key == kmac_key_exp)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckKmacKeyValid, kmac_key_exp.valid == kmac_key.valid)
 
-  // TODO update hmac and aes checker later
-  `KM_ASSERT(CheckAesKey, is_aes_key_good && aes_key_exp.valid -> aes_key == aes_key_exp)
-  `KM_ASSERT(CheckAesKeyValid, aes_key_exp.valid == aes_key.valid)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckAesKey, aes_sideload_status == SideLoadAvail && aes_key_exp.valid ->
+                           aes_key == aes_key_exp)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckAesKeyValid, aes_key_exp.valid == aes_key.valid)
 
-  `KM_ASSERT(CheckOtbnKey, is_otbn_key_good && otbn_key_exp.valid -> otbn_key == otbn_key_exp)
-  `KM_ASSERT(CheckOtbnKeyValid, otbn_key_exp.valid == otbn_key.valid)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckOtbnKey, otbn_sideload_status == SideLoadAvail && otbn_key_exp.valid
+                           -> otbn_key == otbn_key_exp)
+  `ASSERT_IFF_KEYMGR_LEGAL(CheckOtbnKeyValid, otbn_key_exp.valid == otbn_key.valid)
 
   // for EDN assertion
   // sync req/ack to core clk domain
@@ -390,5 +453,5 @@
   `ASSERT(CheckEdn2ndReq, $rose(edn_req_sync) && edn_req_cnt == 1 |-> edn_wait_cnt < 20,
           clk, !rst_n)
 
-  `undef KM_ASSERT
+  `undef ASSERT_IFF_KEYMGR_LEGAL
 endinterface
diff --git a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
index d02ffb9..5840b85 100644
--- a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
@@ -547,8 +547,17 @@
         end
       end
       "reseed_interval_shadowed": begin
-        if (addr_phase_write)
-          cfg.keymgr_vif.edn_interval = `gmv(ral.reseed_interval_shadowed.val);
+        if (addr_phase_write) cfg.keymgr_vif.edn_interval = `gmv(ral.reseed_interval_shadowed.val);
+      end
+      "sideload_clear": begin
+        if (addr_phase_write) begin
+          fork
+            begin
+              cfg.clk_rst_vif.wait_clks(1);
+              cfg.keymgr_vif.clear_sideload_key(`gmv(ral.sideload_clear.val));
+            end
+          join_none
+        end
       end
       default: begin
         if (!uvm_re_match("sw_share*", csr.get_name())) begin // sw_share
diff --git a/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_one_intf_vseq.sv b/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_one_intf_vseq.sv
index 9afcbff..a06739f 100644
--- a/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_one_intf_vseq.sv
+++ b/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_one_intf_vseq.sv
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 // Enable OpGenHwOut to test sideload key to a selected interface
-class keymgr_sideload_one_intf_vseq extends keymgr_smoke_vseq;
+class keymgr_sideload_one_intf_vseq extends keymgr_sideload_vseq;
   `uvm_object_utils(keymgr_sideload_one_intf_vseq)
   `uvm_object_new
 
@@ -12,9 +12,15 @@
   constraint gen_operation_c {
     gen_operation == keymgr_pkg::OpGenHwOut;
   }
+
   constraint key_dest_c {
     key_dest == sideload_dest;
   }
+
+  constraint do_clear_sideload_c {
+   do_clear_sideload == 1;
+  }
+
   function void pre_randomize();
     `DV_GET_ENUM_PLUSARG(keymgr_pkg::keymgr_key_dest_e, sideload_dest, 1)
     super.pre_randomize();
diff --git a/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_vseq.sv b/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_vseq.sv
index ccbe80e..6549c03 100644
--- a/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_vseq.sv
+++ b/hw/ip/keymgr/dv/env/seq_lib/keymgr_sideload_vseq.sv
@@ -7,8 +7,32 @@
   `uvm_object_utils(keymgr_sideload_vseq)
   `uvm_object_new
 
+  rand bit do_clear_sideload;
+  rand bit [2:0] clear_dest;
+
   // also test HW sideload
   constraint gen_operation_c {
     gen_operation inside {keymgr_pkg::OpGenId, keymgr_pkg::OpGenSwOut, keymgr_pkg::OpGenHwOut};
   }
+
+  constraint clear_dest_c {
+    clear_dest dist {[0:3] :/ 4,
+                     // reserved value, clear all sideload
+                     [4:$] :/ 1};
+  }
+
+  virtual task keymgr_operations(bit advance_state = $urandom_range(0, 1),
+                                 int num_gen_op    = $urandom_range(1, 4),
+                                 bit clr_output    = $urandom_range(0, 1),
+                                 bit wait_done     = 1);
+    super.keymgr_operations(advance_state, num_gen_op, clr_output, wait_done);
+
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(do_clear_sideload)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(clear_dest)
+    if (do_clear_sideload) begin
+      `uvm_info(`gfn, $sformatf("Clear sideload with value %0d", clear_dest), UVM_MEDIUM)
+      csr_wr(.ptr(ral.sideload_clear), .value(clear_dest));
+    end
+  endtask : keymgr_operations
+
 endclass : keymgr_sideload_vseq