diff --git a/components/esp32/esp_timer.c b/components/esp32/esp_timer.c index 67ae2dd727..ded2de8de1 100644 --- a/components/esp32/esp_timer.c +++ b/components/esp32/esp_timer.c @@ -467,6 +467,18 @@ esp_err_t esp_timer_dump(FILE* stream) return ESP_OK; } +int64_t IRAM_ATTR esp_timer_get_next_alarm() +{ + int64_t next_alarm = INT64_MAX; + timer_list_lock(); + esp_timer_handle_t it = LIST_FIRST(&s_timers); + if (it) { + next_alarm = it->alarm; + } + timer_list_unlock(); + return next_alarm; +} + int64_t IRAM_ATTR esp_timer_get_time() { return (int64_t) esp_timer_impl_get_time(); diff --git a/components/esp32/esp_timer_esp32.c b/components/esp32/esp_timer_esp32.c index 4c21910e8a..1bc4885756 100644 --- a/components/esp32/esp_timer_esp32.c +++ b/components/esp32/esp_timer_esp32.c @@ -311,6 +311,18 @@ void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us) portEXIT_CRITICAL_ISR(&s_time_update_lock); } +void esp_timer_impl_advance(int64_t time_us) +{ + assert(time_us > 0 && "negative adjustments not supported yet"); + + portENTER_CRITICAL(&s_time_update_lock); + uint64_t count = REG_READ(FRC_TIMER_COUNT_REG(1)); + REG_WRITE(FRC_TIMER_LOAD_REG(1), 0); + s_time_base_us += count / s_timer_ticks_per_us + time_us; + esp_timer_impl_set_alarm(esp_timer_get_next_alarm()); + portEXIT_CRITICAL(&s_time_update_lock); +} + esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler) { s_alarm_handler = alarm_handler; diff --git a/components/esp32/esp_timer_impl.h b/components/esp32/esp_timer_impl.h index 7d49e3eeb0..9c4642c474 100644 --- a/components/esp32/esp_timer_impl.h +++ b/components/esp32/esp_timer_impl.h @@ -60,6 +60,15 @@ void esp_timer_impl_set_alarm(uint64_t timestamp); */ void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); +/** + * @brief Adjust current esp_timer time by a certain value + * + * Called from light sleep code to synchronize esp_timer time with RTC time. + * + * @param time_us adjustment to apply to esp_timer time, in microseconds + */ +void esp_timer_impl_advance(int64_t time_us); + /** * @brief Get time, in microseconds, since esp_timer_impl_init was called * @return timestamp in microseconds diff --git a/components/esp32/include/esp_sleep.h b/components/esp32/include/esp_sleep.h index 2bdeac474f..a8bce5f796 100644 --- a/components/esp32/include/esp_sleep.h +++ b/components/esp32/include/esp_sleep.h @@ -38,6 +38,7 @@ typedef enum { 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_FAST_MEM, //!< RTC fast memory + ESP_PD_DOMAIN_XTAL, //!< XTAL oscillator ESP_PD_DOMAIN_MAX //!< Number of domains } esp_sleep_pd_domain_t; diff --git a/components/esp32/include/esp_timer.h b/components/esp32/include/esp_timer.h index 07e2372140..ff5c13ab4c 100644 --- a/components/esp32/include/esp_timer.h +++ b/components/esp32/include/esp_timer.h @@ -189,6 +189,13 @@ esp_err_t esp_timer_delete(esp_timer_handle_t timer); */ int64_t esp_timer_get_time(); +/** + * @brief Get the timestamp when the next timeout is expected to occur + * @return Timestamp of the nearest timer event, in microseconds. + * The timebase is the same as for the values returned by esp_timer_get_time. + */ +int64_t esp_timer_get_next_alarm(); + /** * @brief Dump the list of timers to a stream * diff --git a/components/esp32/sleep_modes.c b/components/esp32/sleep_modes.c index 1a9c449a2a..a5f14d7f2b 100644 --- a/components/esp32/sleep_modes.c +++ b/components/esp32/sleep_modes.c @@ -17,6 +17,7 @@ #include #include "esp_attr.h" #include "esp_sleep.h" +#include "esp_timer_impl.h" #include "esp_log.h" #include "esp_clk.h" #include "esp_newlib.h" @@ -42,6 +43,19 @@ // Time from VDD_SDIO power up to first flash read in ROM code #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) && \ (source == value)) @@ -56,9 +70,11 @@ typedef struct { uint32_t ext1_rtc_gpio_mask : 18; uint32_t ext0_trigger_level : 1; 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 }, .wakeup_triggers = 0 }; @@ -125,12 +141,31 @@ void esp_deep_sleep(uint64_t time_in_us) 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) { - // Flush UARTs so that output is not lost due to APB frequency change - uart_tx_wait_idle(0); - uart_tx_wait_idle(1); - uart_tx_wait_idle(2); + // Stop UART output so that output is not lost due to APB frequency change + suspend_uarts(); + + // 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 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) { 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 if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) && s_config.sleep_duration > 0) { timer_wakeup_prepare(); } + uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0); - // Enter sleep - rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags); - rtc_sleep_init(config); - return rtc_sleep_start(s_config.wakeup_triggers, 0); + // Restore CPU frequency + rtc_clk_cpu_freq_set(cpu_freq); + + // re-enable UART output + resume_uarts(); + + return result; } void IRAM_ATTR esp_deep_sleep_start() { + // record current RTC time + s_config.rtc_ticks_at_sleep_start = rtc_time_get(); + // Configure wake stub if (esp_get_deep_sleep_wake_stub() == NULL) { 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 uint32_t pd_flags = get_power_down_flags(); + // Correct the sleep time + s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US; + // 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 // 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. */ 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)); 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) { // Enter sleep @@ -217,9 +267,6 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags, 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 (pd_flags & RTC_SLEEP_PD_VDDSDIO) { // 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() { static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED; - portENTER_CRITICAL(&light_sleep_lock); - int other_cpu = xPortGetCoreID() ? 0 : 1; - esp_cpu_stall(other_cpu); - - // Other CPU is stalled, need to disable DPORT protection - esp_dport_access_int_pause(); + s_config.rtc_ticks_at_sleep_start = rtc_time_get(); + uint64_t frc_time_at_start = esp_timer_get_time(); + DPORT_STALL_OTHER_CPU_START(); // Decide which power domains can be powered down 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; // If it needs to be powered down, adjust sleep time. const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US + CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY; - // Don't power down VDD_SDIO if pSRAM is used. #ifndef CONFIG_SPIRAM_SUPPORT - if (s_config.sleep_duration > FLASH_PD_MIN_SLEEP_TIME_US && - s_config.sleep_duration > flash_enable_time_us) { + const uint32_t vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_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; - s_config.sleep_duration -= flash_enable_time_us; + s_config.sleep_time_adjustment += flash_enable_time_us; } #endif //CONFIG_SPIRAM_SUPPORT + rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); // Safety net: enable WDT in case exit from light sleep fails 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 - 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); - // At this point, if FRC1 is used for timekeeping, time will be lagging behind. - // This will update the microsecond count based on RTC timer. + // FRC1 has been clock gated for the duration of the sleep, correct for that. + 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(); - // However, we do not advance RTOS ticks here; doing so would be rather messy, - // 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(); + DPORT_STALL_OTHER_CPU_END(); rtc_wdt_disable(); portEXIT_CRITICAL(&light_sleep_lock); return err; @@ -343,9 +398,13 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) static void timer_wakeup_prepare() { uint32_t period = esp_clk_slowclk_cal_get(); - uint64_t rtc_count_delta = rtc_time_us_to_slowclk(s_config.sleep_duration, period); - uint64_t cur_rtc_count = rtc_time_get(); - rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta); + int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment; + if (sleep_duration < 0) { + 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() @@ -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 */}; 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]], @@ -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) { 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; } diff --git a/components/esp32/test/test_sleep.c b/components/esp32/test/test_sleep.c index fa1b902ac2..ac090fff98 100644 --- a/components/esp32/test/test_sleep.c +++ b/components/esp32/test/test_sleep.c @@ -1,10 +1,16 @@ #include "unity.h" #include +#include #include "esp_sleep.h" +#include "esp_clk.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/task.h" - +#include "freertos/semphr.h" #include "soc/rtc.h" // for wakeup trigger defines #include "soc/rtc_cntl_reg.h" // for read rtc registers directly (cause) #include "soc/soc.h" // for direct register read macros @@ -14,10 +20,6 @@ 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) { @@ -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_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]") { 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); } +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 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; - 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 // 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 // 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); @@ -175,7 +281,7 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]") 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); // Disable timer source. @@ -195,12 +301,11 @@ TEST_CASE("disable source trigger behavior", "[deepsleep]") dt = get_time_ms(); 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); // Check error message when source is already disabled esp_err_t err_code = esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); TEST_ASSERT(err_code == ESP_ERR_INVALID_STATE); - printf("Test case completed successfully."); } diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index ec9935fd57..86beea5342 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -427,7 +427,6 @@ void rtc_clk_wait_for_slow_cycle(); * @brief sleep configuration for rtc_sleep_init function */ typedef struct { - uint32_t soc_clk_sel : 2; //!< SoC clock select, see RTC_CNTL_SOC_CLK_SEL uint32_t lslp_mem_inf_fpu : 1; //!< force normal voltage in sleep mode (digital domain memory) uint32_t rtc_mem_inf_fpu : 1; //!< force normal voltage in sleep mode (RTC memory) uint32_t rtc_mem_inf_follow_cpu : 1;//!< keep low voltage in sleep mode (even if ULP/touch is used) @@ -444,6 +443,7 @@ typedef struct { uint32_t rtc_dbias_slp : 3; //!< set bias for RTC domain, in sleep mode uint32_t lslp_meminf_pd : 1; //!< remove all peripheral force power up flags uint32_t vddsdio_pd_en : 1; //!< power down VDDSDIO regulator + uint32_t xtal_fpu : 1; //!< keep main XTAL powered up in sleep } rtc_sleep_config_t; /** @@ -455,7 +455,6 @@ typedef struct { * @param RTC_SLEEP_PD_x flags combined using bitwise OR */ #define RTC_SLEEP_CONFIG_DEFAULT(sleep_flags) { \ - .soc_clk_sel = RTC_CNTL_SOC_CLK_SEL_XTL, \ .lslp_mem_inf_fpu = 0, \ .rtc_mem_inf_fpu = 0, \ .rtc_mem_inf_follow_cpu = ((sleep_flags) & RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU) ? 1 : 0, \ @@ -468,10 +467,11 @@ typedef struct { .wdt_flashboot_mod_en = 0, \ .dig_dbias_wak = RTC_CNTL_DBIAS_1V10, \ .dig_dbias_slp = RTC_CNTL_DBIAS_0V90, \ - .rtc_dbias_wak = RTC_CNTL_DBIAS_0V90, \ + .rtc_dbias_wak = RTC_CNTL_DBIAS_1V10, \ .rtc_dbias_slp = RTC_CNTL_DBIAS_0V90, \ .lslp_meminf_pd = 1, \ .vddsdio_pd_en = ((sleep_flags) & RTC_SLEEP_PD_VDDSDIO) ? 1 : 0, \ + .xtal_fpu = ((sleep_flags) & RTC_SLEEP_PD_XTAL) ? 0 : 1 \ }; #define RTC_SLEEP_PD_DIG BIT(0) //!< Deep sleep (power down digital domain) @@ -480,6 +480,7 @@ typedef struct { #define RTC_SLEEP_PD_RTC_FAST_MEM BIT(3) //!< Power down RTC FAST memory #define RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU BIT(4) //!< RTC FAST and SLOW memories are automatically powered up and down along with the CPU #define RTC_SLEEP_PD_VDDSDIO BIT(5) //!< Power down VDDSDIO regulator +#define RTC_SLEEP_PD_XTAL BIT(6) //!< Power down main XTAL /** * @brief Prepare the chip to enter sleep mode diff --git a/components/soc/esp32/include/soc/rtc_cntl_reg.h b/components/soc/esp32/include/soc/rtc_cntl_reg.h index d54a7dde7c..090b3f7072 100644 --- a/components/soc/esp32/include/soc/rtc_cntl_reg.h +++ b/components/soc/esp32/include/soc/rtc_cntl_reg.h @@ -1070,7 +1070,7 @@ #define RTC_CNTL_DBG_ATTEN_M ((RTC_CNTL_DBG_ATTEN_V)<<(RTC_CNTL_DBG_ATTEN_S)) #define RTC_CNTL_DBG_ATTEN_V 0x3 #define RTC_CNTL_DBG_ATTEN_S 24 - +#define RTC_CNTL_DBG_ATTEN_DEFAULT 3 #define RTC_CNTL_REG (DR_REG_RTCCNTL_BASE + 0x7c) /* RTC_CNTL_FORCE_PU : R/W ;bitpos:[31] ;default: 1'd1 ; */ /*description: RTC_REG force power up*/ diff --git a/components/soc/esp32/rtc_init.c b/components/soc/esp32/rtc_init.c index dfd0669087..43374d6b02 100644 --- a/components/soc/esp32/rtc_init.c +++ b/components/soc/esp32/rtc_init.c @@ -30,7 +30,7 @@ void rtc_init(rtc_config_t cfg) REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, cfg.xtal_wait); REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, cfg.ck8m_wait); - REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0x3); + REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, RTC_CNTL_DBG_ATTEN_DEFAULT); SET_PERI_REG_MASK(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DEC_HEARTBEAT_WIDTH | RTC_CNTL_INC_HEARTBEAT_PERIOD); diff --git a/components/soc/esp32/rtc_pm.c b/components/soc/esp32/rtc_pm.c index 2023bd824a..e5e7377259 100644 --- a/components/soc/esp32/rtc_pm.c +++ b/components/soc/esp32/rtc_pm.c @@ -48,7 +48,6 @@ pm_sw_reject_t pm_set_sleep_mode(pm_sleep_mode_t sleep_mode, void(*pmac_save_par } rtc_sleep_config_t cfg = { 0 }; - cfg.soc_clk_sel = RTC_CNTL_SOC_CLK_SEL_XTL; switch (sleep_mode) { case PM_LIGHT_SLEEP: diff --git a/components/soc/esp32/rtc_sleep.c b/components/soc/esp32/rtc_sleep.c index 82a04d8cab..041c2d1b6b 100644 --- a/components/soc/esp32/rtc_sleep.c +++ b/components/soc/esp32/rtc_sleep.c @@ -29,16 +29,26 @@ #define MHZ (1000000) /* Various delays to be programmed into power control state machines */ -#define ROM_RAM_POWERUP_DELAY 3 -#define ROM_RAM_WAIT_DELAY 3 -#define WIFI_POWERUP_DELAY 3 -#define WIFI_WAIT_DELAY 3 -#define RTC_POWERUP_DELAY 3 -#define RTC_WAIT_DELAY 3 -#define DG_WRAP_POWERUP_DELAY 3 -#define DG_WRAP_WAIT_DELAY 3 -#define RTC_MEM_POWERUP_DELAY 3 -#define RTC_MEM_WAIT_DELAY 3 +#define RTC_CNTL_XTL_BUF_WAIT_SLP 2 +#define RTC_CNTL_PLL_BUF_WAIT_SLP 2 +#define RTC_CNTL_CK8M_WAIT_SLP 4 +#define OTHER_BLOCKS_POWERUP 1 +#define OTHER_BLOCKS_WAIT 1 + +#define ROM_RAM_POWERUP_CYCLES OTHER_BLOCKS_POWERUP +#define ROM_RAM_WAIT_CYCLES OTHER_BLOCKS_WAIT + +#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 @@ -89,44 +99,31 @@ static void rtc_sleep_pd(rtc_sleep_pd_config_t cfg) void rtc_sleep_init(rtc_sleep_config_t cfg) { - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, cfg.soc_clk_sel); + // 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, RTC_CNTL_PLL_BUF_WAIT_SLP); + 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_SLP); - //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_XTL_BUF_WAIT, RTC_CNTL_XTL_BUF_WAIT_DEFAULT); - REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); - //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); + // set shortest possible sleep time limit + REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL, RTC_CNTL_MIN_SLP_VAL_MIN); - if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_PLL) { - REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, RTC_CNTL_PLL_BUF_WAIT_DEFAULT); - } else if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_XTL) { - ets_update_cpu_frequency(xtal_freq); - rtc_clk_apb_freq_update(xtal_freq * MHZ); - } else if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_8M) { - ets_update_cpu_frequency(8); - rtc_clk_apb_freq_update(8 * MHZ); - } + // 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); - if (cfg.lslp_mem_inf_fpu) { - SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); - } else { - CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); - } + 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(pd_cfg); @@ -198,6 +195,8 @@ void rtc_sleep_init(rtc_sleep_config_t cfg) REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0); } + REG_SET_FIELD(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, cfg.xtal_fpu); + /* enable VDDSDIO control by state machine */ REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE); REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en); @@ -230,5 +229,8 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR); + + /* restore DBG_ATTEN to the default value */ + REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, RTC_CNTL_DBG_ATTEN_DEFAULT); return reject; } diff --git a/tools/unit-test-app/components/unity/unity_platform.c b/tools/unit-test-app/components/unity/unity_platform.c index 0a33eb66cc..fa1adf6333 100644 --- a/tools/unit-test-app/components/unity/unity_platform.c +++ b/tools/unit-test-app/components/unity/unity_platform.c @@ -17,8 +17,6 @@ #include "esp_heap_trace.h" #endif -#define unity_printf ets_printf - // Pointers to the head and tail of linked list of test description structs: static struct test_desc_t* s_unity_tests_first = NULL; static struct test_desc_t* s_unity_tests_last = NULL; @@ -153,10 +151,10 @@ void unity_testcase_register(struct test_desc_t* desc) * */ static void print_multiple_function_test_menu(const struct test_desc_t* test_ms) { - unity_printf("%s\n", test_ms->name); + printf("%s\n", test_ms->name); for (int i = 0; i < test_ms->test_fn_count; i++) { - unity_printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]); + printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]); } } @@ -189,6 +187,10 @@ void multiple_function_option(const struct test_desc_t* test_ms) static void unity_run_single_test(const struct test_desc_t* test) { printf("Running %s...\n", test->name); + // Unit test runner expects to see test name before the test starts + fflush(stdout); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + Unity.TestFile = test->file; Unity.CurrentDetail1 = test->desc; if(test->test_fn_count == 1) { @@ -293,17 +295,17 @@ static void trim_trailing_space(char* str) static int print_test_menu(void) { int test_counter = 0; - unity_printf("\n\nHere's the test menu, pick your combo:\n"); + printf("\n\nHere's the test menu, pick your combo:\n"); for (const struct test_desc_t* test = s_unity_tests_first; test != NULL; test = test->next, ++test_counter) { - unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc); + printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc); if(test->test_fn_count > 1) { for (int i = 0; i < test->test_fn_count; i++) { - unity_printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]); + printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]); } } } @@ -324,7 +326,7 @@ static int get_test_count(void) void unity_run_menu() { - unity_printf("\n\nPress ENTER to see the list of tests.\n"); + printf("\n\nPress ENTER to see the list of tests.\n"); int test_count = get_test_count(); while (true) { diff --git a/tools/unit-test-app/unit_test.py b/tools/unit-test-app/unit_test.py index 4ea7ddf579..594a71becf 100644 --- a/tools/unit-test-app/unit_test.py +++ b/tools/unit-test-app/unit_test.py @@ -142,7 +142,7 @@ def run_unit_test_cases(env, extra_data): # to determine if DUT is ready to test. dut.write("-", flush=False) dut.expect_any(UT_APP_BOOT_UP_DONE, - "0 Tests 0 Failures 0 Ignored") + "0 Tests 0 Failures 0 Ignored", timeout=UT_TIMEOUT) # run test case dut.write("\"{}\"".format(one_case["name"]))