[adc_ctrl/dv] Added counter tests

- Added tests
  - adc_ctrl_poweron_counter
  - adc_ctrl_lowpower_counter
- Added test sequences
  - adc_ctrl_counter_vseq
    Randomize wakeup_time powerup_time and low/high/one shot power mode, wait for data then repeat.
  - adc_ctrl_poweron_counter_vseq
    Extends adc_ctrl_counter_vseq for normal and one shot mode.
  - adc_ctrl_lowpower_counter_vseq
    Extends adc_ctrl_counter_vseq for low power mode.
- Added assertions
  - WakeupTime_A - checks wakeup timing in low power mode
  - EnterLowPower_A - Checks ADC controller enters low power mode
- Updated model
  - Updated to latest spec / RTL
  - Modelled adc_fsm_reset register
  - Modelled interrupt registers for One Shot mode.
- Updated adc_ctrl_filters_polled_vseq to perform FSM reset at the end of each iteration as
to make sure model and RTL are synchronised before each new configuration
- Updated PwrupTime_A to new spec (programmed value + 2) cycles
- Enabled PwrupTime_A for all tests and removed plusarg control

Signed-off-by: Nigel Scales <nigel.scales@gmail.com>
diff --git a/hw/ip/adc_ctrl/data/adc_ctrl_testplan.hjson b/hw/ip/adc_ctrl/data/adc_ctrl_testplan.hjson
index 87a7c21..bf38126 100644
--- a/hw/ip/adc_ctrl/data/adc_ctrl_testplan.hjson
+++ b/hw/ip/adc_ctrl/data/adc_ctrl_testplan.hjson
@@ -183,7 +183,7 @@
           - Confirm timing of power down and channel select signals to ADC.
           '''
       milestone: V2
-      tests: []
+      tests: ["adc_ctrl_poweron_counter"]
     }
     {
       name: lowpower_counter
@@ -202,7 +202,7 @@
           - Confirm return to fast sampling happens as expected.
         '''
       milestone: V2
-      tests: []
+      tests: ["adc_ctrl_lowpower_counter"]
     }
     {
       name: fsm_reset
diff --git a/hw/ip/adc_ctrl/dv/adc_ctrl_sim_cfg.hjson b/hw/ip/adc_ctrl/dv/adc_ctrl_sim_cfg.hjson
index 76e5338..cd774dd 100644
--- a/hw/ip/adc_ctrl/dv/adc_ctrl_sim_cfg.hjson
+++ b/hw/ip/adc_ctrl/dv/adc_ctrl_sim_cfg.hjson
@@ -49,7 +49,7 @@
     {
       name: adc_ctrl_smoke
       uvm_test_seq: adc_ctrl_smoke_vseq
-      run_opts: ["+pwrup_time_chk=0"]
+      run_opts: []
     }
 
     {
@@ -84,6 +84,17 @@
       uvm_test_seq: adc_ctrl_filters_wakeup_vseq
       run_opts: ["+filters_fixed=1"]
     }
+
+    {
+      name: adc_ctrl_poweron_counter
+      uvm_test_seq: adc_ctrl_poweron_counter_vseq
+    }
+
+    {
+      name: adc_ctrl_lowpower_counter
+      uvm_test_seq: adc_ctrl_lowpower_counter_vseq
+    }
+
     // TODO: add more tests here
   ]
 
diff --git a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env.core b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env.core
index bfa0add..71fc377 100644
--- a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env.core
+++ b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env.core
@@ -24,6 +24,9 @@
       - seq_lib/adc_ctrl_filters_polled_vseq.sv: {is_include_file: true}
       - seq_lib/adc_ctrl_filters_interrupt_vseq.sv: {is_include_file: true}
       - seq_lib/adc_ctrl_filters_wakeup_vseq.sv: {is_include_file: true}
+      - seq_lib/adc_ctrl_counter_vseq.sv: {is_include_file: true}
+      - seq_lib/adc_ctrl_poweron_counter_vseq.sv: {is_include_file: true}
+      - seq_lib/adc_ctrl_lowpower_counter_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_cfg.sv b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_cfg.sv
index 4d4e4a5..01f2bdb 100644
--- a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_cfg.sv
+++ b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_cfg.sv
@@ -22,10 +22,10 @@
   adc_ctrl_testmode_e testmode;
 
   // Interrupt control bits
-  rand bit [ADC_CTRL_CHANNELS:0] adc_intr_ctl;
+  bit [ADC_CTRL_NUM_FILTERS:0] adc_intr_ctl = 0;
 
   // Wakeup control bits
-  rand bit [ADC_CTRL_CHANNELS-1:0] adc_wakeup_ctl;
+  bit [ADC_CTRL_NUM_FILTERS-1:0] adc_wakeup_ctl = 0;
 
 
   // Power up / wake up time
diff --git a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_pkg.sv b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_pkg.sv
index 8c82ec1..0e8d5b0 100644
--- a/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_pkg.sv
+++ b/hw/ip/adc_ctrl/dv/env/adc_ctrl_env_pkg.sv
@@ -31,6 +31,11 @@
   parameter int unsigned ADC_CTRL_NUM_FILTERS = 8;
   // Interrupt index in interrupts interface
   parameter int ADC_CTRL_INTERRUPT_INDEX = 0;
+  // Width of pwrup_width field
+  parameter int ADC_CTRL_PWRUP_TIME_WIDTH = 4;
+  // Width of wakeup field
+  parameter int ADC_CTRL_WAKEUP_TIME_WIDTH = 24;
+
 
   // types
 
diff --git a/hw/ip/adc_ctrl/dv/env/adc_ctrl_scoreboard.sv b/hw/ip/adc_ctrl/dv/env/adc_ctrl_scoreboard.sv
index 36210b7..1fbca7c 100644
--- a/hw/ip/adc_ctrl/dv/env/adc_ctrl_scoreboard.sv
+++ b/hw/ip/adc_ctrl/dv/env/adc_ctrl_scoreboard.sv
@@ -31,9 +31,7 @@
   // Normal power and low power sample match counters
   protected uint16_t m_np_counter, m_lp_counter;
   // Expected filter status bits
-  protected
-  bit [ADC_CTRL_NUM_FILTERS - 1 : 0]
-      m_expected_filter_status, m_expected_filter_status_prev;
+  protected bit [ADC_CTRL_NUM_FILTERS - 1 : 0] m_expected_filter_status;
   // Debounce detected
   protected bit m_debounced;
   // Expected adc_intr_status (1 bit per filter + oneshot mode)
@@ -48,6 +46,10 @@
   protected event m_intr_state_wr_ev;
   // Expected wakeup line
   protected bit m_expected_wakeup;
+  // Write to adc_fsm_reset
+  protected event m_adc_fsm_reset_wr_ev;
+  // FSM reset reg value
+  protected bit m_adc_fsm_reset;
 
   `uvm_component_utils(adc_ctrl_scoreboard)
 
@@ -106,10 +108,14 @@
 
       // Compare against expected every change of interrupt line
       if (cfg.en_scb & (m_interrupt ^ m_interrupt_prev)) begin
-        `uvm_info(`gfn, $sformatf(
-                  "monitor_intr_proc: interrupt pin change m_interrupt=%b", m_interrupt),
-                  UVM_MEDIUM)
-        `DV_CHECK_EQ(m_interrupt, m_expected_intr_state)
+        fork
+          begin
+            `uvm_info(`gfn, $sformatf(
+                      "monitor_intr_proc: interrupt pin change m_interrupt=%b", m_interrupt),
+                      UVM_MEDIUM)
+            `DV_CHECK_EQ(m_interrupt, m_expected_intr_state)
+          end
+        join_none
       end
 
     end
@@ -196,8 +202,7 @@
     case (csr.get_name())
       // add individual case item for each csr
       "intr_state": begin
-        // TODO: update modeling for One Shot mode
-        do_read_check = !(cfg.testmode inside {AdcCtrlOneShot});
+        do_read_check = 1;
         if (addr_phase_write) begin
           ->m_intr_state_wr_ev;
           // Implement W1C
@@ -215,7 +220,7 @@
       end
       "adc_intr_status": begin
         // TODO: update modeling for One Shot mode
-        do_read_check = !(cfg.testmode inside {AdcCtrlOneShot});
+        do_read_check = 1;
         if (addr_phase_write) begin
           ->m_adc_intr_status_wr_ev;
           // Implement W1C
@@ -243,15 +248,7 @@
           ->m_filter_status_wr_ev;
           // Implement W1C
           m_expected_filter_status &= (~item.a_data);
-          // Update m_expected_filter_status_prev after 1 clock
-          fork
-            begin
-              cfg.clk_aon_rst_vif.wait_clks(1);
-              m_expected_filter_status_prev = m_expected_filter_status;
-            end
-          join_none
         end
-
       end
       "adc_chn0_filter_ctl_0", "adc_chn0_filter_ctl_1", "adc_chn0_filter_ctl_2",
           "adc_chn0_filter_ctl_3", "adc_chn0_filter_ctl_4", "adc_chn0_filter_ctl_5",
@@ -305,6 +302,13 @@
         end
       end
 
+      // FSM Reset
+      "adc_fsm_rst": begin
+        if (addr_phase_write) begin
+          ->m_adc_fsm_reset_wr_ev;
+          do_adc_fsm_reset(item.a_data[0]);
+        end
+      end
 
       default: begin
         //`uvm_error(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
@@ -313,7 +317,7 @@
 
     // On reads, if do_read_check, is set, then check mirrored_value against item.d_data
     if (data_phase_read) begin
-      if (do_read_check) begin
+      if (do_read_check && cfg.en_scb) begin
         `DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data, $sformatf(
                      "reg name: %0s", csr.get_full_name()))
       end
@@ -349,41 +353,61 @@
 
   // Process debounce
   // From the spec:
-  // All pairs of filters that are enabled in adc_chn0_filter_ctl[7:0] and adc_chn1_filter_ctl[7:0] are evaluated
-  // after each pair of samples has been taken. The filter result is passed to the periodic scan counter if enabled and not at its limit
-  // otherwise the result is passed to the debounce counter. The list below describes how the counters interpret the filter results:
+  // All pairs of filters that are enabled in adc_chn0_filter_ctl[7:0] and adc_chn1_filter_ctl[7:0]
+  // are evaluated after each pair of samples has been taken. The filter result is passed to the
+  // periodic scan counter if enabled and not at its limit otherwise the result is passed to the
+  // debounce counter. The list below describes how the counters interpret the filter results:
   // If no filters are hit then the counter will reset to zero.
-  // If one or more filters are hit but the set hit differs from the previous evaluation the counter resets to zero.
-  // If one or more filters are hit and either none was hit in the previous evaluation or the same set was hit in the previous
-  // evaluation and the counter is not at its limit then the counter will increment.
-  // If the counter is the periodic scan counter and it reaches its limit then continuous scanning is enabled and the debounce
-  // counter will be used for future evaluations.
-  // If the counter is the debounce counter and it reaches its limit then:
-  // If an interrupt is not already being raised then the current sample values are latched into adc_chn_val[0].adc_chn_value_intr
-  // and adc_chn_val[1].adc_chn_value_intr. i.e. these registers only record the value of the first debounced hit.
-  // The adc_intr_status register is updated by setting the bits corresponding to filters that are hit (note that bits that
-  // are already set will not be cleared). This will cause the block to raise an interrupt if it was not already doing so.
-  // If any filters are hit that are enabled in adc_wakeup_ctl the corresponding bits in the adc_wakeup_status register are set
-  // which may initiate a wakeup.
-  // Note that the debounce counter will remain at its limit until the set of filters that are set changes when it will be
-  // reset to zero and start to debounce the next event.
+  // If one or more filters are hit but the set hit differs from the previous evaluation the
+  // counter resets to zero.
+  // If one or more filters are hit and either none was hit in the previous evaluation or the same
+  // set was hit in the previous evaluation and the counter is not at its threshold then the
+  // counter will increment.
+  // If one or more filters are hit and the same set was hit in the previous evaluation and the
+  // counter is at its threshold then the counter stays at the threshold.
+  // If the counter is the periodic scan counter and it reaches its threshold, as defined by
+  // adc_lp_sample_ctl.lp_sample_cnt, then continuous scanning is enabled and the debounce counter
+  // will be used for future evaluations.
+  // If the counter is the debounce counter and it reaches its threshold, as defined by
+  // adc_sample_ctl.np_sample_cnt, then:
+  // An interrupt is raised if the threshold is met for the first time.
+  // The current sample values are latched into adc_chn_val[0].adc_chn_value_intr and
+  // adc_chn_val[1].adc_chn_value_intr.
+  // If a series of interrupts and matches are seen, these registers only record the value
+  // of the last debounced hit.
+  // The adc_intr_status register is updated by setting the bits corresponding to filters that
+  // are hit (note that bits that are already set will not be cleared). This will cause the block
+  // to raise an interrupt if it was not already doing so.
+  // If a filter is a hit and is also enabled in adc_wakeup_ctl the corresponding filter
+  // generates a wakeup.
+  // Note that the debounce counter will remain at its threshold until the set of filters are
+  // changed by software to debounce a different event or if the current match changes.
+  // This implies that a stable matching event continuously matches until some condition in the
+  // system (changed filter settings or changed ADC output) alters the result.
+  // Because scanning continues the adc_intr_status register will reflect any debounced events
+  // that are detected between the controller raising an interrupt and the status bits being
+  // cleared (by having 1 written to them). However, the adc_chn_val[0].adc_chn_value_intr and
+  // adc_chn_val[1].adc_chn_value_intrregisters record the value at the time the interrupt was
+  // first raised and thus reflect the filter state from that point.
 
   virtual function void process_debounce();
-    if (m_match != 0) begin
+    if (m_match != 0 && !m_adc_fsm_reset) begin
       if ((m_match == m_match_prev) || (m_match_prev == 0)) begin
         // If one or more filters are hit and either none was hit in the previous evaluation
         // or the same set was hit in the previous evaluation and the counter is not
         // at its limit then the counter will increment.
-        if (m_np_counter < (cfg.np_sample_cnt - 1)) begin
+        if (m_np_counter < cfg.np_sample_cnt) begin
           // Not at it's limit
           m_np_counter++;
-        end else if (!m_debounced) begin
-          bit intr_enable = ral.intr_enable.debug_cable.get_mirrored_value();
-          // Capture matches
-          m_debounced = 1;
-          m_expected_filter_status |= m_match;
-          m_expected_adc_intr_status |= (m_expected_filter_status & ~m_expected_filter_status_prev);
-          m_expected_intr_state = intr_enable & (|(m_expected_adc_intr_status & cfg.adc_intr_ctl));
+          // Note m_np_counter has now been incremented below so we can detect the
+          // transition from (cfg.np_sample_cnt - 1) to cfg.np_sample_cnt
+          if (m_np_counter == cfg.np_sample_cnt && !m_debounced) begin
+            // Capture matches
+            m_debounced = 1;
+            m_expected_filter_status |= m_match;
+            m_expected_adc_intr_status |= m_match & cfg.adc_intr_ctl;
+            m_expected_intr_state |= (|(m_match & cfg.adc_intr_ctl));
+          end
         end
       end else begin
         // Previous matched set different to current
@@ -395,12 +419,20 @@
       m_np_counter = 0;
       m_debounced  = 0;
     end
+
+    // Implement One Shot Mode
+    // One hot interrupt is one bit above the last filter's interrupt
+    if (cfg.testmode inside {AdcCtrlOneShot}) begin
+      m_expected_adc_intr_status[ADC_CTRL_NUM_FILTERS] = cfg.adc_intr_ctl[ADC_CTRL_NUM_FILTERS];
+      m_expected_intr_state |= cfg.adc_intr_ctl[ADC_CTRL_NUM_FILTERS];
+    end
+
     // Delayfor edge detection
     m_match_prev = m_match;
-    m_expected_filter_status_prev = m_expected_filter_status;
 
     // Decode expected wakeup - allow dynamic control
     m_expected_wakeup = |(m_expected_filter_status & cfg.ral.adc_wakeup_ctl.get_mirrored_value());
+
   endfunction
 
 
@@ -416,12 +448,28 @@
     m_chn_match = 0;
     m_np_counter = 0;
     m_lp_counter = 0;
+    m_debounced = 0;
     m_expected_filter_status = 0;
-    m_expected_filter_status_prev = 0;
     m_expected_adc_intr_status = 0;
     m_expected_intr_state = 0;
   endfunction
 
+  // Software reset
+  // val = register value
+  virtual function void do_adc_fsm_reset(bit val);
+    m_adc_fsm_reset = val;
+    if (val) begin
+      // Clear model state
+      m_match = 0;
+      m_match_prev = 0;
+      m_chn_match = 0;
+      m_np_counter = 0;
+      m_lp_counter = 0;
+      m_debounced = 0;
+    end
+  endfunction
+
+
   function void check_phase(uvm_phase phase);
     super.check_phase(phase);
     // post test checks - ensure that all local fifos and queues are empty
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_base_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_base_vseq.sv
index 4079159..6588682 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_base_vseq.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_base_vseq.sv
@@ -110,7 +110,7 @@
   endfunction
 
   // Turn off ADC CTRL without triggering assertions
-  task adc_ctrl_off();
+  virtual task adc_ctrl_off();
     // Disable assertions which will trigger because of the abrupt turn off
     `DV_ASSERT_CTRL_REQ("ADC_IF_A_CTRL", 0)
     csr_wr(ral.adc_en_ctl, 'h0);
@@ -123,6 +123,18 @@
     join_none
   endtask
 
+  // Perform software reset
+  virtual task do_adc_fsm_reset();
+    // Disable assertions which will trigger because of the abrupt reset
+    `DV_ASSERT_CTRL_REQ("ADC_IF_A_CTRL", 0)
+    csr_wr(ral.adc_fsm_rst, 1);
+    cfg.clk_aon_rst_vif.wait_clks($urandom_range(5, 10));
+    csr_wr(ral.adc_fsm_rst, 0);
+    cfg.clk_aon_rst_vif.wait_clks($urandom_range(5, 10));
+    // Re-enable assertions
+    `DV_ASSERT_CTRL_REQ("ADC_IF_A_CTRL", 1)
+  endtask
+
 endclass : adc_ctrl_base_vseq
 
 
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_counter_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_counter_vseq.sv
new file mode 100644
index 0000000..51e1712
--- /dev/null
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_counter_vseq.sv
@@ -0,0 +1,85 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+// Check power on/wakeup counters using the assertions in the testbench
+// Just randomize pwrup_time and wakeup_time register and enable the ADC_CTRL in one normal mode
+// low power mode or oneshot mode
+class adc_ctrl_counter_vseq extends adc_ctrl_base_vseq;
+
+  rand int pwrup_time;
+  rand int wakeup_time;
+  rand adc_ctrl_testmode_e testmode;
+
+  `uvm_object_utils_begin(adc_ctrl_counter_vseq)
+    `uvm_field_enum(adc_ctrl_testmode_e, testmode, UVM_DEFAULT)
+    `uvm_field_int(pwrup_time, UVM_DEFAULT)
+    `uvm_field_int(wakeup_time, UVM_DEFAULT)
+  `uvm_object_utils_end
+
+  `uvm_object_new
+
+  // Counter values
+  constraint counters_c {
+    pwrup_time inside {[0 : (2 ** ADC_CTRL_PWRUP_TIME_WIDTH) - 1]};
+    wakeup_time inside {[0 : (2 ** ADC_CTRL_WAKEUP_TIME_WIDTH) - 1]};
+  }
+
+  constraint num_trans_c {num_trans inside {[10 : 20]};}
+
+  virtual task body();
+    // Disable read of intr_state at the end of the sequence
+    do_clear_all_interrupts = 0;
+
+    // Make sure ADC is off
+    csr_wr(ral.adc_en_ctl, 'h0);
+
+    repeat (num_trans) begin
+
+      // Copy sequence fields to config object
+      cfg.testmode = testmode;
+      cfg.pwrup_time = pwrup_time;
+      cfg.wakeup_time = wakeup_time;
+
+      // Set up common register fields
+      ral.adc_en_ctl.adc_enable.set(1);
+      ral.adc_pd_ctl.pwrup_time.set(pwrup_time);
+      ral.adc_pd_ctl.wakeup_time.set(wakeup_time);
+
+      // Set up variable register fields
+      case (testmode)
+        AdcCtrlOneShot: begin
+          ral.adc_en_ctl.oneshot_mode.set(1);
+          ral.adc_pd_ctl.lp_mode.set(0);
+        end
+        AdcCtrlNormal: begin
+          ral.adc_en_ctl.oneshot_mode.set(0);
+          ral.adc_pd_ctl.lp_mode.set(0);
+        end
+        AdcCtrlLowpower: begin
+          ral.adc_en_ctl.oneshot_mode.set(0);
+          ral.adc_pd_ctl.lp_mode.set(1);
+        end
+        default: `uvm_fatal(`gfn, "Unknown test mode")
+      endcase
+
+      // Write registers to enable ADC_CTRL
+      csr_wr(ral.adc_pd_ctl, ral.adc_pd_ctl.get());
+      csr_wr(ral.adc_en_ctl, ral.adc_en_ctl.get());
+
+      // Wait for data transfers
+      wait_for_txfers();
+
+      // Disable ADC.
+      adc_ctrl_off();
+
+      // Re-randomize this sequence
+      `DV_CHECK_RANDOMIZE_FATAL(this)
+
+    end
+  endtask : body
+
+  virtual task wait_for_txfers();
+    wait_all_rx();
+  endtask
+
+endclass : adc_ctrl_counter_vseq
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_interrupt_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_interrupt_vseq.sv
index 9ee6442..4d258b1 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_interrupt_vseq.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_interrupt_vseq.sv
@@ -10,8 +10,11 @@
   `uvm_object_new
 
   virtual task configure_adc_ctrl();
+    bit [ADC_CTRL_NUM_FILTERS:0] adc_intr_ctl;
     super.configure_adc_ctrl();
     // Enable interrupts
+    `DV_CHECK_STD_RANDOMIZE_FATAL(adc_intr_ctl)
+    cfg.adc_intr_ctl = adc_intr_ctl;
     csr_wr(ral.adc_intr_ctl, cfg.adc_intr_ctl);
     csr_wr(ral.intr_enable, 'h1);
   endtask
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_polled_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_polled_vseq.sv
index 207e7fd..36e8bd8 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_polled_vseq.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_polled_vseq.sv
@@ -74,12 +74,15 @@
       // Now turn off ADC_CTRL
       adc_ctrl_off();
 
+      // FSM reset to synchronise RTL & model
+      do_adc_fsm_reset();
+
       // Re-randomize configuration if enabled
       if (!cfg.filters_fixed) `DV_CHECK_RANDOMIZE_FATAL(cfg);
 
     end
     // A short delay to allow all CDC to complete
-    cfg.clk_aon_rst_vif.wait_clks($urandom_range(3, 5));
+    cfg.clk_aon_rst_vif.wait_clks($urandom_range(10, 15));
   endtask : body
 
   // Check the status registers after every ADC sample (all channels)
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_wakeup_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_wakeup_vseq.sv
index 29b8af5..204e45b 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_wakeup_vseq.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_filters_wakeup_vseq.sv
@@ -10,8 +10,11 @@
   `uvm_object_new
 
   virtual task configure_adc_ctrl();
+    bit [ADC_CTRL_NUM_FILTERS-1:0] adc_wakeup_ctl;
     super.configure_adc_ctrl();
     // Enable wakeup
+    `DV_CHECK_STD_RANDOMIZE_FATAL(adc_wakeup_ctl)
+    cfg.adc_wakeup_ctl = adc_wakeup_ctl;
     csr_wr(ral.adc_wakeup_ctl, cfg.adc_wakeup_ctl);
   endtask
 
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_lowpower_counter_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_lowpower_counter_vseq.sv
new file mode 100644
index 0000000..2a59105
--- /dev/null
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_lowpower_counter_vseq.sv
@@ -0,0 +1,28 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+// Check power on/wakeup counters using the assertions in the testbench
+// Comprehensively test wakeup_time counter
+class adc_ctrl_lowpower_counter_vseq extends adc_ctrl_counter_vseq;
+
+  `uvm_object_utils(adc_ctrl_lowpower_counter_vseq)
+  `uvm_object_new
+
+  // Valid values
+  constraint testmode_c {testmode inside {AdcCtrlLowpower};}
+
+  // Counter values
+  constraint counters_c {
+    pwrup_time inside {[0 : 2 ** 4 - 1]};
+    wakeup_time inside {[100 : 200]};
+  }
+
+  virtual task wait_for_txfers();
+    wait_all_rx();
+    `DV_ASSERT_CTRL_REQ("WakeupTime_A_CTRL", 1)
+    wait_all_rx();
+    wait_all_rx();
+    `DV_ASSERT_CTRL_REQ("WakeupTime_A_CTRL", 0)
+  endtask
+
+endclass : adc_ctrl_lowpower_counter_vseq
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_poweron_counter_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_poweron_counter_vseq.sv
new file mode 100644
index 0000000..8deb62c
--- /dev/null
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_poweron_counter_vseq.sv
@@ -0,0 +1,14 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+// Check power on/wakeup counters using the assertions in the testbench
+// Comprehensively test pwrup_time counter
+class adc_ctrl_poweron_counter_vseq extends adc_ctrl_counter_vseq;
+
+  `uvm_object_utils(adc_ctrl_poweron_counter_vseq)
+  `uvm_object_new
+
+  // Valid values
+  constraint testmode_c {testmode inside {AdcCtrlOneShot, AdcCtrlNormal};}
+
+endclass : adc_ctrl_poweron_counter_vseq
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_smoke_vseq.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_smoke_vseq.sv
index f9bdef1..97deca5 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_smoke_vseq.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_smoke_vseq.sv
@@ -23,6 +23,13 @@
 
   `uvm_object_new
 
+  virtual task pre_start();
+    super.pre_start();
+    cfg.testmode = AdcCtrlOneShot;
+    cfg.adc_intr_ctl = 0;
+    cfg.adc_wakeup_ctl = 0;
+  endtask
+
   task body();
     uvm_reg_data_t rdata;
     // Vector with onehot interrupt status bit position = 1
@@ -33,6 +40,13 @@
 
     // Set one shot interrupt enable
     csr_wr(ral.adc_intr_ctl, ONE_SHOT_INTR);
+    // Copy to config object for scoreboard
+    cfg.adc_intr_ctl = ONE_SHOT_INTR;
+
+    // Configure power control register
+    ral.adc_pd_ctl.pwrup_time.set(cfg.pwrup_time);
+    ral.adc_pd_ctl.wakeup_time.set(cfg.wakeup_time);
+    csr_wr(ral.adc_pd_ctl, ral.adc_pd_ctl.get());
 
     repeat (20) begin
 
diff --git a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_vseq_list.sv b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_vseq_list.sv
index 035e3b5..2c775f7 100644
--- a/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_vseq_list.sv
+++ b/hw/ip/adc_ctrl/dv/env/seq_lib/adc_ctrl_vseq_list.sv
@@ -9,3 +9,7 @@
 `include "adc_ctrl_filters_polled_vseq.sv"
 `include "adc_ctrl_filters_interrupt_vseq.sv"
 `include "adc_ctrl_filters_wakeup_vseq.sv"
+`include "adc_ctrl_counter_vseq.sv"
+`include "adc_ctrl_poweron_counter_vseq.sv"
+`include "adc_ctrl_lowpower_counter_vseq.sv"
+
diff --git a/hw/ip/adc_ctrl/dv/tb.sv b/hw/ip/adc_ctrl/dv/tb.sv
index 5267680..d5011e1 100644
--- a/hw/ip/adc_ctrl/dv/tb.sv
+++ b/hw/ip/adc_ctrl/dv/tb.sv
@@ -2,6 +2,30 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 //
+
+// Enhanced DV_ASSERT_CTRL which kills active assertions before disabling them
+`ifndef ADC_CTRL_DV_ASSERT_CTRL
+`define ADC_CTRL_DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = $sformatf("%m")) \
+  initial begin \
+    bit assert_en; \
+    forever begin \
+      uvm_config_db#(bit)::wait_modified(null, SCOPE_, LABEL_); \
+      if (!uvm_config_db#(bit)::get(null, SCOPE_, LABEL_, assert_en)) begin \
+        `uvm_fatal(ID_, $sformatf("Failed to get \"%0s\" from uvm_config_db", LABEL_)) \
+      end \
+      if (assert_en) begin \
+        `uvm_info(ID_, $sformatf("Enabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
+        $asserton(LEVELS_, HIER_); \
+      end else begin \
+        `uvm_info(ID_, $sformatf("Disabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
+        $assertkill(LEVELS_, HIER_); \
+        $assertoff(LEVELS_, HIER_); \
+      end \
+    end \
+  end
+`endif
+
+
 module tb;
   // dep packages
   import uvm_pkg::*;
@@ -26,9 +50,14 @@
   wire ast_pkg::adc_ast_req_t adc_o;
   ast_pkg::adc_ast_rsp_t adc_i;
   adc_ctrl_env_cfg cfg;
+  // Basic testing mode
+  adc_ctrl_testmode_e cfg_testmode;
   // Auxiliary logic to time power up -> first channel request
   int pwrup_time, cfg_pwrup_time;
   bit pwrup_time_en;
+  // Auxiliary logic to time power down -> power up
+  int wakeup_time, cfg_wakeup_time;
+  bit wakeup_time_en;
 
 
   `DV_ALERT_IF_CONNECT
@@ -97,7 +126,14 @@
       `uvm_fatal("TB", "Couldn't find the environment config")
     end
     `uvm_info("TB", "Found environment config", UVM_MEDIUM)
-    cfg_pwrup_time = cfg.pwrup_time;
+
+    // Constantly update from configuration object
+    forever begin
+      cfg_testmode = cfg.testmode;
+      cfg_pwrup_time = cfg.pwrup_time;
+      cfg_wakeup_time = cfg.wakeup_time;
+      @(cfg.pwrup_time or cfg.wakeup_time or cfg.testmode);
+    end
   end
 
   // Push pull agents
@@ -173,17 +209,50 @@
   // Pulse to check power up counter
   wire pwrup_time_chk = |adc_o.channel_sel & pwrup_time_en;
 
+  // Auxiliary logic to time clocks from power down to power up
+  always @(posedge clk_aon or negedge rst_aon_n) begin
+    if (!rst_aon_n) begin
+      wakeup_time <= 0;
+      wakeup_time_en <= 1;
+    end else begin
+      if (adc_o.pd == 1) begin
+        wakeup_time <= 0;
+        wakeup_time_en <= 1;
+      end else begin
+        if (adc_o.pd == 1) wakeup_time_en <= 0;
+        else if (wakeup_time_en) wakeup_time <= wakeup_time + 1;
+      end
+    end
+  end
+  // Pulse to check wake up counter
+  wire wakeup_time_chk = adc_o.pd & wakeup_time_en;
+
+  // Check the DUT enters low power
+  // In low power test mode, after falling edges on power down
+  // and the last ADC channel select, power down should be re-asserted within 10 clock cycles
+  //verilog_format: off - avoid bad formatting
+  property EnterLowPower_P;
+    ((cfg_testmode == AdcCtrlLowpower) && $fell(adc_o.pd))
+        ##[+] $fell(adc_o.channel_sel[ADC_CTRL_CHANNELS - 1]) |=> ##[0:10] adc_o.pd;
+  endproperty
+  //verilog_format: on
+
   // Assertions
   `ASSERT(ChannelSelOnehot_A, $onehot0(adc_o.channel_sel), clk_aon, ~rst_aon_n)
   `ASSERT_KNOWN(ChannelSelKnown_A, adc_o.channel_sel, clk_aon, ~rst_aon_n)
   `ASSERT_KNOWN(PdKnown_A, adc_o.pd, clk_aon, ~rst_aon_n)
-  `ASSERT(PwrupTime_A, $rose(pwrup_time_chk) |-> pwrup_time == cfg_pwrup_time, clk_aon, ~rst_aon_n)
+  `ASSERT(PwrupTime_A, $rose(pwrup_time_chk) |-> pwrup_time == (cfg_pwrup_time + 2), clk_aon,
+          ~rst_aon_n)
+  `ASSERT(WakeupTime_A, $rose(wakeup_time_chk) |-> wakeup_time == cfg_wakeup_time, clk_aon,
+          ~rst_aon_n)
+  `ASSERT(EnterLowPower_A, EnterLowPower_P, clk_aon, ~rst_aon_n)
+
 
   // Assertion controls
-  `DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[0])
-  `DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[1])
+  `ADC_CTRL_DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[0])
+  `ADC_CTRL_DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[1])
   `DV_ASSERT_CTRL("PwrupTime_A_CTRL", PwrupTime_A)
-
+  `DV_ASSERT_CTRL("WakeupTime_A_CTRL", WakeupTime_A)
 
 endmodule
 
diff --git a/hw/ip/adc_ctrl/dv/tests/adc_ctrl_base_test.sv b/hw/ip/adc_ctrl/dv/tests/adc_ctrl_base_test.sv
index ad2d223..b3e87f4 100644
--- a/hw/ip/adc_ctrl/dv/tests/adc_ctrl_base_test.sv
+++ b/hw/ip/adc_ctrl/dv/tests/adc_ctrl_base_test.sv
@@ -18,7 +18,6 @@
   // the run_phase; as such, nothing more needs to be done
   virtual function void build_phase(uvm_phase phase);
     bit print_ral = 0;
-    bit pwrup_time_chk = 1;
     bit filters_fixed = 0;
 
     // Defaults - can be overridden by plusargs
@@ -34,14 +33,12 @@
       `uvm_info(`gfn, cfg.ral.sprint(), UVM_LOW)
     end
 
-    // Enable power up check
-    void'($value$plusargs("pwrup_time_chk=%0b", pwrup_time_chk));
-    `DV_ASSERT_CTRL_REQ("PwrupTime_A_CTRL", pwrup_time_chk)
-
     // Enable fixed filters
     void'($value$plusargs("filters_fixed=%0b", filters_fixed));
     cfg.filters_fixed = filters_fixed;
 
+    // Disable wake up assertion - is periodically enabled by the test sequence
+    `DV_ASSERT_CTRL_REQ("WakeupTime_A_CTRL", 0)
 
     // Print test config
     `uvm_info(`gfn, cfg.sprint(), UVM_LOW)