blob: 81c0bfd7653197cd4a0794dcf9498cd28da8c0ee [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// This sequence will verify the ultra low power feature. It detects the specific
// signal transition on pwrb_in, ac_present and lid_open pins, starts the timer
// configured in registers. Once the timing condition is met, ulp_status and wkup_status
// registers gets updated with the triggered event. Software will read and clear the register.
class sysrst_ctrl_ultra_low_pwr_vseq extends sysrst_ctrl_base_vseq;
`uvm_object_utils(sysrst_ctrl_ultra_low_pwr_vseq)
`uvm_object_new
rand uint16_t set_pwrb_timer, set_lid_timer, set_ac_timer;
rand int pwrb_cycles, ac_cycles, lid_cycles;
rand bit en_ulp;
// TODO: z3_wakeup check logic could move to scb.
uint16_t get_ac_timer, get_pwrb_timer, get_lid_timer;
bit enable_ulp, exp_z3_wakeup;
constraint set_pwrb_timer_c {
set_pwrb_timer dist {
[10:100] :/ 95,
[101:$] :/ 5
};
}
constraint set_lid_timer_c {
set_lid_timer dist {
[10:100] :/ 95,
[101:$] :/ 5
};
}
constraint set_ac_timer_c {
set_ac_timer dist {
[10:100] :/ 95,
[101:$] :/ 5
};
}
constraint pwrb_cycles_c { pwrb_cycles dist {
[1 : set_pwrb_timer] :/20,
(set_pwrb_timer + 1) :/20,
[set_pwrb_timer + 2 : set_pwrb_timer * 2] :/60
};
}
constraint lid_cycles_c { lid_cycles dist {
[1 : set_lid_timer] :/20,
(set_lid_timer + 1) :/20,
[set_lid_timer + 2 : set_lid_timer * 2] :/60
};
}
constraint ac_cycles_c { ac_cycles dist {
[1 : set_ac_timer] :/20,
(set_ac_timer + 1) :/20,
[set_ac_timer + 2 : set_ac_timer * 2] :/60
};
}
constraint num_trans_c {num_trans inside {[1 : 3]};}
task drive_ac();
cfg.vif.ac_present = 1;
for (int i = 0; i < ac_cycles; i++) begin
cfg.clk_aon_rst_vif.wait_clks(1);
if (exp_z3_wakeup == 0 && enable_ulp && i > (get_ac_timer + 1)) exp_z3_wakeup = 1;
end
cfg.vif.ac_present = 0;
endtask
task drive_pwrb();
cfg.vif.pwrb_in = 1;
cfg.clk_aon_rst_vif.wait_clks($urandom_range(1,20));
cfg.vif.pwrb_in = 0;
for (int i = 0; i < pwrb_cycles; i++) begin
cfg.clk_aon_rst_vif.wait_clks(1);
if (exp_z3_wakeup == 0 && enable_ulp && i > (get_pwrb_timer + 1)) exp_z3_wakeup = 1;
end
cfg.vif.pwrb_in = 1;
endtask
task drive_lid();
cfg.vif.lid_open = 0;
cfg.clk_aon_rst_vif.wait_clks($urandom_range(1,20));
cfg.vif.lid_open = 1;
for (int i = 0; i < lid_cycles; i++) begin
cfg.clk_aon_rst_vif.wait_clks(1);
if (exp_z3_wakeup == 0 && enable_ulp && i > (get_lid_timer + 1)) exp_z3_wakeup = 1;
end
cfg.vif.lid_open = 0;
endtask
virtual task check_z3_wkup_nonblocking();
fork
forever begin
@(posedge cfg.vif.z3_wakeup);
cfg.clk_aon_rst_vif.wait_n_clks(1);
`DV_CHECK(exp_z3_wakeup, 1)
end
join_none;
endtask
task body();
uvm_reg_data_t rdata, wkup_sts_rdata;
`uvm_info(`gfn, "Starting the body from ultra_low_pwr_vseq", UVM_LOW)
check_z3_wkup_nonblocking();
repeat (num_trans) begin
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(en_ulp)
// Enable ultra low power feature
ral.ulp_ctl.ulp_enable.set(en_ulp);
csr_update(ral.ulp_ctl);
// Set the debounce timer for pwrb, ac and lid_open
csr_wr(ral.ulp_ac_debounce_ctl, set_ac_timer);
csr_wr(ral.ulp_lid_debounce_ctl, set_lid_timer);
csr_wr(ral.ulp_pwrb_debounce_ctl, set_pwrb_timer);
csr_rd(ral.ulp_ac_debounce_ctl, get_ac_timer);
csr_rd(ral.ulp_pwrb_debounce_ctl, get_pwrb_timer);
csr_rd(ral.ulp_lid_debounce_ctl, get_lid_timer);
csr_rd(ral.ulp_ctl, rdata);
enable_ulp = get_field_val(ral.ulp_ctl.ulp_enable, rdata);
// Disable the bus clock
cfg.clk_rst_vif.stop_clk();
// It takes 2-3 clock cycles to sync the register values
cfg.clk_aon_rst_vif.wait_clks(2);
repeat ($urandom_range(1,5)) begin
fork
begin
cfg.clk_aon_rst_vif.wait_clks(1);
drive_pwrb();
end
begin
cfg.clk_aon_rst_vif.wait_clks(1);
drive_ac();
end
begin
cfg.clk_aon_rst_vif.wait_clks(1);
drive_lid();
end
join
// Wait until all debounce timer expires, so the state machines will be reset to Idle.
begin
int wait_cycles[$];
wait_cycles.push_back(cycles_to_finish_debounce(pwrb_cycles, set_pwrb_timer));
wait_cycles.push_back(cycles_to_finish_debounce(lid_cycles, set_lid_timer));
wait_cycles.push_back(cycles_to_finish_debounce(ac_cycles, set_ac_timer));
cfg.clk_aon_rst_vif.wait_clks($urandom_range(2, 10) + max(wait_cycles));
end
end
// Enable the bus clock to read the status register
cfg.clk_rst_vif.start_clk();
`uvm_info(`gfn, {$sformatf("enable_ulp=%0b, pwrb_cycles=%0d, pwrb_timer=%0d",
enable_ulp, pwrb_cycles, get_pwrb_timer),
$sformatf("ac_cycles=%0d, get_ac_timer=%0d", ac_cycles, get_ac_timer),
$sformatf("lid_cycles=%0d, lid_timer=%0d", lid_cycles, get_lid_timer)},
UVM_MEDIUM)
// (cycles == timer) => sysrst_ctrl_detect.state : DebounceSt -> IdleSt
// (cycles == timer + 1) => sysrst_ctrl_detect.state : DetectSt -> IdleSt
// Therefore we only need to check when cycles > timer + 1
if (enable_ulp == 1 &&
(pwrb_cycles > (get_pwrb_timer + 1) ||
ac_cycles > (get_ac_timer + 1) ||
lid_cycles > (get_lid_timer + 1))) begin
cfg.clk_aon_rst_vif.wait_clks(1);
`DV_CHECK_EQ(cfg.vif.z3_wakeup, 1);
csr_rd(ral.wkup_status, wkup_sts_rdata);
csr_rd_check(ral.wkup_status, .compare_value(1));
// Check if the ulp wakeup event is detected
csr_rd_check(ral.ulp_status, .compare_value(1));
// Clear the ulp_status register
csr_wr(ral.ulp_status, 'h1);
cfg.clk_aon_rst_vif.wait_clks(20);
// Check if the register is cleared
csr_rd_check(ral.ulp_status, .compare_value(0));
// Disable the ultra low power feature
ral.ulp_ctl.ulp_enable.set(0);
csr_update(ral.ulp_ctl);
// Clear the wkup_status register
csr_wr(ral.wkup_status, 'h1);
cfg.clk_aon_rst_vif.wait_clks(20);
// Check if the register is cleared
csr_rd_check(ral.wkup_status, .compare_value(0));
// Check z3_wakeup reset back to 0
`DV_CHECK_EQ(cfg.vif.z3_wakeup, 0);
exp_z3_wakeup = 0;
end else begin
`DV_CHECK_EQ(cfg.vif.z3_wakeup, 0);
`DV_CHECK_EQ(exp_z3_wakeup, 0);
csr_rd(ral.wkup_status,wkup_sts_rdata);
csr_rd_check(ral.wkup_status, .compare_value(0));
csr_rd_check(ral.ulp_status, .compare_value(0));
end
// Sample the wakeup event covergroup before clearing the status register
if (cfg.en_cov) begin
cov.wakeup_event.sysrst_ctrl_wkup_event_cg.sample(
get_field_val(ral.wkup_status.wakeup_sts, wkup_sts_rdata),
cfg.vif.pwrb_in,
cfg.vif.lid_open,
cfg.vif.ac_present,
cfg.intr_vif.pins
);
end
cfg.clk_aon_rst_vif.wait_clks($urandom_range(0, 10));
end
endtask : body
// A helper function to determine if the set time is long enough to cover the debounce state.
// If the set time is longer than the debounce time, return 0.
// If the set time if shorter than the debounce time, return the cycles required to finish
// the debounce state.
virtual function uint cycles_to_finish_debounce(int set_cycles, int debounce_timer);
return (set_cycles >= debounce_timer) ? 0 : (debounce_timer - set_cycles);
endfunction
endclass : sysrst_ctrl_ultra_low_pwr_vseq