blob: 583dd226ea34ad270c5f1139946c286a42b7f533 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_aon_timer.h"
#include "sw/device/lib/dif/dif_pinmux.h"
#include "sw/device/lib/dif/dif_pwm.h"
#include "sw/device/lib/dif/dif_pwrmgr.h"
#include "sw/device/lib/dif/dif_rstmgr.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/aon_timer_testutils.h"
#include "sw/device/lib/testing/pwrmgr_testutils.h"
#include "sw/device/lib/testing/rstmgr_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "pwm_regs.h"
/**
* SLEEP PWM PULSES test
*
* This test configure 6 pwm channels with a fixed duty cycle
* then kicks power manager sleep mode to see pwm pluses are
* not affected by power down event.
* pwm out --> pinmux setup is chosen arbitrary as below
* pwmout[0] -> IOB10
* pwmout[1] -> IOB11
* pwmout[2] -> IOB12
* pwmout[3] -> IOc10
* pwmout[4] -> IOc11
* pwmout[5] -> IOc12
*
* Since purpose of this test is to check pwm -> pinmux
* connectivity and pulse integrity under sleep event,
* Fixed pwm configuration, mode and 0 phase delay are chosen.
*/
OTTF_DEFINE_TEST_CONFIG();
static const dif_pinmux_index_t kPinmuxOutsel[PWM_PARAM_N_OUTPUTS] = {
kTopEarlgreyPinmuxOutselPwmAonPwm0, kTopEarlgreyPinmuxOutselPwmAonPwm1,
kTopEarlgreyPinmuxOutselPwmAonPwm2, kTopEarlgreyPinmuxOutselPwmAonPwm3,
kTopEarlgreyPinmuxOutselPwmAonPwm4, kTopEarlgreyPinmuxOutselPwmAonPwm5,
};
static const dif_pinmux_index_t kPinmuxMioOut[PWM_PARAM_N_OUTPUTS] = {
kTopEarlgreyPinmuxMioOutIob10, kTopEarlgreyPinmuxMioOutIob11,
kTopEarlgreyPinmuxMioOutIob12, kTopEarlgreyPinmuxMioOutIoc10,
kTopEarlgreyPinmuxMioOutIoc11, kTopEarlgreyPinmuxMioOutIoc12,
};
static const dif_pwm_channel_t kPwmChannel[PWM_PARAM_N_OUTPUTS] = {
kDifPwmChannel0, kDifPwmChannel1, kDifPwmChannel2,
kDifPwmChannel3, kDifPwmChannel4, kDifPwmChannel5,
};
// Duty cycle in the unit of beat
// These are random numbers betwen [1,beats_per_pulse_cycle)
// make 'static volatile' to overwrite from
// hw/top_earlgrey/dv/env/seq_lib/chip_sw_pwm_pulses_vseq.sv
// via backdoor
static volatile const uint16_t kPwmDutycycle[PWM_PARAM_N_OUTPUTS] = {
6, 11, 27, 8, 17, 7,
};
static const dif_pwm_config_t config_ = {
// set beat period to 3
.clock_divisor = 2,
// upper 5bits of phase cntr only matter
// and total(on+off) beats per cycle will be 32
.beats_per_pulse_cycle = 32,
};
// This is initial value of config variable
static const dif_pwm_channel_config_t default_ch_cfg_ = {
.duty_cycle_a = 0,
.duty_cycle_b = 0,
.phase_delay = 0,
.mode = kDifPwmModeFirmware,
.polarity = kDifPwmPolarityActiveHigh,
.blink_parameter_x = 0,
.blink_parameter_y = 0,
};
// Configure pwm channel register for all 6 channels.
// This also contain disable and enable each channel.
void config_pwm_channels(dif_pwm_t *pwm) {
dif_pwm_channel_config_t channel_config_ = default_ch_cfg_;
for (int i = 0; i < PWM_PARAM_N_OUTPUTS; ++i) {
CHECK_DIF_OK(
dif_pwm_channel_set_enabled(pwm, kPwmChannel[i], kDifToggleDisabled));
channel_config_.duty_cycle_a = kPwmDutycycle[i];
CHECK_DIF_OK(
dif_pwm_configure_channel(pwm, kPwmChannel[i], channel_config_));
CHECK_DIF_OK(
dif_pwm_channel_set_enabled(pwm, kPwmChannel[i], kDifToggleEnabled));
}
}
bool test_main(void) {
dif_pwrmgr_t pwrmgr;
dif_rstmgr_t rstmgr;
// Issue a wakeup signal in ~150us through the AON timer.
//
// At 200kHz, threshold of 30 is equal to 150us. There is an additional
// ~4 cycle overhead for the CSR value to synchronize with the AON clock.
// We should expect the wake up to trigger in ~170us. This is sufficient
// time to allow pwrmgr config and the low power entry on WFI to complete.
//
// Adjust the threshold for Verilator since it runs on different clock
// frequencies.
uint32_t wakeup_threshold = 30;
if (kDeviceType == kDeviceSimVerilator) {
wakeup_threshold = 300;
}
// Initialize pwrmgr
CHECK_DIF_OK(dif_pwrmgr_init(
mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
// Initialize rstmgr since this will check some registers.
CHECK_DIF_OK(dif_rstmgr_init(
mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
dif_aon_timer_t aon_timer;
CHECK_DIF_OK(dif_aon_timer_init(
mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
// Assuming the chip hasn't slept yet, wakeup reason should be empty.
// Notice we are clearing rstmgr's RESET_INFO, so after the aon wakeup there
// is only one bit set.
if (pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0)) {
dif_pwm_t pwm;
dif_pinmux_t pinmux;
// Initialize pwm
CHECK_DIF_OK(dif_pwm_init(
mmio_region_from_addr(TOP_EARLGREY_PWM_AON_BASE_ADDR), &pwm));
// Update pwm.CFG
CHECK_DIF_OK(dif_pwm_configure(&pwm, config_));
// Update all 6 pwm channels
config_pwm_channels(&pwm);
// enable phase count to make the change effective
CHECK_DIF_OK(dif_pwm_phase_cntr_set_enabled(&pwm, kDifToggleEnabled));
// Initialize pinmux - this assigns PwmAonPwm[0..5] to
// IOB10..12 and IOC10..12
// LOG_INFO is used to indicate pwmout is available to
// SV pwm_monitor
CHECK_DIF_OK(dif_pinmux_init(
mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
LOG_INFO("pinmux_init begin");
for (int i = 0; i < PWM_PARAM_N_OUTPUTS; ++i) {
CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kPinmuxMioOut[i],
kPinmuxOutsel[i]));
}
LOG_INFO("pinmux_init end");
// Add 1ms to initial pulses go through before sleep event
busy_spin_micros(1 * 1000);
LOG_INFO("POR reset");
CHECK(rstmgr_testutils_reset_info_any(&rstmgr, kDifRstmgrResetInfoPor));
// Prepare rstmgr for a reset.
rstmgr_testutils_pre_reset(&rstmgr);
aon_timer_testutils_wakeup_config(&aon_timer, wakeup_threshold);
// Deep sleep.
pwrmgr_testutils_enable_low_power(&pwrmgr,
kDifPwrmgrWakeupRequestSourceFive, 0);
// Enter low power mode.
LOG_INFO("Issue WFI to enter sleep");
wait_for_interrupt();
} else if (pwrmgr_testutils_is_wakeup_reason(
&pwrmgr, kDifPwrmgrWakeupRequestSourceFive)) {
LOG_INFO("Wakeup reset");
CHECK(rstmgr_testutils_is_reset_info(&rstmgr,
kDifRstmgrResetInfoLowPowerExit));
LOG_INFO("Aon timer wakeup detected");
rstmgr_testutils_post_reset(&rstmgr, kDifRstmgrResetInfoLowPowerExit, 0, 0,
0, 0);
// add another 2ms to give more time to pwm pulses sequences
busy_spin_micros(2 * 1000);
return true;
} else {
dif_pwrmgr_wakeup_reason_t wakeup_reason;
CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason));
LOG_ERROR("Unexpected wakeup detected: type = %d, request_source = %d",
wakeup_reason.types, wakeup_reason.request_sources);
return false;
}
return false;
}