[dv/kmac] cycle accurate model bugfixes - pt 5

this PR makes more fixes to the cycle accurate model, this time relating
to the EDN entropy interface - specifically, overhauling how we time the
first round of a keccak hash.

Some related fixes have also been made to the base sequence, with how we
calculate a "static" entropy mode that is then applied to each hash
iteration.

Signed-off-by: Udi Jonnalagadda <udij@google.com>
diff --git a/hw/ip/kmac/dv/env/kmac_scoreboard.sv b/hw/ip/kmac/dv/env/kmac_scoreboard.sv
index 7629d78..c8bcb05 100644
--- a/hw/ip/kmac/dv/env/kmac_scoreboard.sv
+++ b/hw/ip/kmac/dv/env/kmac_scoreboard.sv
@@ -225,6 +225,10 @@
         refresh_entropy = 0;
         `uvm_info(`gfn, "dropped refresh_entropy", UVM_HIGH)
       end
+      // after EDN request returns fresh entropy, it only becomes valid after 6 cycles:
+      // - 5 to expand the entropy
+      // - 1 to latch the entropy
+      cfg.clk_rst_vif.wait_clks(CYCLES_TO_FILL_ENTROPY + 1);
     end
   endtask
 
@@ -545,12 +549,8 @@
               cfg.clk_rst_vif.wait_clks(sha3_pkg::KeccakRate[strength]);
               `uvm_info(`gfn, "finished waiting for sha3pad process", UVM_HIGH)
 
-              // Keccak logic needs 1 cycle to latch internal control signal
-              // after sha3pad finishes transmitting prefix/key data blocks
-              cfg.clk_rst_vif.wait_clks(1);
-
               // wait for the keccak logic to perform KECCAK_NUM_ROUNDS rounds
-              wait_keccak_rounds((kmac_en && entropy_fast_process) ? (i == 1) : 0);
+              wait_keccak_rounds(.is_key_process((kmac_en && entropy_fast_process) ? (i == 1) : 0));
 
             end
             prefix_and_keys_done = 1;
@@ -605,27 +605,38 @@
   //
   // The logic in this task is relatively straightforward and implements the described behavior
   // in the timing model.
-  virtual task wait_keccak_rounds(bit is_key_process = 1'b0);
+  virtual task wait_keccak_rounds(bit is_key_process = 1'b0,
+                                  bit wait_for_run_latch = 1'b1);
     int unsigned cycles_first_round = 0;
     int unsigned cycles_per_round = 0;
+
+    // This bit is used to indicate that a full entropy expansion is necessary.
+    // A full entropy expansion will occur on the very first time that keccak rounds run after the
+    // design comes out of a reset, requiring that every round take the full 7 cycles (unless fast
+    // processing is enabled).
     bit full_entropy_expansion = 0;
 
+    // insert zero delay to ensure all entropy-related updates have settled
+    //
+    // this also helps catch an edge case where EDN returns valid entropy
+    // on the same timestep that this task starts on
+    #0;
+
     `uvm_info(`gfn, "entered wait_keccak_rounds", UVM_HIGH)
 
+    // Keccak logic needs 1 cycle to latch internal control signal
+    // after sha3pad finishes transmitting prefix/key data blocks
+    if (wait_for_run_latch) cfg.clk_rst_vif.wait_clks(1);
+
     if (cfg.enable_masking) begin
       // If masking is enabled then entropy is used,
       // timing is more complex because of the various entropy features
 
       if (entropy_mode inside {EntropyModeSw, EntropyModeEdn}) begin
 
-        // If using entropy from EDN, need to check whether the request is due to a normal
-        // entropy refresh after completed hash or whether the request is being sent immediately out
-        // of reset once KMAC starts operation.
-        //
-        // In this case, full expansion is necessary
+        // If using entropy from EDN, need to check whether the request is due to the request
+        // being sent immediately out of reset once KMAC starts operation.
         if (entropy_mode == EntropyModeEdn) begin
-          // zero delay to ensure all updates have settled
-          #0;
           if (in_edn_fetch && first_op_after_rst) begin
             full_entropy_expansion = 1;
           end
@@ -654,15 +665,43 @@
 
     `uvm_info(`gfn, "starting to wait for keccak", UVM_HIGH)
 
+    `uvm_info(`gfn, $sformatf("cycles_first_round: %0d", cycles_first_round), UVM_HIGH)
+    `uvm_info(`gfn, $sformatf("cycles_per_round: %0d", cycles_per_round), UVM_HIGH)
+
+    `uvm_info(`gfn, $sformatf("full_entropy_expansion: %0d", full_entropy_expansion), UVM_HIGH)
+
     for (int i = 0; i < KECCAK_NUM_ROUNDS; i++) begin
       if (i == 0) begin
         if (full_entropy_expansion) begin
           wait(in_edn_fetch == 0);
           cfg.clk_rst_vif.wait_clks(1 + ENTROPY_FULL_EXPANSION_CYCLES);
-        end else if (cycles_first_round != 0) begin
-          cfg.clk_rst_vif.wait_clks(cycles_first_round);
         end else begin
-          cfg.clk_rst_vif.wait_clks(cycles_per_round);
+          bit fresh_entropy_ready = 0;
+          // We need to be able to detect cases where fresh entropy is provided during the first
+          // keccak round, so we spawn a subprocess `wait_fresh_entropy` to raise a signal bit if
+          // this scenario is detected.
+          //
+          // If it is detected, we must wait for some extra cycles to allow the entropy to be
+          // readied internally.
+          fork
+            begin : wait_fresh_entropy
+              @(negedge in_edn_fetch);
+              fresh_entropy_ready = 1;
+              `uvm_info(`gfn, "raised fresh_entropy_ready", UVM_HIGH)
+            end : wait_fresh_entropy
+            begin : wait_first_keccak_round
+              if (cycles_first_round != 0) begin
+                cfg.clk_rst_vif.wait_clks(cycles_first_round);
+              end else begin
+                cfg.clk_rst_vif.wait_clks(cycles_per_round);
+              end
+              disable wait_fresh_entropy;
+            end : wait_first_keccak_round
+          join
+          if (fresh_entropy_ready) begin
+            // 2 cycles total
+            cfg.clk_rst_vif.wait_clks(2);
+          end
         end
       end else if (i == 1 && refresh_entropy) begin
         // If entropy is simply refreshed after the end of previous hashing operation,
@@ -953,9 +992,6 @@
                               do_increment = 0;
                             end
 
-                            // wait for 'run' signal to be latched
-                            cfg.clk_rst_vif.wait_clks(1);
-
                             wait_keccak_rounds();
                             num_blocks_filled = 0;
                             cfg.clk_rst_vif.wait_clks(1);
@@ -987,7 +1023,6 @@
                           do_increment = 0;
                           if (num_blocks_filled == sha3_pkg::KeccakRate[strength]) begin
                             `uvm_info(`gfn, "filled up blocks while processing full kmac_app_last block", UVM_HIGH)
-                            cfg.clk_rst_vif.wait_clks(1);
                             wait_keccak_rounds();
                             num_blocks_filled = 0;
                             // if need to run keccak rounds, fifo_rd_ptr increments one cycle later
@@ -1022,7 +1057,6 @@
                           if (num_blocks_filled == sha3_pkg::KeccakRate[strength]) begin
                             wait_kmac_app_flush = 0;
                             `uvm_info(`gfn, "filled up blocks while processing overflow kmac_app_last block", UVM_HIGH)
-                            cfg.clk_rst_vif.wait_clks(1);
                             wait_keccak_rounds();
                             num_blocks_filled = 0;
                             cfg.clk_rst_vif.wait_clks(2);
@@ -1046,9 +1080,6 @@
                           UVM_HIGH)
                       cfg.clk_rst_vif.wait_clks(sha3_pkg::KeccakRate[strength] - num_blocks_filled);
 
-                      // wait one more cycle for keccak to latch sha3pad control signal
-                      cfg.clk_rst_vif.wait_clks(1);
-
                       wait_keccak_rounds();
 
                       num_blocks_filled = 0;
@@ -1085,6 +1116,13 @@
                     // Unset do_increment to avoid infinitely incrementing it
                     cfg.clk_rst_vif.wait_n_clks(1);
                     do_increment = 0;
+
+                    // If we now have a full set of blocks, do not wait a cycle to start running
+                    // keccak, go straight to the next iteration of the loop and start running
+                    // immediately.
+                    if (num_blocks_filled == sha3_pkg::KeccakRate[strength]) begin
+                      continue;
+                    end
                   end
                 end else if (num_blocks_filled == sha3_pkg::KeccakRate[strength]) begin
                   // If we have filled up an entire set of blocks, we must immediately send it off
@@ -1132,7 +1170,8 @@
                         do_increment = 1;
                         cfg.clk_rst_vif.wait_n_clks(1);
                         do_increment = 0;
-                        cfg.clk_rst_vif.wait_clks(1);
+                        // TODO - this still might be required.
+                        //cfg.clk_rst_vif.wait_clks(1);
                       end
                       wait_keccak_rounds();
 
@@ -1171,8 +1210,6 @@
                       end
                       // Wait for sha3pad to transmit all blocks to the keccak logic
                       cfg.clk_rst_vif.wait_clks(sha3_pkg::KeccakRate[strength]);
-                      // Wait 1 more cycle for sha3pad control signal to be latched by keccak
-                      cfg.clk_rst_vif.wait_clks(1);
                       wait_keccak_rounds();
                       // signal that the initial hash round has been completed
                       `uvm_info(`gfn, "raising msg_digest_done", UVM_HIGH)
@@ -1233,7 +1270,7 @@
             msg_digest_done = 0;
             `uvm_info(`gfn, "dropping msg_digest_done", UVM_HIGH)
 
-            wait_keccak_rounds();
+            wait_keccak_rounds(.wait_for_run_latch(1'b0));
             msg_digest_done = 1;
             `uvm_info(`gfn, "raising msg_digest_done", UVM_HIGH)
           end
diff --git a/hw/ip/kmac/dv/env/seq_lib/kmac_app_vseq.sv b/hw/ip/kmac/dv/env/seq_lib/kmac_app_vseq.sv
index dc4a74a..9eb3849 100644
--- a/hw/ip/kmac/dv/env/seq_lib/kmac_app_vseq.sv
+++ b/hw/ip/kmac/dv/env/seq_lib/kmac_app_vseq.sv
@@ -16,7 +16,7 @@
 
   // msg size when using app interface must be non-zero
   constraint app_msg_size_c {
-    msg.size() > 0;
+    en_app -> msg.size() > 0;
   }
 
   constraint kmac_app_c {
diff --git a/hw/ip/kmac/dv/env/seq_lib/kmac_base_vseq.sv b/hw/ip/kmac/dv/env/seq_lib/kmac_base_vseq.sv
index ae8e622..4b4ccdb 100644
--- a/hw/ip/kmac/dv/env/seq_lib/kmac_base_vseq.sv
+++ b/hw/ip/kmac/dv/env/seq_lib/kmac_base_vseq.sv
@@ -58,7 +58,15 @@
   rand bit en_sideload;
 
   // entropy mode
-  rand kmac_pkg::entropy_mode_e entropy_mode;
+  rand kmac_pkg::entropy_mode_e entropy_mode = EntropyModeSw;
+  // We do not want to change the value of `entropy_mode` in between hashing iterations.
+  //
+  // So we use a static entropy mode value to hold the same mode through the whole test,
+  // and constrain `entropy_mode` accordingly.
+  //
+  // Structure it in this way, as timer verification will allow different entropy modes to be used
+  // in different iterations.
+  kmac_pkg::entropy_mode_e static_entropy_mode = EntropyModeSw;
 
   // entropy fast process mode
   rand bit entropy_fast_process;
@@ -142,6 +150,10 @@
     (en_sideload) -> key_len == Key256;
   }
 
+  constraint entropy_mode_c {
+    entropy_mode == static_entropy_mode;
+  }
+
   // Set the block size based on the random security strength.
   // This is only relevant for XOF functions (L128 or L256), but will be
   // set for all security strengths.
@@ -208,6 +220,12 @@
     // TODO
   endtask
 
+  virtual task pre_start();
+    super.pre_start();
+    `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(static_entropy_mode,
+        static_entropy_mode inside {EntropyModeSw, EntropyModeEdn};)
+  endtask
+
   // setup basic kmac features
   virtual task kmac_init();
     // Wait for KMAC to reach idle state
diff --git a/hw/ip/kmac/dv/env/seq_lib/kmac_common_vseq.sv b/hw/ip/kmac/dv/env/seq_lib/kmac_common_vseq.sv
index 7eb2a0c..9e6f4e1 100644
--- a/hw/ip/kmac/dv/env/seq_lib/kmac_common_vseq.sv
+++ b/hw/ip/kmac/dv/env/seq_lib/kmac_common_vseq.sv
@@ -12,6 +12,7 @@
 
   virtual task pre_start();
     do_kmac_init = 1'b0;
+    entropy_mode_c.constraint_mode(0);
     super.pre_start();
   endtask
 
diff --git a/hw/ip/kmac/dv/env/seq_lib/kmac_smoke_vseq.sv b/hw/ip/kmac/dv/env/seq_lib/kmac_smoke_vseq.sv
index 0a6cc58..6b7739b 100644
--- a/hw/ip/kmac/dv/env/seq_lib/kmac_smoke_vseq.sv
+++ b/hw/ip/kmac/dv/env/seq_lib/kmac_smoke_vseq.sv
@@ -78,7 +78,6 @@
   // a message hash.
   virtual task pre_start();
     do_kmac_init = 0;
-    entropy_mode.rand_mode(0);
     super.pre_start();
   endtask
 
@@ -90,8 +89,6 @@
 
     `uvm_info(`gfn, $sformatf("Starting %0d message hashes", num_trans), UVM_LOW)
 
-    `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(entropy_mode, entropy_mode inside {EntropyModeSw, EntropyModeEdn};)
-
     for (int i = 0; i < num_trans; i++) begin
       bit [7:0] share0[];
       bit [7:0] share1[];