[csrng/dv] Extend `csrng_cmds` test to randomly disable and re-enable CSRNG

Signed-off-by: Andreas Kurth <adk@lowrisc.org>
diff --git a/hw/ip/csrng/dv/env/csrng_env_cfg.sv b/hw/ip/csrng/dv/env/csrng_env_cfg.sv
index cd9983c..06da191 100644
--- a/hw/ip/csrng/dv/env/csrng_env_cfg.sv
+++ b/hw/ip/csrng/dv/env/csrng_env_cfg.sv
@@ -27,7 +27,14 @@
   // Knobs & Weights
   uint otp_en_cs_sw_app_read_pct, otp_en_cs_sw_app_read_inval_pct, lc_hw_debug_en_pct, regwen_pct,
        enable_pct, sw_app_enable_pct, read_int_state_pct, force_state_pct, check_int_state_pct,
-       num_cmds_min, num_cmds_max, aes_halt_pct, min_aes_halt_clks, max_aes_halt_clks;
+       num_cmds_min, num_cmds_max, aes_halt_pct, min_aes_halt_clks, max_aes_halt_clks,
+       min_num_disable_enable, max_num_disable_enable,
+       min_enable_clks, max_enable_clks,
+       min_disable_edn_before_csrng_clks, max_disable_edn_before_csrng_clks,
+       min_disable_csrng_before_entropy_src_clks, max_disable_csrng_before_entropy_src_clks,
+       min_disable_clks, max_disable_clks,
+       min_enable_entropy_src_before_csrng_clks, max_enable_entropy_src_before_csrng_clks,
+       min_enable_csrng_before_edn_clks, max_enable_csrng_before_edn_clks;
 
   bit    use_invalid_mubi;
 
@@ -116,6 +123,69 @@
       [fifo_write_err : fifo_state_err_test] := 1
   };}
 
+  // Number of times CSRNG gets disabled and re-enabled
+  rand uint num_disable_enable;
+  constraint num_disable_enable_c {
+    num_disable_enable >= min_num_disable_enable;
+    num_disable_enable <= max_num_disable_enable;
+  }
+
+  // In tests that disable CSRNG, how many cycles to keep all agents and CSRNG enabled
+  rand uint enable_clks;
+  constraint edn_enable_clks_c {
+    enable_clks >= min_enable_clks;
+    enable_clks <= max_enable_clks;
+  }
+
+  // In tests that disable CSRNG, how many cycles to wait to disable CSRNG after EDN agents have
+  // been disabled
+  rand uint disable_edn_before_csrng_clks;
+  constraint disable_edn_before_csrng_clks_c {
+    disable_edn_before_csrng_clks >= min_disable_edn_before_csrng_clks;
+    disable_edn_before_csrng_clks <= max_disable_edn_before_csrng_clks;
+  }
+
+  // In tests that disable CSRNG, how many cycles to wait to disable entropy_src after CSRNG has
+  // been disabled
+  rand uint disable_csrng_before_entropy_src_clks;
+  constraint disable_csrng_before_entropy_src_clks_c {
+    disable_csrng_before_entropy_src_clks >= min_disable_csrng_before_entropy_src_clks;
+    disable_csrng_before_entropy_src_clks <= max_disable_csrng_before_entropy_src_clks;
+  }
+
+  // In tests that disable CSRNG, how many cycles to keep all agents and CSRNG disabled
+  rand uint disable_clks;
+  constraint disable_clks_c {
+    disable_clks >= min_disable_clks;
+    disable_clks <= max_disable_clks;
+  }
+
+  // In tests that disable CSRNG, how many cycles to enable the entropy_src agent in advance of
+  // CSRNG
+  rand uint enable_entropy_src_before_csrng_clks;
+  constraint enable_entropy_src_before_csrng_clks_c {
+    enable_entropy_src_before_csrng_clks >= min_enable_entropy_src_before_csrng_clks;
+    enable_entropy_src_before_csrng_clks <= max_enable_entropy_src_before_csrng_clks;
+  }
+
+  // In tests that disable CSRNG, how many cycles to enable CSRNG in advance of enabling EDN agents
+  rand uint enable_csrng_before_edn_clks;
+  constraint enable_csrng_before_edn_clks_c {
+    enable_csrng_before_edn_clks >= min_enable_csrng_before_edn_clks;
+    enable_csrng_before_edn_clks <= max_enable_csrng_before_edn_clks;
+  }
+
+  // Re-randomize enable and disable delays.  This is intended to be called between iterations in
+  // tests that disable and re-enable CSRNG (and the agents).
+  function automatic void randomize_disable_enable_clks();
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(enable_clks)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(disable_edn_before_csrng_clks)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(disable_csrng_before_entropy_src_clks)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(disable_clks)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(enable_entropy_src_before_csrng_clks)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(enable_csrng_before_edn_clks)
+  endfunction
+
   // Functions
   function void post_randomize();
     if (use_invalid_mubi) begin
diff --git a/hw/ip/csrng/dv/env/seq_lib/csrng_cmds_vseq.sv b/hw/ip/csrng/dv/env/seq_lib/csrng_cmds_vseq.sv
index a715a81..b01d5b0 100644
--- a/hw/ip/csrng/dv/env/seq_lib/csrng_cmds_vseq.sv
+++ b/hw/ip/csrng/dv/env/seq_lib/csrng_cmds_vseq.sv
@@ -82,6 +82,20 @@
     end
   endfunction
 
+  virtual task csrng_set_enable(bit enable);
+    mubi4_t mubi_en = prim_mubi_pkg::mubi4_bool_to_mubi(enable);
+
+    // Disabling CSRNG may drop an ongoing req to entropy_src.  This is generally not allowed in
+    // push_pull_if, but in this case it is allowed because entropy_src is to be disabled and
+    // re-enabled shortly after.
+    `DV_ASSERT_CTRL_REQ("EntropySrcIf_ReqHighUntilAck_A_CTRL", enable)
+
+    // While CSRNG is disabled, entropy_src may ack a previous req that is no longer high.
+    `DV_ASSERT_CTRL_REQ("EntropySrcIf_AckAssertedOnlyWhenReqAsserted_A_CTRL", enable)
+
+    csr_wr(.ptr(ral.ctrl.enable), .value(mubi_en), .blocking(1));
+  endtask
+
   task body();
     super.body();
 
@@ -106,33 +120,88 @@
 
     // Send commands
     fork
-      for (int i = 0; i < NUM_HW_APPS + 1; i++) begin
-        automatic int j = i;
-        fork
-          begin
-            foreach (cs_item_q[j][k]) begin
-              send_cmd_req(.app(j), .cs_item(cs_item_q[j][k]));
+      fork
+        for (int i = 0; i < NUM_HW_APPS + 1; i++) begin
+          automatic int j = i;
+          fork
+            forever begin
+              automatic csrng_item item;
+              if (cs_item_q[j].size() == 0) begin
+                cfg.clk_rst_vif.wait_clks(1);
+                continue;
+              end
+              item = cs_item_q[j].pop_front();
+              send_cmd_req(.app(j), .cs_item(item));
               cmds_sent += 1;
             end
-          end
-        join_none;
-      end
+          join_none;
+        end
 
-      do begin
-        `uvm_info(`gfn, $sformatf("aes_halt_clks = %0d, cmds_sent = %0d, cmds_gen = %0d",
-                  aes_halt_clks, cmds_sent, cmds_gen), UVM_DEBUG)
-        if (cfg.aes_halt) begin
-          `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(aes_halt_clks, aes_halt_clks inside
-              { [cfg.min_aes_halt_clks:cfg.max_aes_halt_clks] };)
-          cfg.clk_rst_vif.wait_clks(aes_halt_clks);
-          m_aes_halt_pull_seq.start(p_sequencer.aes_halt_sequencer_h);
+        forever begin
+          `uvm_info(`gfn, $sformatf("aes_halt_clks = %0d, cmds_sent = %0d, cmds_gen = %0d",
+                    aes_halt_clks, cmds_sent, cmds_gen), UVM_DEBUG)
+          if (cfg.aes_halt) begin
+            `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(aes_halt_clks, aes_halt_clks inside
+                { [cfg.min_aes_halt_clks:cfg.max_aes_halt_clks] };)
+            cfg.clk_rst_vif.wait_clks(aes_halt_clks);
+            m_aes_halt_pull_seq.start(p_sequencer.aes_halt_sequencer_h);
+          end
+          else begin
+            cfg.clk_rst_vif.wait_clks(500);
+          end
         end
-        else begin
-          cfg.clk_rst_vif.wait_clks(500);
+      join
+
+      begin
+        // Disable and re-enable CSRNG (as well as EDN and entropy_src agents) at random instants.
+        while (cfg.num_disable_enable > 0) begin
+          cfg.clk_rst_vif.wait_clks(cfg.enable_clks);
+
+          `uvm_info(`gfn, "Disabling EDN.", UVM_LOW)
+          cfg.csrng_agents_vif.drive_edn_disable(1);
+
+          // Clear any items that may remain in the command queues.
+          for (int i = 0; i < NUM_HW_APPS + 1; i++) begin
+            cs_item_q[i].delete();
+          end
+
+          cfg.clk_rst_vif.wait_clks(cfg.disable_edn_before_csrng_clks);
+
+          `uvm_info(`gfn, "Disabling CSRNG.", UVM_LOW)
+          csrng_set_enable(0);
+
+          cfg.clk_rst_vif.wait_clks(cfg.disable_csrng_before_entropy_src_clks);
+
+          `uvm_info(`gfn, "Disabling entropy_src.", UVM_LOW)
+          cfg.csrng_agents_vif.drive_entropy_src_disable(1);
+
+          cfg.clk_rst_vif.wait_clks(cfg.disable_clks);
+
+          `uvm_info(`gfn, "Re-enabling entropy_src.", UVM_LOW)
+          cfg.csrng_agents_vif.drive_entropy_src_disable(0);
+
+          cfg.clk_rst_vif.wait_clks(cfg.enable_entropy_src_before_csrng_clks);
+
+          `uvm_info(`gfn, "Re-enabling CSRNG.", UVM_LOW)
+          csrng_set_enable(1);
+
+          cfg.clk_rst_vif.wait_clks(cfg.enable_csrng_before_edn_clks);
+
+          // Refill command queues.
+          create_cmds_all_apps();
+          print_cmds_all_apps();
+
+          `uvm_info(`gfn, "Re-enabling EDN.", UVM_LOW)
+          cfg.csrng_agents_vif.drive_edn_disable(0);
+
+          cfg.randomize_disable_enable_clks();
+          cfg.num_disable_enable -= 1;
         end
+        `DV_WAIT(cmds_sent == cmds_gen)
+        `uvm_info(`gfn, "All commands sent, completing test.", UVM_LOW)
       end
-      while (cmds_sent < cmds_gen);
-    join
+    join_any
+    disable fork;
 
     // Check internal state, then uninstantiate if not already
     if (cfg.check_int_state) begin
diff --git a/hw/ip/csrng/dv/tb.sv b/hw/ip/csrng/dv/tb.sv
index 23ecad4..1f0020a 100644
--- a/hw/ip/csrng/dv/tb.sv
+++ b/hw/ip/csrng/dv/tb.sv
@@ -162,4 +162,9 @@
           [`CTR_DRBG_UPD.BlkEncAckFifoWidth-1 -: `CTR_DRBG_UPD.BlkLen]) !=
       $past(`BLOCK_ENCRYPT_PATH.cipher_data_out, 2), clk, !rst_n)
 
+  // Assertion controls
+  `DV_ASSERT_CTRL("EntropySrcIf_ReqHighUntilAck_A_CTRL", entropy_src_if.ReqHighUntilAck_A)
+  `DV_ASSERT_CTRL("EntropySrcIf_AckAssertedOnlyWhenReqAsserted_A_CTRL",
+                  entropy_src_if.AckAssertedOnlyWhenReqAsserted_A)
+
 endmodule
diff --git a/hw/ip/csrng/dv/tests/csrng_cmds_test.sv b/hw/ip/csrng/dv/tests/csrng_cmds_test.sv
index 3d271aa..22c5c7d 100644
--- a/hw/ip/csrng/dv/tests/csrng_cmds_test.sv
+++ b/hw/ip/csrng/dv/tests/csrng_cmds_test.sv
@@ -16,6 +16,20 @@
     cfg.min_aes_halt_clks = 400;
     cfg.max_aes_halt_clks = 600;
     cfg.force_state_pct   = 100;
+    cfg.min_num_disable_enable = 0;
+    cfg.max_num_disable_enable = 10;
+    cfg.min_enable_clks = 1;
+    cfg.max_enable_clks = 10000;
+    cfg.min_disable_edn_before_csrng_clks = 10;
+    cfg.max_disable_edn_before_csrng_clks = 200;
+    cfg.min_disable_csrng_before_entropy_src_clks = 10;
+    cfg.max_disable_csrng_before_entropy_src_clks = 200;
+    cfg.min_disable_clks = 20;
+    cfg.max_disable_clks = 200;
+    cfg.min_enable_entropy_src_before_csrng_clks = 10;
+    cfg.max_enable_entropy_src_before_csrng_clks = 200;
+    cfg.min_enable_csrng_before_edn_clks = 10;
+    cfg.max_enable_csrng_before_edn_clks = 200;
 
     for (int i = 0; i < NUM_HW_APPS; i++) begin
       // CSRNG has a single AES primitive shared among the three application interfaces. To hit the