blob: e320aef90f4373c32d20953a24a27551f83b6ea6 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// MIO Pads connection
// -------------------
//
// In this test, the vseq should assert the designated PAD to wake the device
// up from the sleep/ deep sleep state. The connections between the PADs and
// the interfaces are hard-coded. Refer tb/tb.sv chip_earlgrey_asic
// instantiation.
//
// Two approaches are possible here:
//
// 1. Trigger each interface op connected to the PAD. For instance, GPIO ports
// [0:11] are connected to [IOA0:IOA8], [IOB0:IOB2]. Then, call
// cfg.gpio_vif.drive_pin() to assert PADS.
//
// 2. Force the PAD inout port directly. This is easier as it does not require
// vif function call. Function call may differ for each interface.
//
class chip_sw_sleep_pin_wake_vseq extends chip_sw_base_vseq;
`uvm_object_utils(chip_sw_sleep_pin_wake_vseq)
`uvm_object_new
// Random delay (in ns)
// - Fast FSM to enter Low power : 18.8 us
// - Slow FSM to enter Low power : 278.7us
//
// delay should cover the edge case
rand int unsigned exit_delay;
constraint exit_delay_range_c {
// Distribution
exit_delay dist {
[ 15000: 20000] := 10, // trying to hit when Fast FSM enters LP
[ 20000:270000] := 2, // Other scenario (in the middle)
[270000:300000] := 10 // trying to hit when Slow FSM enters LP
};
}
// SW-randomized values.
top_earlgrey_pkg::pad_type_e pad_type;
int unsigned pad_idx;
virtual task body();
string printed_log;
super.body();
// Wait until we reach the SW test state.
`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInTest)
`DV_WAIT(cfg.sw_logger_vif.printed_log == "pinmux_init end")
// TODO: Get the PAD selection result from SW
// 1. MIO / DIO
// 2. MIO PAD SEL OR DIO PAD SEL
@(cfg.sw_logger_vif.printed_log_event);
printed_log = string'(cfg.sw_logger_vif.printed_log);
`DV_CHECK_STREQ(printed_log.substr(0, 9), "Pad Select")
// SW sends a log with chosen mode, pad formatted as below:
// Pad Selection: {pad_type} / {pad_idx}
pad_type = top_earlgrey_pkg::pad_type_e'(cfg.sw_logger_vif.printed_arg[0]);
pad_idx = cfg.sw_logger_vif.printed_arg[1];
`uvm_info(`gfn, $sformatf("%0s[%0d] is chosen for wakeup", pad_type.name(), pad_idx), UVM_LOW)
// Enable weak pulls on MIOs as well to avoid assertion errors.
cfg.chip_vif.disconnect_all_interfaces(.disconnect_default_pulls(0));
drive_pad(pad_type, pad_idx, 0);
`DV_WAIT(cfg.chip_vif.pwrmgr_low_power);
#(exit_delay * 1ns);
drive_pad(pad_type, pad_idx, 1);
// The SW test randomizes between normal and deep sleep. If deep sleep is chosen, the SW reboots
// after wake up and starts all over again, beginning from the ROM. The ROM requires the SW
// strap pins to be driven to legal values when it comes online, so we add this logic below.
// TODO: Find a better way to do this automatically.
fork
begin
`DV_WAIT(cfg.sw_test_status_vif.sw_test_status == SwTestStatusInBootRom)
cfg.chip_vif.sw_straps_if.drive(0);
end
// On low power exit, the ROM restarts execution. The testbench at some point, drives the SW
// strap pins again to legal values so that the ROM does not bomb. It may be possible that the
// pin chosen for wakeup is one of the same pins used for the SW straps. If the sw_straps_if
// drives those pins at the same time as mios_if, then it will result in X-prop. If we
// disconnect the mios_if too soon, then it will result in X-prop from the pinmux's (which is
// in the AON domain) wakeup detection logic. So, we need to disconnect the pin from mios_if
// at the same time the sw_straps_if is driven.
if (pad_type == top_earlgrey_pkg::MioPad &&
pad_idx inside {[top_earlgrey_pkg::MioPadIoc2:top_earlgrey_pkg::MioPadIoc0]}) begin
@(cfg.chip_vif.sw_straps_if.pins_oe);
drive_pad(pad_type, pad_idx, 1'bz);
end
join_none
endtask : body
virtual task post_start();
super.post_start();
// If we reached this point, the SW must have disabled the wakeup detection logic. It is now
// safe to undrive the pad.
drive_pad(pad_type, pad_idx, 1'bz);
endtask
function void drive_pad(top_earlgrey_pkg::pad_type_e pad_type, int unsigned pad_idx, logic value);
`uvm_info(`gfn, $sformatf("Driving %0s[%0d] to %0b", pad_type.name(), pad_idx, value), UVM_LOW)
case (pad_type)
top_earlgrey_pkg::DioPad: begin
`DV_CHECK(pad_idx inside {[0:top_earlgrey_pkg::DioCount-1]})
// DIO from the pinmux peripheral side needs to be mapped to the pad side. This mapping is
// provided by chip_common_pkg::DioToDioPadMap.
if (value === 1'bz) cfg.chip_vif.dios_if.drive_en_pin(DioToDioPadMap[pad_idx], 0);
else cfg.chip_vif.dios_if.drive_pin(DioToDioPadMap[pad_idx], value);
end
top_earlgrey_pkg::MioPad: begin
`DV_CHECK(pad_idx inside {[0:MioPadCount-1]})
if (value === 1'bz) cfg.chip_vif.mios_if.drive_en_pin(pad_idx, 0);
else cfg.chip_vif.mios_if.drive_pin(pad_idx, value);
end
default: `uvm_fatal(`gfn, $sformatf("Invalid pad type: %0d", pad_type))
endcase
endfunction
endclass : chip_sw_sleep_pin_wake_vseq