[dv/pwrmgr] Add some covergroups
Hook up wakeup control and interrupt, and clock control covergroups.
Improve the dv and testplan documents.
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 871369b..2ca8292 100644
--- a/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
+++ b/hw/ip/pwrmgr/data/pwrmgr_testplan.hjson
@@ -71,6 +71,7 @@
- Checks the output `pwr_rst_req.reset_cause` matches a low power
cause.
- Check that `intr_wakeup_o` is set according to `intr_enable` CSR.
+ - Coverage collected by `wakeup_cg` and `wakeup_intr_cg`.
'''
milestone: V2
tests: []
@@ -227,20 +228,31 @@
covergroups: [
{
- name: wakeup_cg
+ name: wakeup_ctrl_cg
desc: '''
- Collects coverage related to wakeup functionality.
+ Collects coverage on wakeup enable and capture functionality.
- Covergroup contains coverpoints for the `wakeups_i` input,
- `wakeup_en`, `wakeup_info`, and `wakeup_info_capture_dis`,
- and suitable crosses.
+ This is collected per individual wakeup bit. Covergroup contains
+ coverpoints for the `wakeup_en` CSR bit, `wakeup_info_capture_dis`
+ CSR, `wakeups_i` input bit, and `wakeup_status` CSR bit, and their
+ cross.
'''
}
{
- name: lowpower_clock_enables_cg
+ name: wakeup_intr_cg
desc: '''
- Collects coverage on clock enables from `control` CSR during a
- lowpower transition.
+ Collects coverage on interrupts for wakeup functionality.
+
+ This is collected per individual wakeup bit. Covergroup contains
+ coverpoints for the `intr_en` CSR, the `wakeup_status` CSR bit,
+ and `intr_status` CSR, and their cross.
+ '''
+ }
+ {
+ name: clock_control_cg
+ desc: '''
+ Collects coverage on clock enable bits from `control` CSR during a
+ lowpower transition and active state.
'''
}
{
diff --git a/hw/ip/pwrmgr/doc/dv/index.md b/hw/ip/pwrmgr/doc/dv/index.md
index 3d22469..d5a6870 100644
--- a/hw/ip/pwrmgr/doc/dv/index.md
+++ b/hw/ip/pwrmgr/doc/dv/index.md
@@ -88,31 +88,26 @@
These tasks are started by the parent sequence's `pre_start` task, and terminated gracefully in the parent sequence's `post_start` task.
-The `pwrmgr_smoke_vseq` sequence tests the pwrmgr through POR, entry and exit from software initiated low power and reset.
-
-The `pwrmgr_wakeup_vseq` sequence checks the transitions to low power and the wakeup settings.
-It randomizes wakeup inputs, wakeup enables, the wakeup info capture enable, and the interrupt enable.
-
-The `pwrmgr_clks_en_vseq` sequence checks that the peripheral clock enables match the settings of the `control` CSR during low power.
-It uses a subset of the wakeup sequence.
-
-The `pwrmgr_aborted_lowpower_vseq` sequence creates scenarios that lead to aborting a lowpower transition.
-The abort can be due to the processor waking up very soon, or otp, lc, or flash being busy.
-
-The `pwrmgr_reset_vseq` sequence checks the pwrmgr response to resets and reset enables.
-
-The `pwrmgr_escalation_reset_vseq` sequence checks the response to an escalation reset.
-
-The `pwrmgr_reset_wakeup_vseq` sequence aligns reset and wakeup from low power.
-
-The `pwrmgr_lowpower_wakeup_race_vseq` sequence aligns a wakeup event coming in proximity to low power entry.
-Notice the wakeup is not expected to impact low power entry, since it is not sampled at this time.
+The test sequences besides the base are as follows:
+* `pwrmgr_smoke_vseq` tests the pwrmgr through POR, entry and exit from software initiated low power and reset.
+* `pwrmgr_wakeup_vseq` checks the transitions to low power and the wakeup settings.
+ It randomizes wakeup inputs, wakeup enables, the wakeup info capture enable, and the interrupt enable.
+* `pwrmgr_clks_en_vseq` checks that the peripheral clock enables match the settings of the `control` CSR during low power.
+ It uses a subset of the wakeup sequence.
+* `pwrmgr_aborted_lowpower_vseq` creates scenarios that lead to aborting a lowpower transition.
+ The abort can be due to the processor waking up very soon, or otp, lc, or flash being busy.
+* `pwrmgr_reset_vseq` checks the pwrmgr response to resets and reset enables.
+* `pwrmgr_escalation_reset_vseq` checks the response to an escalation reset.
+* `pwrmgr_reset_wakeup_vseq` aligns reset and wakeup from low power.
+* `pwrmgr_lowpower_wakeup_race_vseq` aligns a wakeup event coming in proximity to low power entry.
+ Notice the wakeup is not expected to impact low power entry, since it is not sampled at this time.
#### Functional coverage
To ensure high quality constrained random stimulus, it is necessary to develop a functional coverage model.
The following covergroups have been developed to prove that the test intent has been adequately met:
-* wakeup_cg
-* lowpower_clock_enables_cg
+* `wakeup_ctrl_cg` covers wakeup and capture control.
+* `wakeup_intr_cg` covers control of the interrupt due to a wakeup.
+* `clock_enables_cg` covers clock controls.
* reset_cg
* reset_lowpower_distance_cg
@@ -136,19 +131,19 @@
- Input `slow_clk_val` is unused.
- Outputs `core_clk_en`, `io_clk_en`, and `usb_clk_en` reset low, and go high prior to the slow fsm requesting the fast fsm to wakeup.
Notice the usb clock can be programmed to stay low on wakeup via the `control` CSR.
- These clock enables should match their corresponding enables in the `control` CSR on low power transitions.
- These clock enables are cleared on reset.
- These clock enables are checked in the scoreboard.
+ These clock enables are cleared on reset, and should match their corresponding enables in the `control` CSR on low power transitions.
+ These clock enables are checked via SVAs in `hw/ip/pwrmgr/dv/sva/pwrmgr_clock_enables_sva_if.sv`.
When slow fsm transitions to `SlowPwrStateReqPwrUp` the clock enables should be on (except usb should match `control.usb_clk_en_active`).
When slow fsm transitions to `SlowPwrStatePwrClampOn` the clock enables should match their bits in the `control` CSR.
- Inputs `core_clk_val`, `io_clk_val`, and `usb_clk_val` track the corresponding enables.
- They are driven by sequences, which turn them off when their enables go off, and turn them back on a few random slow clock cycles after their enables go on.
+ They are driven by `slow_responder`, which turn them off when their enables go off, and turn them back on a few random slow clock cycles after their enables go on.
Slow fsm waits for them to go high prior to requesting fast fsm wakeup.
Lack of a high transition when needed is detected via timeout.
Such timeout would be due to the corresponding enables being set incorrectly.
+ These inputs are checked via SVAs in `hw/ip/pwrmgr/dv/sva/pwrmgr_ast_sva_if.sv`.
- Output `main_pd_n` should go high when slow fsm transitions to `SlowPwrStateMainPowerOn`, and should match `control.main_pd_n` CSR when slow fsm transitions to `SlowPwrStateMainPowerOff`.
- Input `main_pok` should turn on for the slow fsm to start power up sequence.
- Sequences will turn this off in response to `main_pd_n` going low, and turn it back on after a few random slow clock cycles from `main_pd_n` going high.
+ This is also driven by `slow_responder`, which turn this off in response to `main_pd_n` going low, and turn it back on after a few random slow clock cycles from `main_pd_n` going high.
Lack of a high transition causes a timeout, and would point to `main_pd_n` being set incorrectly.
- Output transitions of `pwr_clamp_env` must always precede transitions of
`pwr_clamp` output.
@@ -161,14 +156,15 @@
- Output `rst_lc_req` resets to 1, also set on reset transition, and on low power transitions that turn off main clock.
Cleared early on during the steps to fast fsm active.
- Input `rst_lc_src_n` go low in response to `rst_lc_req` high, go high when `rst_lc_req` clears (and lc is reset).
- Driven by sequences in response to `rst_lc_req`, waiting a few random cycles prior to transitions.
+ Driven by `fast_responder` in response to `rst_lc_req`, waiting a few random cycles prior to transitions.
Fast fsm waits for it to go low before deactivating, and for it to go high before activating.
Checked implicitly by lack of timeout: a timeout would be due to `rst_lc_req` being set incorrectly.
- Output `rst_sys_req` resets to 1, also set to on reset, and on low power transitions that turn off main clock.
-Cleared right before the fast fsm goes active.
+ Cleared right before the fast fsm goes active.
- Input `rst_sys_src_n` go low in response to `rst_sys_req` high.
Transitions go high when `rst_sysd_req` clears (and lc is reset).
Fast fsm waits for it to go low before deactivating.
+ Also driver by `fast_responder`.
Checked implicitly by lack of timeout.
- Output `rstreqs` correspond to the enabled pwrmgr rstreqs inputs plus escalation reset.
Checked in scoreboard.
@@ -179,15 +175,15 @@
- Output `ip_clk_en` resets low, is driven high by fast fsm when going active, and driven low when going inactive.
- Input `clk_status` is expected to track `ip_clk_en`.
Fast fsm waits for it going high prior to going active, and for it to go low prior to deactivating.
- Driven by sequences, which turns it off when `ip_clk_en` goes low, and rurn it back on a few random cycles after `ip_clk_en` going high.
+ Driven by `fast_responder`, which turns it off when `ip_clk_en` goes low, and rurn it back on a few random cycles after `ip_clk_en` going high.
Checked by lack of a timeout: such timeout would be due to `ip_clk_en` being set incorrectly.
##### OTP
- Output `otp_init` resets low, goes high when the fast fsm is going active, and low after the `otp_done` input goes high.
-- Input `otp_done` is driven by sequences.
+- Input `otp_done` is driven by `fast_responder`.
It is initialized low, and goes high some random cycles after `otp_init` goes high.
The sequencer will timeout if `otp_init` is not driven high.
-- Input `otp_idle` will be normally be set high, but will be set low by the `pwrmgr_aborted_lowpower_vseq` sequence.
+- Input `otp_idle` will normally be set high, but will be set low by the `pwrmgr_aborted_lowpower_vseq` sequence.
###### LC
The pins connecting to LC behave pretty much the same way as those to OTP.
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
index 81ed45a..4cbce09 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_env_cov.sv
@@ -4,10 +4,65 @@
/**
* Covergoups that are dependent on run-time parameters that may be available
- * only in build_phase can be defined here
+ * only in build_phase can be defined here.
* Covergroups may also be wrapped inside helper classes if needed.
*/
+// Wrapper class for wakeup control covergroup.
+class pwrmgr_wakeup_ctrl_cg_wrap;
+ // This covers enable, capture, and status of wakeups.
+ covergroup wakeup_ctrl_cg(
+ string name
+ ) with function sample (
+ bit enable, bit capture, bit wakeup, bit status
+ );
+ option.name = name;
+ option.per_instance = 1;
+
+ enable_cp: coverpoint enable;
+ capture_cp: coverpoint capture;
+ wakeup_cp: coverpoint wakeup;
+ status_cp: coverpoint status;
+
+ wakeup_cross: cross enable_cp, capture_cp, wakeup_cp, status_cp;
+ endgroup
+
+ function new(string name);
+ wakeup_ctrl_cg = new(name);
+ endfunction
+
+ function void sample (bit enable, bit capture, bit wakeup, bit status);
+ wakeup_ctrl_cg.sample(enable, capture, wakeup, status);
+ endfunction
+endclass
+
+// Wrapper class for wakeup interrupt covergroup.
+class pwrmgr_wakeup_intr_cg_wrap;
+ // This covers interrupts generated by wakeups.
+ covergroup wakeup_intr_cg(
+ string name
+ ) with function sample (
+ bit enable, bit wakeup, bit interrupt
+ );
+ option.name = name;
+ option.per_instance = 1;
+
+ enable_cp: coverpoint enable;
+ wakeup_cp: coverpoint wakeup;
+ interrupt_cp: coverpoint interrupt;
+
+ interrupt_cross: cross enable_cp, wakeup_cp, interrupt_cp;
+ endgroup
+
+ function new(string name);
+ wakeup_intr_cg = new(name);
+ endfunction
+
+ function void sample (bit enable, bit wakeup, bit interrupt);
+ wakeup_intr_cg.sample(enable, wakeup, interrupt);
+ endfunction
+endclass
+
class pwrmgr_env_cov extends cip_base_env_cov #(
.CFG_T(pwrmgr_env_cfg)
);
@@ -17,11 +72,27 @@
// pwrmgr_env_cfg: cfg
// covergroups
- // [add covergroups here]
+ pwrmgr_wakeup_ctrl_cg_wrap wakeup_ctrl_cg_wrap[pwrmgr_reg_pkg::NumWkups];
+ pwrmgr_wakeup_intr_cg_wrap wakeup_intr_cg_wrap[pwrmgr_reg_pkg::NumWkups];
+
+ // This collects coverage on the clock control functionality.
+ covergroup clock_control_cg with function sample (bit core, bit io, bit usb_lp, bit usb_active);
+ core_cp: coverpoint core;
+ io_cp: coverpoint io;
+ usb_lp_cp: coverpoint usb_lp;
+ usb_active_cp: coverpoint usb_active;
+
+ control_cross: cross core_cp, io_cp, usb_lp_cp, usb_active_cp;
+ endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
- // [instantiate covergroups here]
+ foreach (wakeup_ctrl_cg_wrap[i]) begin
+ pwrmgr_env_pkg::wakeup_e wakeup = pwrmgr_env_pkg::wakeup_e'(i);
+ wakeup_ctrl_cg_wrap[i] = new({wakeup.name, "_ctrl_cg"});
+ wakeup_intr_cg_wrap[i] = new({wakeup.name, "_intr_cg"});
+ end
+ clock_control_cg = new();
endfunction : new
virtual function void build_phase(uvm_phase phase);
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_env_pkg.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_env_pkg.sv
index e0ce614..7e55074 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_env_pkg.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_env_pkg.sv
@@ -25,6 +25,14 @@
parameter string LIST_OF_ALERTS[] = {"fatal_fault"};
// types
+ typedef enum int {
+ WakeupSysrst,
+ WakeupDbgCable,
+ WakeupAon,
+ WakeupUsb,
+ WakeupAonTimer
+ } wakeup_e;
+
typedef struct packed {
logic core_clk_en;
logic io_clk_en;
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
index 8c50bf8..cc60c1f 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_if.sv
@@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
//
// pwrmgr interface.
-
+//
+// Samples some internal signals to help coverage collection:
interface pwrmgr_if (
input logic clk,
input logic rst_n,
@@ -51,19 +52,40 @@
// Relevant CSR values.
pwrmgr_env_pkg::clk_enables_t clk_enables;
+ logic wakeup_en_regwen;
logic [ pwrmgr_reg_pkg::NumWkups-1:0] wakeup_en;
logic [ pwrmgr_reg_pkg::NumWkups-1:0] wakeup_status;
+ logic wakeup_capture_en;
logic [pwrmgr_reg_pkg::NumRstReqs-1:0] reset_en;
logic [pwrmgr_reg_pkg::NumRstReqs-1:0] reset_status;
+ // Internal signals.
+`ifndef PATO_TO_DUT
+ `define PATH_TO_DUT tb.dut
+`endif
+
// Slow fsm state.
- pwrmgr_pkg::slow_pwr_state_e slow_state;
- always_comb slow_state = tb.dut.u_slow_fsm.state_q;
+ pwrmgr_pkg::slow_pwr_state_e slow_state;
+ always_comb slow_state = `PATH_TO_DUT.u_slow_fsm.state_q;
// Fast fsm state.
pwrmgr_pkg::fast_pwr_state_e fast_state;
- always_comb fast_state = tb.dut.i_fsm.state_q;
+ always_comb fast_state = `PATH_TO_DUT.i_fsm.state_q;
+
+ // Wakeup_status ro CSR.
+ logic [pwrmgr_reg_pkg::NumWkups-1:0] wake_status;
+ always_comb
+ wake_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,
+ `PATH_TO_DUT.hw2reg.wake_status[1].d,
+ `PATH_TO_DUT.hw2reg.wake_status[0].d
+ };
+
+ logic intr_enable;
+ always_comb intr_enable = `PATH_TO_DUT.reg2hw.intr_enable.q;
function automatic void update_ast_main_pok(logic value);
pwr_ast_rsp.main_pok = value;
@@ -93,10 +115,26 @@
pwr_cpu.core_sleeping = value;
endfunction
+ function automatic void update_wakeup_en_regwen(logic value);
+ wakeup_en_regwen &= value;
+ endfunction
+
+ function automatic void update_wakeup_en(logic [pwrmgr_reg_pkg::NumWkups-1:0] value);
+ wakeup_en = value;
+ endfunction
+
+ function automatic void update_wakeup_status(logic [pwrmgr_reg_pkg::NumWkups-1:0] value);
+ wakeup_status = value;
+ endfunction
+
function automatic void update_wakeups(logic [pwrmgr_reg_pkg::NumWkups-1:0] wakeups);
wakeups_i = wakeups;
endfunction
+ function automatic void update_wakeup_capture_dis(logic value);
+ wakeup_capture_en = !value;
+ endfunction
+
function automatic void update_resets(logic [pwrmgr_reg_pkg::NumRstReqs-1:0] resets);
rstreqs_i = resets;
endfunction
diff --git a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
index 95b3c6b..8a31886 100644
--- a/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
+++ b/hw/ip/pwrmgr/dv/env/pwrmgr_scoreboard.sv
@@ -29,9 +29,50 @@
super.run_phase(phase);
cfg.run_phase = phase;
fork
+ wakeup_ctrl_coverage_collector();
+ wakeup_intr_coverage_collector();
+ low_power_coverage_collector();
join_none
endtask
+ task wakeup_ctrl_coverage_collector();
+ forever
+ @(posedge cfg.pwrmgr_vif.wakeups_i) begin
+ if (cfg.en_cov) begin
+ 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,
+ cfg.pwrmgr_vif.wakeups_i[i], cfg.pwrmgr_vif.wakeup_status[i]);
+ end
+ end
+ end
+ endtask
+
+ task wakeup_intr_coverage_collector();
+ forever
+ @(posedge (cfg.pwrmgr_vif.fast_state == pwrmgr_pkg::FastPwrStateRomCheck)) begin
+ if (cfg.en_cov) begin
+ foreach (cov.wakeup_intr_cg_wrap[i]) begin
+ cov.wakeup_intr_cg_wrap[i].sample(cfg.pwrmgr_vif.wakeup_status[i],
+ cfg.pwrmgr_vif.intr_enable,
+ cfg.pwrmgr_vif.intr_wakeup);
+ end
+ end
+ end
+ endtask
+
+ task low_power_coverage_collector();
+ forever
+ @(posedge cfg.pwrmgr_vif.pwr_rst_req.reset_cause == pwrmgr_pkg::LowPwrEntry) begin
+ if (cfg.en_cov) begin
+ cov.clock_control_cg.sample(cfg.pwrmgr_vif.clk_enables.core_clk_en,
+ cfg.pwrmgr_vif.clk_enables.io_clk_en,
+ cfg.pwrmgr_vif.clk_enables.usb_clk_en_lp,
+ cfg.pwrmgr_vif.clk_enables.usb_clk_en_active);
+ end
+ end
+ endtask
+
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name);
uvm_reg csr;
bit do_read_check = 1'b1;
@@ -82,12 +123,15 @@
"control": begin
// Only some bits can be checked on reads. Bit 0 is cleared by hardware
// on low power transition or when registering a valid reset.
- cfg.pwrmgr_vif.update_clock_enables(
- '{core_clk_en: ral.control.core_clk_en.get_mirrored_value(),
- io_clk_en: ral.control.io_clk_en.get_mirrored_value(),
- usb_clk_en_lp: ral.control.usb_clk_en_lp.get_mirrored_value(),
- usb_clk_en_active: ral.control.usb_clk_en_active.get_mirrored_value(),
- main_pd_n: ral.control.main_pd_n.get_mirrored_value()});
+ if (data_phase_write) begin
+ bit core_clk_en = get_field_val(ral.control.core_clk_en, item.a_data);
+ bit io_clk_en = get_field_val(ral.control.io_clk_en, item.a_data);
+ bit usb_clk_en_lp = get_field_val(ral.control.usb_clk_en_lp, item.a_data);
+ bit usb_clk_en_active = get_field_val(ral.control.usb_clk_en_active, item.a_data);
+ bit main_pd_n = get_field_val(ral.control.main_pd_n, item.a_data);
+ cfg.pwrmgr_vif.update_clock_enables(
+ '{core_clk_en, io_clk_en, usb_clk_en_lp, usb_clk_en_active, main_pd_n});
+ end
end
"cfg_cdc_sync": begin
// rw1c: When written to 1 this bit self-clears when the slow clock domain
@@ -96,8 +140,14 @@
end
"wakeup_en_regwen": begin
// rw0c, so writing a 1 is a no-op.
+ if (data_phase_write) begin
+ cfg.pwrmgr_vif.update_wakeup_en_regwen(item.a_data);
+ end
end
"wakeup_en": begin
+ if (data_phase_write) begin
+ cfg.pwrmgr_vif.update_wakeup_en(item.a_data);
+ end
end
"wake_status": begin
// Read-only.
@@ -117,6 +167,9 @@
do_read_check = 1'b0;
end
"wake_info_capture_dis": begin
+ if (data_phase_write) begin
+ cfg.pwrmgr_vif.update_wakeup_capture_dis(item.a_data);
+ end
end
"wake_info": begin
// rw1c: write 1 clears, write 0 is no-op.
diff --git a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_smoke_vseq.sv b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_smoke_vseq.sv
index 442ed07..df58d80 100644
--- a/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_smoke_vseq.sv
+++ b/hw/ip/pwrmgr/dv/env/seq_lib/pwrmgr_smoke_vseq.sv
@@ -51,7 +51,8 @@
csr_rd_check(.ptr(ral.wake_status[0]), .compare_value(wakeups & wakeup_en),
.err_msg("failed wake_status check"));
- csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(0), .err_msg("failed reset_status check"));
+ csr_rd_check(.ptr(ral.reset_status[0]), .compare_value(0),
+ .err_msg("failed reset_status check"));
// Enable resets.
reset_en = '1;