[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)