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