[dv/pwrmgr] Add lowpower wakeup race test

Add ignore_bins directive for no reset case.
Add sw_rst_req_i to interface in order to trigger sw resets.
Enhance reset_cg for sw_rst and improve crosses.
Fix sampling of wakeup_status in interface.

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 c2d04d7..39d3f7b 100644
--- a/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
+++ b/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
@@ -219,7 +219,7 @@
             - Either pwrmgr remains active or a full low power cycle occurs.
             '''
       milestone: V2
-      tests: []
+      tests: ["pwrmgr_lowpower_wakeup_race"]
     }
     {
       name: stress
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_env.core b/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
index 5cf9ae7..75dcf25 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_env.core
@@ -21,6 +21,7 @@
       - seq_lib/pwrmgr_base_vseq.sv: {is_include_file: true}
       - seq_lib/pwrmgr_aborted_low_power_vseq.sv: {is_include_file: true}
       - seq_lib/pwrmgr_common_vseq.sv: {is_include_file: true}
+      - 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_vseq.sv: {is_include_file: true}
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
index 7a20e3f..068d845 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
@@ -87,12 +87,25 @@
     control_cross: cross core_cp, io_cp, usb_lp_cp, usb_active_cp, main_pd_n_cp, sleep_cp;
   endgroup
 
-  covergroup reset_cg with function sample (resets_out_t resets_out, resets_t resets_en, bit sleep);
-    resets_out_cp: coverpoint resets_out;
-    resets_en_cp: coverpoint resets_en;
+  covergroup reset_cg with function sample (
+      resets_t hw_resets,
+      resets_t hw_resets_en,
+      logic sw_rst,
+      logic main_pwr_rst,
+      logic esc_rst,
+      bit sleep
+  );
+    hw_resets_cp: coverpoint hw_resets;
+    sw_rst_cp: coverpoint sw_rst;
+    main_pwr_rst_cp: coverpoint main_pwr_rst;
+    esc_rst_cp: coverpoint esc_rst;
+    hw_resets_en_cp: coverpoint hw_resets_en;
     sleep_cp: coverpoint sleep;
 
-    resets_cross: cross resets_out_cp, resets_en_cp, sleep_cp;
+    hw_resets_cross: cross hw_resets_cp, hw_resets_en_cp, sleep_cp;
+    esc_rst_cross: cross esc_rst_cp, sleep_cp;
+    main_pwr_rst_cross: cross main_pwr_rst_cp, sleep_cp;
+    sw_rst_cross: cross sw_rst_cp, sleep_cp;
   endgroup
 
   function new(string name, uvm_component parent);
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
index 0dc20af..fd7679c 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
@@ -46,6 +46,8 @@
   logic                                                              low_power;
   rom_ctrl_pkg::pwrmgr_data_t                                        rom_ctrl;
 
+  prim_mubi_pkg::mubi4_t                                             sw_rst_req_i;
+
   prim_esc_pkg::esc_tx_t                                             esc_rst_tx;
   prim_esc_pkg::esc_rx_t                                             esc_rst_rx;
 
@@ -78,9 +80,8 @@
   always_comb fast_state = `PATH_TO_DUT.u_fsm.state_q;
 
   // Wakeup_status ro CSR.
-  logic [pwrmgr_reg_pkg::NumWkups-1:0] wake_status;
   always_comb
-    wake_status = {
+    wakeup_status = {
       `PATH_TO_DUT.hw2reg.wake_status[4].d,
       `PATH_TO_DUT.hw2reg.wake_status[3].d,
       `PATH_TO_DUT.hw2reg.wake_status[2].d,
@@ -151,6 +152,10 @@
     reset_en = reset_en_value;
   endfunction
 
+  function automatic void update_sw_rst_req(prim_mubi_pkg::mubi4_t value);
+    sw_rst_req_i = value;
+  endfunction
+
   // Sends a main power glitch and disables a design assertion that trips for power glitches.
   task automatic glitch_power_reset();
     rst_main_n = 1'b0;
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
index fc297cd..0fc8189 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
@@ -40,6 +40,8 @@
     forever
       @(posedge cfg.pwrmgr_vif.wakeups_i) begin
         if (cfg.en_cov) begin
+          // Allow for synchronization delay.
+          cfg.slow_clk_rst_vif.wait_clks(2);
           foreach (cov.wakeup_ctrl_cg_wrap[i]) begin
             cov.wakeup_ctrl_cg_wrap[i].sample(
                 cfg.pwrmgr_vif.wakeup_en[i], cfg.pwrmgr_vif.wakeup_capture_en,
@@ -78,7 +80,11 @@
     forever
       @(posedge cfg.pwrmgr_vif.pwr_rst_req.reset_cause == pwrmgr_pkg::HwReq) begin
         if (cfg.en_cov) begin
-          cov.reset_cg.sample(cfg.pwrmgr_vif.pwr_rst_req.rstreqs, cfg.pwrmgr_vif.reset_en, 1'b0);
+          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'b0));
         end
       end
   endtask
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 bd8bbff..0e1f1fe 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
@@ -275,6 +275,7 @@
           // peripherals are reset they should drop their reset requests.
           if (cfg.pwrmgr_vif.fast_cb.pwr_rst_req.rst_lc_req[1] == 1'b0) begin
             cfg.pwrmgr_vif.update_resets('0);
+            cfg.pwrmgr_vif.update_sw_rst_req(prim_mubi_pkg::MuBi4False);
             `uvm_info(`gfn, "Clearing resets", UVM_MEDIUM)
           end
           drop_objection("rst_lc_src_n");
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_lowpower_wakeup_race_vseq.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_lowpower_wakeup_race_vseq.sv
new file mode 100644
index 0000000..5f37b78
--- /dev/null
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_lowpower_wakeup_race_vseq.sv
@@ -0,0 +1,125 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// The lowpower_wakeup race test randomly enables wakeups, info capture, and interrupts,
+// and sends wakeups in the temporal vecinity of low power entry. It also sends wakeups
+// after wakeup processing starts.
+class pwrmgr_lowpower_wakeup_race_vseq extends pwrmgr_base_vseq;
+  `uvm_object_utils(pwrmgr_lowpower_wakeup_race_vseq)
+
+  `uvm_object_new
+
+  constraint wakeups_c {wakeups != 0;}
+
+  rand bit keep_prior_wake_info;
+
+  constraint wakeup_en_c {
+    solve wakeups before wakeups_en;
+    |(wakeups_en & wakeups) == 1'b1;
+  }
+
+  rand int cycles_before_early_wakeup;
+  rand int cycles_before_transition;
+  constraint cycles_racing_c {
+    cycles_before_early_wakeup inside {[2 : 8]};
+    cycles_before_transition inside {[2 : 8]};
+  }
+
+  task body();
+    logic [TL_DW-1:0] value;
+    wakeups_t prior_reasons = '0;
+    bit prior_fall_through = '0;
+    bit prior_abort = '0;
+
+    cfg.slow_clk_rst_vif.wait_for_reset(.wait_negedge(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)
+      csr_wr(.ptr(ral.wakeup_en[0]), .value(wakeups_en));
+      `uvm_info(`gfn, $sformatf("Enabled wakeups=0x%x", wakeups_en & wakeups), UVM_MEDIUM)
+
+      if (keep_prior_wake_info) begin
+        csr_rd(.ptr(ral.wake_info.reasons), .value(prior_reasons));
+        csr_rd(.ptr(ral.wake_info.fall_through), .value(prior_fall_through));
+        csr_rd(.ptr(ral.wake_info.abort), .value(prior_abort));
+      end else begin
+        clear_wake_info();
+        prior_reasons = '0;
+        prior_fall_through = '0;
+        prior_abort = '0;
+      end
+      `uvm_info(`gfn, $sformatf(
+                "Prior wake_info: reasons=0x%x, fall_through=%b, abort=%b",
+                prior_reasons,
+                prior_fall_through,
+                prior_abort
+                ), UVM_MEDIUM)
+
+      `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();
+      set_nvms_idle();
+
+      // This will send the wakeup and trigger low power entry so they almost coincide.
+      fork
+        begin
+          cfg.clk_rst_vif.wait_clks(cycles_before_transition);
+          // Initiate low power transition.
+          cfg.pwrmgr_vif.update_cpu_sleeping(1'b1);
+        end
+        begin
+          cfg.clk_rst_vif.wait_clks(cycles_before_early_wakeup);
+          // Send the wakeups.
+          cfg.pwrmgr_vif.update_wakeups(wakeups);
+        end
+      join
+
+      if (ral.control.main_pd_n.get_mirrored_value() == 1'b0) begin
+        wait_for_reset_cause(pwrmgr_pkg::LowPwrEntry);
+      end
+
+      // Now bring it back.
+      cfg.clk_rst_vif.wait_clks(cycles_before_wakeup);
+
+     // 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(wakeups & wakeups_en),
+                   .err_msg("failed wake_status check"));
+      `uvm_info(`gfn, $sformatf("Got wake_status=0x%x", wakeups & wakeups_en), UVM_MEDIUM)
+      wait(cfg.pwrmgr_vif.pwr_clk_req.main_ip_clk_en == 1'b1);
+
+      // Send more wakeups to make sure they are reported in CSRs. With this all enabled
+      // wakeups should be registered.
+      cfg.pwrmgr_vif.update_wakeups('1);
+
+      wait_for_fast_fsm_active();
+      `uvm_info(`gfn, "Back from wakeup", UVM_MEDIUM)
+
+      csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(0),
+                   .err_msg("failed reset_status check"));
+
+      check_wake_info(.reasons(wakeups_en), .prior_reasons(prior_reasons), .fall_through(1'b0),
+                      .prior_fall_through(prior_fall_through), .abort(1'b0),
+                      .prior_abort(prior_abort));
+
+      // 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.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_lowpower_wakeup_race_vseq
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 25af53e..cb36bc5 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
@@ -4,12 +4,17 @@
 
 // The reset test randomly introduces external resets, power glitches, and escalation resets.
 class pwrmgr_reset_vseq extends pwrmgr_base_vseq;
+  import prim_mubi_pkg::mubi4_t;
+  import prim_mubi_pkg::MuBi4False;
+  import prim_mubi_pkg::MuBi4True;
+
   `uvm_object_utils(pwrmgr_reset_vseq)
 
   `uvm_object_new
 
   rand bit power_glitch_reset;
   rand bit escalation_reset;
+  rand prim_mubi_pkg::mubi4_t sw_rst_from_rstmgr;
 
   // TODO(maturana) Enable escalation resets once there is support for driving them.
   constraint escalation_reset_c {escalation_reset == 1'b0;}
@@ -21,6 +26,11 @@
   constraint wakeups_c {wakeups == 0;}
   constraint wakeups_en_c {wakeups_en == 0;}
 
+  function void post_randomize();
+    sw_rst_from_rstmgr = get_rand_mubi4_val(8, 4, 4);
+    super.post_randomize();
+  endfunction
+
   task body();
     logic [TL_DW-1:0] value;
     resets_t enabled_resets;
@@ -51,6 +61,8 @@
       cfg.clk_rst_vif.wait_clks(cycles_before_reset);
       `uvm_info(`gfn, $sformatf("Sending resets=0x%x", resets), UVM_MEDIUM)
       cfg.pwrmgr_vif.update_resets(resets);
+      `uvm_info(`gfn, $sformatf("Sending sw reset from rstmgr=%b", sw_rst_from_rstmgr), UVM_MEDIUM)
+      cfg.pwrmgr_vif.update_sw_rst_req(sw_rst_from_rstmgr);
 
       cfg.slow_clk_rst_vif.wait_clks(4);
 
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 c3c8435..3b7476a 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
@@ -4,6 +4,7 @@
 
 `include "pwrmgr_base_vseq.sv"
 `include "pwrmgr_aborted_low_power_vseq.sv"
+`include "pwrmgr_lowpower_wakeup_race_vseq.sv"
 `include "pwrmgr_reset_vseq.sv"
 `include "pwrmgr_smoke_vseq.sv"
 `include "pwrmgr_wakeup_vseq.sv"
diff --git a/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson b/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
index a548bca..6e27b50 100644
--- a/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
+++ b/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson
@@ -65,6 +65,11 @@
       run_opts: ["+test_timeout_ns=1000000"]
     }
     {
+      name: pwrmgr_lowpower_wakeup_race
+      uvm_test_seq: pwrmgr_lowpower_wakeup_race_vseq
+      run_opts: ["+test_timeout_ns=1000000"]
+    }
+    {
       name: pwrmgr_wakeup
       uvm_test_seq: pwrmgr_wakeup_vseq
       run_opts: ["+test_timeout_ns=1000000"]
diff --git a/hw/ip/pwrmgr/dv/tb.sv b/hw/ip/pwrmgr/dv/tb.sv
index c1566ee..cdbd612 100644
--- a/hw/ip/pwrmgr/dv/tb.sv
+++ b/hw/ip/pwrmgr/dv/tb.sv
@@ -88,7 +88,7 @@
 
     .rom_ctrl_i(pwrmgr_if.rom_ctrl),
 
-    .sw_rst_req_i(prim_mubi_pkg::MuBi4False),
+    .sw_rst_req_i(pwrmgr_if.sw_rst_req_i),
 
     .esc_rst_tx_i(pwrmgr_if.esc_rst_tx),
     .esc_rst_rx_o(pwrmgr_if.esc_rst_rx),