sleep: optimize light sleep wakeup latency

This commit is contained in:
Ivan Grokhotkov 2018-04-04 15:05:50 +08:00
parent ac623a9756
commit 94250e42a0
4 changed files with 269 additions and 87 deletions

View file

@ -38,6 +38,7 @@ typedef enum {
ESP_PD_DOMAIN_RTC_PERIPH, //!< RTC IO, sensors and ULP co-processor ESP_PD_DOMAIN_RTC_PERIPH, //!< RTC IO, sensors and ULP co-processor
ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory
ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory
ESP_PD_DOMAIN_XTAL, //!< XTAL oscillator
ESP_PD_DOMAIN_MAX //!< Number of domains ESP_PD_DOMAIN_MAX //!< Number of domains
} esp_sleep_pd_domain_t; } esp_sleep_pd_domain_t;

View file

@ -17,6 +17,7 @@
#include <sys/param.h> #include <sys/param.h>
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_timer_impl.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_clk.h" #include "esp_clk.h"
#include "esp_newlib.h" #include "esp_newlib.h"
@ -42,6 +43,19 @@
// Time from VDD_SDIO power up to first flash read in ROM code // Time from VDD_SDIO power up to first flash read in ROM code
#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700 #define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700
// Extra time it takes to enter and exit light sleep and deep sleep
// For deep sleep, this is until the wake stub runs (not the app).
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
#define LIGHT_SLEEP_TIME_OVERHEAD_US (650 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#else
#define LIGHT_SLEEP_TIME_OVERHEAD_US (250 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#endif // CONFIG_ESP32_RTC_CLOCK_SOURCE
// Minimal amount of time we can sleep for
#define LIGHT_SLEEP_MIN_TIME_US 200
#define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \ #define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \
(source == value)) (source == value))
@ -56,9 +70,11 @@ typedef struct {
uint32_t ext1_rtc_gpio_mask : 18; uint32_t ext1_rtc_gpio_mask : 18;
uint32_t ext0_trigger_level : 1; uint32_t ext0_trigger_level : 1;
uint32_t ext0_rtc_gpio_num : 5; uint32_t ext0_rtc_gpio_num : 5;
} deep_sleep_config_t; uint32_t sleep_time_adjustment;
uint64_t rtc_ticks_at_sleep_start;
} sleep_config_t;
static deep_sleep_config_t s_config = { static sleep_config_t s_config = {
.pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO }, .pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO },
.wakeup_triggers = 0 .wakeup_triggers = 0
}; };
@ -125,12 +141,31 @@ void esp_deep_sleep(uint64_t time_in_us)
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
static void IRAM_ATTR suspend_uarts()
{
for (int i = 0; i < 3; ++i) {
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
uart_tx_wait_idle(i);
}
}
static void IRAM_ATTR resume_uarts()
{
for (int i = 0; i < 3; ++i) {
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
}
}
static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
{ {
// Flush UARTs so that output is not lost due to APB frequency change // Stop UART output so that output is not lost due to APB frequency change
uart_tx_wait_idle(0); suspend_uarts();
uart_tx_wait_idle(1);
uart_tx_wait_idle(2); // Save current frequency and switch to XTAL
rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
// Configure pins for external wakeup // Configure pins for external wakeup
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
@ -143,20 +178,32 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) { if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) {
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN); SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN);
} }
// Enter sleep
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags);
rtc_sleep_init(config);
// Configure timer wakeup // Configure timer wakeup
if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) && if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) &&
s_config.sleep_duration > 0) { s_config.sleep_duration > 0) {
timer_wakeup_prepare(); timer_wakeup_prepare();
} }
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0);
// Enter sleep // Restore CPU frequency
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags); rtc_clk_cpu_freq_set(cpu_freq);
rtc_sleep_init(config);
return rtc_sleep_start(s_config.wakeup_triggers, 0); // re-enable UART output
resume_uarts();
return result;
} }
void IRAM_ATTR esp_deep_sleep_start() void IRAM_ATTR esp_deep_sleep_start()
{ {
// record current RTC time
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
// Configure wake stub // Configure wake stub
if (esp_get_deep_sleep_wake_stub() == NULL) { if (esp_get_deep_sleep_wake_stub() == NULL) {
esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep);
@ -165,8 +212,11 @@ void IRAM_ATTR esp_deep_sleep_start()
// Decide which power domains can be powered down // Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags(); uint32_t pd_flags = get_power_down_flags();
// Correct the sleep time
s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US;
// Enter sleep // Enter sleep
esp_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags); esp_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | RTC_SLEEP_PD_XTAL | pd_flags);
// Because RTC is in a slower clock domain than the CPU, it // Because RTC is in a slower clock domain than the CPU, it
// can take several CPU cycles for the sleep mode to start. // can take several CPU cycles for the sleep mode to start.
@ -201,11 +251,11 @@ static void rtc_wdt_disable()
* Placed into IRAM as flash may need some time to be powered on. * Placed into IRAM as flash may need some time to be powered on.
*/ */
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us, uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline)); rtc_vddsdio_config_t vddsdio_config) IRAM_ATTR __attribute__((noinline));
static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
rtc_cpu_freq_t cpu_freq, uint32_t flash_enable_time_us, uint32_t flash_enable_time_us,
rtc_vddsdio_config_t vddsdio_config) rtc_vddsdio_config_t vddsdio_config)
{ {
// Enter sleep // Enter sleep
@ -217,9 +267,6 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
rtc_vddsdio_set_config(vddsdio_config); rtc_vddsdio_set_config(vddsdio_config);
} }
// Restore CPU frequency
rtc_clk_cpu_freq_set(cpu_freq);
// If SPI flash was powered down, wait for it to become ready // If SPI flash was powered down, wait for it to become ready
if (pd_flags & RTC_SLEEP_PD_VDDSDIO) { if (pd_flags & RTC_SLEEP_PD_VDDSDIO) {
// Wait for the flash chip to start up // Wait for the flash chip to start up
@ -231,53 +278,61 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
esp_err_t esp_light_sleep_start() esp_err_t esp_light_sleep_start()
{ {
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED; static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&light_sleep_lock); portENTER_CRITICAL(&light_sleep_lock);
int other_cpu = xPortGetCoreID() ? 0 : 1; s_config.rtc_ticks_at_sleep_start = rtc_time_get();
esp_cpu_stall(other_cpu); uint64_t frc_time_at_start = esp_timer_get_time();
DPORT_STALL_OTHER_CPU_START();
// Other CPU is stalled, need to disable DPORT protection
esp_dport_access_int_pause();
// Decide which power domains can be powered down // Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags(); uint32_t pd_flags = get_power_down_flags();
// Amount of time to subtract from actual sleep time.
// This is spent on entering and leaving light sleep.
s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US;
// Decide if VDD_SDIO needs to be powered down; // Decide if VDD_SDIO needs to be powered down;
// If it needs to be powered down, adjust sleep time. // If it needs to be powered down, adjust sleep time.
const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US
+ CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY; + CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY;
// Don't power down VDD_SDIO if pSRAM is used.
#ifndef CONFIG_SPIRAM_SUPPORT #ifndef CONFIG_SPIRAM_SUPPORT
if (s_config.sleep_duration > FLASH_PD_MIN_SLEEP_TIME_US && const uint32_t vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_TIME_US,
s_config.sleep_duration > flash_enable_time_us) { flash_enable_time_us + LIGHT_SLEEP_TIME_OVERHEAD_US + LIGHT_SLEEP_MIN_TIME_US);
if (s_config.sleep_duration > vddsdio_pd_sleep_duration) {
pd_flags |= RTC_SLEEP_PD_VDDSDIO; pd_flags |= RTC_SLEEP_PD_VDDSDIO;
s_config.sleep_duration -= flash_enable_time_us; s_config.sleep_time_adjustment += flash_enable_time_us;
} }
#endif //CONFIG_SPIRAM_SUPPORT #endif //CONFIG_SPIRAM_SUPPORT
rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config();
// Safety net: enable WDT in case exit from light sleep fails // Safety net: enable WDT in case exit from light sleep fails
rtc_wdt_enable(1000); rtc_wdt_enable(1000);
// Save current CPU frequency, light sleep will switch to XTAL
rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
// Enter sleep, then wait for flash to be ready on wakeup // Enter sleep, then wait for flash to be ready on wakeup
esp_err_t err = esp_light_sleep_inner(pd_flags, cpu_freq, esp_err_t err = esp_light_sleep_inner(pd_flags,
flash_enable_time_us, vddsdio_config); flash_enable_time_us, vddsdio_config);
// At this point, if FRC1 is used for timekeeping, time will be lagging behind. // FRC1 has been clock gated for the duration of the sleep, correct for that.
// This will update the microsecond count based on RTC timer. uint64_t rtc_ticks_at_end = rtc_time_get();
uint64_t frc_time_at_end = esp_timer_get_time();
uint64_t rtc_time_diff = rtc_time_slowclk_to_us(rtc_ticks_at_end - s_config.rtc_ticks_at_sleep_start,
esp_clk_slowclk_cal_get());
uint64_t frc_time_diff = frc_time_at_end - frc_time_at_start;
int64_t time_diff = rtc_time_diff - frc_time_diff;
/* Small negative values (up to 1 RTC_SLOW clock period) are possible,
* for very small values of sleep_duration. Ignore those to keep esp_timer
* monotonic.
*/
if (time_diff > 0) {
esp_timer_impl_advance(time_diff);
}
esp_set_time_from_rtc(); esp_set_time_from_rtc();
// However, we do not advance RTOS ticks here; doing so would be rather messy, DPORT_STALL_OTHER_CPU_END();
// as ticks can only be advanced on CPU0.
// If this is needed by the application, automatic light sleep (tickless idle)
// will handle that better.
esp_cpu_unstall(other_cpu);
esp_dport_access_int_resume();
rtc_wdt_disable(); rtc_wdt_disable();
portEXIT_CRITICAL(&light_sleep_lock); portEXIT_CRITICAL(&light_sleep_lock);
return err; return err;
@ -343,9 +398,13 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
static void timer_wakeup_prepare() static void timer_wakeup_prepare()
{ {
uint32_t period = esp_clk_slowclk_cal_get(); uint32_t period = esp_clk_slowclk_cal_get();
uint64_t rtc_count_delta = rtc_time_us_to_slowclk(s_config.sleep_duration, period); int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment;
uint64_t cur_rtc_count = rtc_time_get(); if (sleep_duration < 0) {
rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta); sleep_duration = 0;
}
int64_t rtc_count_delta = rtc_time_us_to_slowclk(sleep_duration, period);
rtc_sleep_set_wakeup_time(s_config.rtc_ticks_at_sleep_start + rtc_count_delta);
} }
esp_err_t esp_sleep_enable_touchpad_wakeup() esp_err_t esp_sleep_enable_touchpad_wakeup()
@ -561,6 +620,10 @@ static uint32_t get_power_down_flags()
} }
} }
if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] == ESP_PD_OPTION_AUTO) {
s_config.pd_options[ESP_PD_DOMAIN_XTAL] = ESP_PD_OPTION_OFF;
}
const char* option_str[] = {"OFF", "ON", "AUTO(OFF)" /* Auto works as OFF */}; const char* option_str[] = {"OFF", "ON", "AUTO(OFF)" /* Auto works as OFF */};
ESP_LOGD(TAG, "RTC_PERIPH: %s, RTC_SLOW_MEM: %s, RTC_FAST_MEM: %s", ESP_LOGD(TAG, "RTC_PERIPH: %s, RTC_SLOW_MEM: %s, RTC_FAST_MEM: %s",
option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH]], option_str[s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH]],
@ -578,5 +641,8 @@ static uint32_t get_power_down_flags()
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) { if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) {
pd_flags |= RTC_SLEEP_PD_RTC_PERIPH; pd_flags |= RTC_SLEEP_PD_RTC_PERIPH;
} }
if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] != ESP_PD_OPTION_ON) {
pd_flags |= RTC_SLEEP_PD_XTAL;
}
return pd_flags; return pd_flags;
} }

View file

@ -1,10 +1,16 @@
#include "unity.h" #include "unity.h"
#include <sys/time.h> #include <sys/time.h>
#include <sys/param.h>
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_clk.h"
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "soc/gpio_reg.h"
#include "soc/rtc.h"
#include "soc/uart_reg.h"
#include "rom/uart.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h"
#include "soc/rtc.h" // for wakeup trigger defines #include "soc/rtc.h" // for wakeup trigger defines
#include "soc/rtc_cntl_reg.h" // for read rtc registers directly (cause) #include "soc/rtc_cntl_reg.h" // for read rtc registers directly (cause)
#include "soc/soc.h" // for direct register read macros #include "soc/soc.h" // for direct register read macros
@ -14,10 +20,6 @@
static struct timeval tv_start, tv_stop; static struct timeval tv_start, tv_stop;
TEST_CASE("esp_deepsleep works", "[deepsleep][reset=DEEPSLEEP_RESET]")
{
esp_deep_sleep(2000000);
}
static void deep_sleep_task(void *arg) static void deep_sleep_task(void *arg)
{ {
@ -36,12 +38,19 @@ static void do_deep_sleep_from_app_cpu()
} }
} }
TEST_CASE("wake up using timer", "[deepsleep][reset=DEEPSLEEP_RESET]") TEST_CASE("wake up from deep sleep using timer", "[deepsleep][reset=DEEPSLEEP_RESET]")
{ {
esp_sleep_enable_timer_wakeup(2000000); esp_sleep_enable_timer_wakeup(2000000);
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
TEST_CASE("light sleep followed by deep sleep", "[deepsleep][reset=DEEPSLEEP_RESET]")
{
esp_sleep_enable_timer_wakeup(1000000);
esp_light_sleep_start();
esp_deep_sleep_start();
}
TEST_CASE("wake up from light sleep using timer", "[deepsleep]") TEST_CASE("wake up from light sleep using timer", "[deepsleep]")
{ {
esp_sleep_enable_timer_wakeup(2000000); esp_sleep_enable_timer_wakeup(2000000);
@ -54,6 +63,103 @@ TEST_CASE("wake up from light sleep using timer", "[deepsleep]")
TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt); TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt);
} }
static void test_light_sleep(void* arg)
{
vTaskDelay(2);
for (int i = 0; i < 1000; ++i) {
printf("%d %d\n", xPortGetCoreID(), i);
fflush(stdout);
esp_light_sleep_start();
}
SemaphoreHandle_t done = (SemaphoreHandle_t) arg;
xSemaphoreGive(done);
vTaskDelete(NULL);
}
TEST_CASE("light sleep stress test", "[deepsleep]")
{
SemaphoreHandle_t done = xSemaphoreCreateCounting(2, 0);
esp_sleep_enable_timer_wakeup(1000);
xTaskCreatePinnedToCore(&test_light_sleep, "ls0", 4096, done, UNITY_FREERTOS_PRIORITY + 1, NULL, 0);
#if portNUM_PROCESSORS == 2
xTaskCreatePinnedToCore(&test_light_sleep, "ls1", 4096, done, UNITY_FREERTOS_PRIORITY + 1, NULL, 1);
#endif
xSemaphoreTake(done, portMAX_DELAY);
#if portNUM_PROCESSORS == 2
xSemaphoreTake(done, portMAX_DELAY);
#endif
vSemaphoreDelete(done);
}
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
#define MAX_SLEEP_TIME_ERROR_US 200
#else
#define MAX_SLEEP_TIME_ERROR_US 100
#endif
TEST_CASE("light sleep duration is correct", "[deepsleep]")
{
// don't power down XTAL — powering it up takes different time on
// different boards
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
// run one light sleep without checking timing, to warm up the cache
esp_sleep_enable_timer_wakeup(1000);
esp_light_sleep_start();
const int sleep_intervals_ms[] = {
1, 1, 2, 3, 4, 5, 6, 7, 8, 10, 15,
20, 25, 50, 100, 200, 500,
};
const int sleep_intervals_count = sizeof(sleep_intervals_ms)/sizeof(sleep_intervals_ms[0]);
for (int i = 0; i < sleep_intervals_count; ++i) {
uint64_t sleep_time = sleep_intervals_ms[i] * 1000;
esp_sleep_enable_timer_wakeup(sleep_time);
for (int repeat = 0; repeat < 5; ++repeat) {
uint64_t start = esp_clk_rtc_time();
int64_t start_hs = esp_timer_get_time();
esp_light_sleep_start();
int64_t stop_hs = esp_timer_get_time();
uint64_t stop = esp_clk_rtc_time();
int diff_us = (int) (stop - start);
int diff_hs_us = (int) (stop_hs - start_hs);
printf("%lld %d\n", sleep_time, (int) (diff_us - sleep_time));
int32_t threshold = MAX(sleep_time / 100, MAX_SLEEP_TIME_ERROR_US);
TEST_ASSERT_INT32_WITHIN(threshold, sleep_time, diff_us);
TEST_ASSERT_INT32_WITHIN(threshold, sleep_time, diff_hs_us);
fflush(stdout);
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
TEST_CASE("light sleep and frequency switching", "[deepsleep]")
{
#ifndef CONFIG_PM_ENABLE
const int uart_clk_freq = REF_CLK_FREQ;
CLEAR_PERI_REG_MASK(UART_CONF0_REG(CONFIG_CONSOLE_UART_NUM), UART_TICK_REF_ALWAYS_ON);
uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
#endif
esp_sleep_enable_timer_wakeup(1000);
rtc_cpu_freq_t default_freq = rtc_clk_cpu_freq_get();
for (int i = 0; i < 1000; ++i) {
if (i % 2 == 0) {
rtc_clk_cpu_freq_set_fast(RTC_CPU_FREQ_XTAL);
} else {
rtc_clk_cpu_freq_set_fast(default_freq);
}
printf("%d\n", i);
fflush(stdout);
esp_light_sleep_start();
}
}
#ifndef CONFIG_FREERTOS_UNICORE #ifndef CONFIG_FREERTOS_UNICORE
TEST_CASE("enter deep sleep on APP CPU and wake up using timer", "[deepsleep][reset=DEEPSLEEP_RESET]") TEST_CASE("enter deep sleep on APP CPU and wake up using timer", "[deepsleep][reset=DEEPSLEEP_RESET]")
{ {
@ -138,7 +244,7 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]")
{ {
float dt = 0; float dt = 0;
printf("Setup timer and ext0 to wakeup imediately from GPIO_13 \n"); printf("Setup timer and ext0 to wake up immediately from GPIO_13 \n");
// Setup ext0 configuration to wake up almost immediately // Setup ext0 configuration to wake up almost immediately
// The wakeup time is proportional to input capacitance * pullup resistance // The wakeup time is proportional to input capacitance * pullup resistance
@ -159,7 +265,7 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]")
// Check wakeup from Ext0 using time measurement because wakeup cause is // Check wakeup from Ext0 using time measurement because wakeup cause is
// not available in light sleep mode // not available in light sleep mode
TEST_ASSERT_INT32_WITHIN(299, 300, (int) dt); TEST_ASSERT_INT32_WITHIN(100, 100, (int) dt);
TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0); TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0);
@ -175,7 +281,7 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]")
TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt); TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt);
// Additionaly check wakeup cause // Additionally check wakeup cause
TEST_ASSERT((get_cause() & RTC_TIMER_TRIG_EN) != 0); TEST_ASSERT((get_cause() & RTC_TIMER_TRIG_EN) != 0);
// Disable timer source. // Disable timer source.
@ -195,12 +301,11 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]")
dt = get_time_ms(); dt = get_time_ms();
printf("Ext0 sleep time = %d \n", (int) dt); printf("Ext0 sleep time = %d \n", (int) dt);
TEST_ASSERT_INT32_WITHIN(199, 200, (int) dt); TEST_ASSERT_INT32_WITHIN(100, 100, (int) dt);
TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0); TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0);
// Check error message when source is already disabled // Check error message when source is already disabled
esp_err_t err_code = esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); esp_err_t err_code = esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
TEST_ASSERT(err_code == ESP_ERR_INVALID_STATE); TEST_ASSERT(err_code == ESP_ERR_INVALID_STATE);
printf("Test case completed successfully.");
} }

View file

@ -29,16 +29,26 @@
#define MHZ (1000000) #define MHZ (1000000)
/* Various delays to be programmed into power control state machines */ /* Various delays to be programmed into power control state machines */
#define ROM_RAM_POWERUP_DELAY 3 #define RTC_CNTL_XTL_BUF_WAIT_SLP 2
#define ROM_RAM_WAIT_DELAY 3 #define RTC_CNTL_PLL_BUF_WAIT_SLP 2
#define WIFI_POWERUP_DELAY 3 #define RTC_CNTL_CK8M_WAIT_SLP 4
#define WIFI_WAIT_DELAY 3 #define OTHER_BLOCKS_POWERUP 1
#define RTC_POWERUP_DELAY 3 #define OTHER_BLOCKS_WAIT 1
#define RTC_WAIT_DELAY 3
#define DG_WRAP_POWERUP_DELAY 3 #define ROM_RAM_POWERUP_CYCLES OTHER_BLOCKS_POWERUP
#define DG_WRAP_WAIT_DELAY 3 #define ROM_RAM_WAIT_CYCLES OTHER_BLOCKS_WAIT
#define RTC_MEM_POWERUP_DELAY 3
#define RTC_MEM_WAIT_DELAY 3 #define WIFI_POWERUP_CYCLES OTHER_BLOCKS_POWERUP
#define WIFI_WAIT_CYCLES OTHER_BLOCKS_WAIT
#define RTC_POWERUP_CYCLES OTHER_BLOCKS_POWERUP
#define RTC_WAIT_CYCLES OTHER_BLOCKS_WAIT
#define DG_WRAP_POWERUP_CYCLES OTHER_BLOCKS_POWERUP
#define DG_WRAP_WAIT_CYCLES OTHER_BLOCKS_WAIT
#define RTC_MEM_POWERUP_CYCLES OTHER_BLOCKS_POWERUP
#define RTC_MEM_WAIT_CYCLES OTHER_BLOCKS_WAIT
/** /**
* @brief Power down flags for rtc_sleep_pd function * @brief Power down flags for rtc_sleep_pd function
@ -89,31 +99,31 @@ static void rtc_sleep_pd(rtc_sleep_pd_config_t cfg)
void rtc_sleep_init(rtc_sleep_config_t cfg) void rtc_sleep_init(rtc_sleep_config_t cfg)
{ {
//set 5 PWC state machine times to fit in main state machine time // set 5 PWC state machine times to fit in main state machine time
REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, 1); REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, RTC_CNTL_PLL_BUF_WAIT_SLP);
REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, RTC_CNTL_XTL_BUF_WAIT_DEFAULT); REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, RTC_CNTL_XTL_BUF_WAIT_SLP);
REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_SLP);
//set rom&ram timer
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_POWERUP_TIMER, ROM_RAM_POWERUP_DELAY);
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_WAIT_TIMER, ROM_RAM_WAIT_DELAY);
//set wifi timer
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, WIFI_POWERUP_DELAY);
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, WIFI_WAIT_DELAY);
//set rtc peri timer
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_POWERUP_TIMER, RTC_POWERUP_DELAY);
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_WAIT_TIMER, RTC_WAIT_DELAY);
//set digital wrap timer
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_POWERUP_TIMER, DG_WRAP_POWERUP_DELAY);
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_WAIT_TIMER, DG_WRAP_WAIT_DELAY);
//set rtc memory timer
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_POWERUP_TIMER, RTC_MEM_POWERUP_DELAY);
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_WAIT_TIMER, RTC_MEM_WAIT_DELAY);
if (cfg.lslp_mem_inf_fpu) { // set shortest possible sleep time limit
SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL, RTC_CNTL_MIN_SLP_VAL_MIN);
} else {
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); // set rom&ram timer
} REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_POWERUP_TIMER, ROM_RAM_POWERUP_CYCLES);
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_WAIT_TIMER, ROM_RAM_WAIT_CYCLES);
// set wifi timer
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, WIFI_POWERUP_CYCLES);
REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, WIFI_WAIT_CYCLES);
// set rtc peri timer
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_POWERUP_TIMER, RTC_POWERUP_CYCLES);
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_WAIT_TIMER, RTC_WAIT_CYCLES);
// set digital wrap timer
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_POWERUP_TIMER, DG_WRAP_POWERUP_CYCLES);
REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_WAIT_TIMER, DG_WRAP_WAIT_CYCLES);
// set rtc memory timer
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_POWERUP_TIMER, RTC_MEM_POWERUP_CYCLES);
REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_WAIT_TIMER, RTC_MEM_WAIT_CYCLES);
REG_SET_FIELD(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU, cfg.lslp_mem_inf_fpu);
rtc_sleep_pd_config_t pd_cfg = RTC_SLEEP_PD_CONFIG_ALL(cfg.lslp_meminf_pd); rtc_sleep_pd_config_t pd_cfg = RTC_SLEEP_PD_CONFIG_ALL(cfg.lslp_meminf_pd);
rtc_sleep_pd(pd_cfg); rtc_sleep_pd(pd_cfg);