[dv/pwrmgr] Add test with both wakeups and resets

Fix handling of some reset related random variables.
Add coverage sampling for reset in sleep state.

Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson b/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
index 39d3f7b..8193f82 100644
--- a/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
+++ b/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
@@ -190,14 +190,16 @@
             **Stimulus**:
             - Trigger reset and wakeup from low power as described for other
               testpoints.
-            - Make sure to test them coinciding also.
+            - Issue reset and wakeup a random number of cycles after the slow
+              state machine is in LowPower state.
+              - This also checks them coinciding.
 
             **Check**:
             - Similar tests as for the wakeup and reset testpoints, except
               making sure they happen per the triggering order.
               '''
       milestone: V2
-      tests: []
+      tests: ["pwrmgr_wakeup_reset"]
     }
     {
       name: lowpower_wakeup_race
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_env.core b/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
index 75dcf25..1dd1ccc 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
@@ -24,6 +24,7 @@
       - seq_lib/pwrmgr_lowpower_wakeup_race_vseq.sv: {is_include_file: true}
       - seq_lib/pwrmgr_reset_vseq.sv: {is_include_file: true}
       - seq_lib/pwrmgr_smoke_vseq.sv: {is_include_file: true}
+      - seq_lib/pwrmgr_wakeup_reset_vseq.sv: {is_include_file: true}
       - seq_lib/pwrmgr_wakeup_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
index fd7679c..dd38449 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
@@ -190,6 +190,7 @@
     pwr_cpu = pwrmgr_pkg::PWR_CPU_DEFAULT;
     wakeups_i = pwrmgr_pkg::WAKEUPS_DEFAULT;
     rstreqs_i = pwrmgr_pkg::RSTREQS_DEFAULT;
+    sw_rst_req_i = prim_mubi_pkg::MuBi4False;
     rom_ctrl = rom_ctrl_pkg::PWRMGR_DATA_DEFAULT;
     esc_rst_tx = prim_esc_pkg::ESC_TX_DEFAULT;
   end
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
index 0fc8189..eb2d377 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
@@ -87,6 +87,16 @@
               .sw_rst(cfg.pwrmgr_vif.sw_rst_req_i == prim_mubi_pkg::MuBi4True), .sleep(1'b0));
         end
       end
+    forever
+      @(posedge cfg.pwrmgr_vif.slow_state == pwrmgr_pkg::SlowPwrStateLowPower) begin
+        if (cfg.en_cov) begin
+          cov.reset_cg.sample(
+              .hw_resets(cfg.pwrmgr_vif.rstreqs_i), .hw_resets_en(cfg.pwrmgr_vif.reset_en),
+              .esc_rst(cfg.pwrmgr_vif.pwr_rst_req.rstreqs[pwrmgr_pkg::ResetEscIdx]),
+              .main_pwr_rst(cfg.pwrmgr_vif.pwr_rst_req.rstreqs[pwrmgr_pkg::ResetMainPwrIdx]),
+              .sw_rst(cfg.pwrmgr_vif.sw_rst_req_i == prim_mubi_pkg::MuBi4True), .sleep(1'b1));
+        end
+      end
   endtask
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name);
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_base_vseq.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_base_vseq.sv
index 0e1f1fe..a90c9f0 100644
--- a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_base_vseq.sv
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_base_vseq.sv
@@ -25,6 +25,15 @@
   rand wakeups_t         wakeups_en;
   rand resets_t          resets;
   rand resets_t          resets_en;
+  rand bit               power_glitch_reset;
+  rand bit               escalation_reset;
+
+  // TODO(maturana) Enable escalation resets once there is support for driving them.
+  constraint escalation_reset_c {escalation_reset == 1'b0;}
+  constraint resets_en_c {
+    solve resets, power_glitch_reset, escalation_reset before resets_en;
+    |{resets_en & resets, power_glitch_reset, escalation_reset} == 1'b1;
+  }
 
   rand bit               disable_wakeup_capture;
 
@@ -77,6 +86,9 @@
   }
   constraint cycles_before_main_pok_c {cycles_before_main_pok inside {[2 : MaxCyclesBeforeEnable]};}
 
+  // This is used to trigger a software reset, as per rstmgr's `reset_req` CSR.
+  prim_mubi_pkg::mubi4_t sw_rst_from_rstmgr = prim_mubi_pkg::MuBi4False;
+
   bit do_pwrmgr_init = 1'b1;
   // This static variable is incremented in each pre_start and decremented in each post_start.
   // It is used to start and stop the responders when the parent sequence starts and ends.
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_reset_vseq.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_reset_vseq.sv
index f3baa33..e140dc2 100644
--- a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_reset_vseq.sv
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_reset_vseq.sv
@@ -12,21 +12,9 @@
 
   `uvm_object_new
 
-  rand bit power_glitch_reset;
-  rand bit escalation_reset;
-
-  // TODO(maturana) Enable escalation resets once there is support for driving them.
-  constraint escalation_reset_c {escalation_reset == 1'b0;}
-  constraint resets_en_c {
-    solve resets, power_glitch_reset, escalation_reset before resets_en;
-    |{resets_en & resets, power_glitch_reset, escalation_reset} == 1'b1;
-  }
-
   constraint wakeups_c {wakeups == 0;}
   constraint wakeups_en_c {wakeups_en == 0;}
 
-  prim_mubi_pkg::mubi4_t sw_rst_from_rstmgr;
-
   function void post_randomize();
     sw_rst_from_rstmgr = get_rand_mubi4_val(8, 4, 4);
     super.post_randomize();
@@ -43,10 +31,11 @@
       `DV_CHECK_RANDOMIZE_FATAL(this)
       enabled_resets = resets_en & resets;
       `uvm_info(`gfn, $sformatf(
-                "Enabled resets=0x%x, power_reset=%b, escalation=%b",
+                "Enabled resets=0x%x, power_reset=%b, escalation=%b, sw_reset=%b",
                 enabled_resets,
                 power_glitch_reset,
-                escalation_reset
+                escalation_reset,
+                sw_rst_from_rstmgr
                 ), UVM_MEDIUM)
 
       csr_wr(.ptr(ral.reset_en[0]), .value(resets_en));
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_vseq_list.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_vseq_list.sv
index 3b7476a..28b4211 100644
--- a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_vseq_list.sv
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_vseq_list.sv
@@ -7,5 +7,6 @@
 `include "pwrmgr_lowpower_wakeup_race_vseq.sv"
 `include "pwrmgr_reset_vseq.sv"
 `include "pwrmgr_smoke_vseq.sv"
+`include "pwrmgr_wakeup_reset_vseq.sv"
 `include "pwrmgr_wakeup_vseq.sv"
 `include "pwrmgr_common_vseq.sv"
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_wakeup_reset_vseq.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_wakeup_reset_vseq.sv
new file mode 100644
index 0000000..81cf49f
--- /dev/null
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_wakeup_reset_vseq.sv
@@ -0,0 +1,112 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// The wakeup_reset test randomly enables wakeups and resets, info capture, and interrupts,
+// and sends wakeups and resets in close temporal proximity at random times.
+class pwrmgr_wakeup_reset_vseq extends pwrmgr_base_vseq;
+  `uvm_object_utils(pwrmgr_wakeup_reset_vseq)
+
+  `uvm_object_new
+
+  constraint wakeups_c {wakeups != 0;}
+
+  constraint wakeup_en_c {
+    solve wakeups before wakeups_en;
+    |(wakeups_en & wakeups) == 1'b1;
+  }
+  constraint disable_wakeup_capture_c {disable_wakeup_capture == 1'b0;}
+
+  task body();
+    logic [TL_DW-1:0] value;
+    resets_t enabled_resets;
+    wakeups_t enabled_wakeups;
+
+    cfg.slow_clk_rst_vif.wait_for_reset(.wait_negedge(0));
+    csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(0));
+    csr_rd_check(.ptr(ral.wake_status[0]), .compare_value(0));
+    for (int i = 0; i < num_trans; ++i) begin
+      `uvm_info(`gfn, "Starting new round", UVM_MEDIUM)
+      `DV_CHECK_RANDOMIZE_FATAL(this)
+      // Enable resets.
+      enabled_resets = resets_en & resets;
+      `uvm_info(`gfn, $sformatf(
+                "Enabled resets=0x%x, power_reset=%b, escalation=%b, sw_reset=%b",
+                enabled_resets,
+                power_glitch_reset,
+                escalation_reset,
+                sw_rst_from_rstmgr
+                ), UVM_MEDIUM)
+      csr_wr(.ptr(ral.reset_en[0]), .value(resets_en));
+
+      // Enable wakeups.
+      enabled_wakeups = wakeups_en & wakeups;
+      `DV_CHECK(enabled_wakeups, $sformatf(
+                "Some wakeup must be enabled: wkups=%b, wkup_en=%b", wakeups, wakeups_en))
+      `uvm_info(`gfn, $sformatf("Enabled wakeups=0x%x", enabled_wakeups), UVM_MEDIUM)
+      csr_wr(.ptr(ral.wakeup_en[0]), .value(wakeups_en));
+
+      clear_wake_info();
+
+      `uvm_info(`gfn, $sformatf("%0sabling wakeup capture", disable_wakeup_capture ? "Dis" : "En"),
+                UVM_MEDIUM)
+      csr_wr(.ptr(ral.wake_info_capture_dis), .value(disable_wakeup_capture));
+
+      update_control_enables(1'b1);
+
+      wait_for_csr_to_propagate_to_slow_domain();
+
+      // Initiate low power transition.
+      cfg.pwrmgr_vif.update_cpu_sleeping(1'b1);
+      set_nvms_idle();
+
+      // Wait for the slow state machine to be in low power.
+      wait (cfg.pwrmgr_vif.slow_state == pwrmgr_pkg::SlowPwrStateLowPower);
+
+      // This will send the wakeup and reset so they almost coincide.
+      fork
+        begin
+          cfg.clk_rst_vif.wait_clks(cycles_before_reset);
+          cfg.pwrmgr_vif.update_resets(resets);
+          cfg.pwrmgr_vif.update_sw_rst_req(sw_rst_from_rstmgr);
+        end
+        begin
+          cfg.clk_rst_vif.wait_clks(cycles_before_wakeup);
+          cfg.pwrmgr_vif.update_wakeups(wakeups);
+        end
+      join
+
+      // Check wake_status prior to wakeup, or the unit requesting wakeup will have been reset.
+      // This read will not work in the chip, since the processor will be asleep.
+      cfg.slow_clk_rst_vif.wait_clks(4);
+      csr_rd_check(.ptr(ral.wake_status[0]), .compare_value(enabled_wakeups),
+                   .err_msg("failed wake_status check"));
+      `uvm_info(`gfn, $sformatf("Got wake_status=0x%x", enabled_wakeups), UVM_MEDIUM)
+      csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(enabled_resets),
+                   .err_msg("failed reset_status check"));
+      wait(cfg.pwrmgr_vif.pwr_clk_req.main_ip_clk_en == 1'b1);
+
+      wait_for_fast_fsm_active();
+
+      csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(0),
+                   .err_msg("failed reset_status check"));
+
+      check_wake_info(.reasons(enabled_wakeups), .prior_reasons(1'b0), .fall_through(1'b0),
+                      .prior_fall_through(1'b0), .abort(1'b0),
+                      .prior_abort(1'b0));
+
+      // This is the expected side-effect of the low power entry reset, since the source of the
+      // non-aon wakeup sources will deassert it as a consequence of their reset.
+      // Some aon wakeups may remain active until software clears them. If they didn't, such wakeups
+      // will remain active, preventing the device from going to sleep.
+      cfg.pwrmgr_vif.update_wakeups('0);
+      cfg.slow_clk_rst_vif.wait_clks(10);
+      csr_rd_check(.ptr(ral.reset_status[0]), .compare_value('0));
+      csr_rd_check(.ptr(ral.wake_status[0]), .compare_value('0));
+
+      // Wait for interrupt to be generated whether or not it is enabled.
+      cfg.slow_clk_rst_vif.wait_clks(10);
+    end
+  endtask
+
+endclass : pwrmgr_wakeup_reset_vseq
diff --git a/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson b/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
index 6e27b50..b51d969 100644
--- a/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
+++ b/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
@@ -75,6 +75,11 @@
       run_opts: ["+test_timeout_ns=1000000"]
     }
     {
+      name: pwrmgr_wakeup_reset
+      uvm_test_seq: pwrmgr_wakeup_reset_vseq
+      run_opts: ["+test_timeout_ns=1000000"]
+    }
+    {
       name: pwrmgr_aborted_low_power
       uvm_test_seq: pwrmgr_aborted_low_power_vseq
     }