From e4811216ffdd19e2a72ff60cf791245caf087091 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:17:13 +0800 Subject: [PATCH 1/3] deep sleep: fix regression due to moving ets_update_cpu_frequency into IRAM Deep sleep stub may call ets_update_cpu_frequency, which has been moved from ROM to IRAM. Restore the ROM version in the linker script, call it ets_update_cpu_frequency_rom, use it in the deep sleep stub. --- components/esp32/deep_sleep.c | 2 +- components/esp32/include/rom/ets_sys.h | 12 ++++++++++++ components/esp32/ld/esp32.rom.ld | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 7f59c21635..774fd4ce7a 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -91,7 +91,7 @@ void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) { #if CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY > 0 // ROM code has not started yet, so we need to set delay factor // used by ets_delay_us first. - ets_update_cpu_frequency(ets_get_detected_xtal_freq() / 1000000); + ets_update_cpu_frequency_rom(ets_get_detected_xtal_freq() / 1000000); // This delay is configured in menuconfig, it can be used to give // the flash chip some time to become ready. ets_delay_us(CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY); diff --git a/components/esp32/include/rom/ets_sys.h b/components/esp32/include/rom/ets_sys.h index 690691675a..0f972f2c33 100644 --- a/components/esp32/include/rom/ets_sys.h +++ b/components/esp32/include/rom/ets_sys.h @@ -383,6 +383,18 @@ void ets_delay_us(uint32_t us); */ void ets_update_cpu_frequency(uint32_t ticks_per_us); +/** + * @brief Set the real CPU ticks per us to the ets, so that ets_delay_us will be accurate. + * + * @note This function only sets the tick rate for the current CPU. It is located in ROM, + * so the deep sleep stub can use it even if IRAM is not initialized yet. + * + * @param uint32_t ticks_per_us : CPU ticks per us. + * + * @return None + */ +void ets_update_cpu_frequency_rom(uint32_t ticks_per_us); + /** * @brief Get the real CPU ticks per us to the ets. * This function do not return real CPU ticks per us, just the record in ets. It can be used to check with the real CPU frequency. diff --git a/components/esp32/ld/esp32.rom.ld b/components/esp32/ld/esp32.rom.ld index 33309894dc..7543fa42aa 100644 --- a/components/esp32/ld/esp32.rom.ld +++ b/components/esp32/ld/esp32.rom.ld @@ -202,7 +202,7 @@ PROVIDE ( ets_timer_init = 0x400084e8 ); PROVIDE ( ets_timer_setfn = 0x40008350 ); PROVIDE ( ets_unpack_flash_code = 0x40007018 ); PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); -/* PROVIDE ( ets_update_cpu_frequency = 0x40008550 ); */ /* Updates g_ticks_per_us on the current CPU only; not on the other core */ +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ PROVIDE ( ets_waiti0 = 0x400067d8 ); PROVIDE ( exc_cause_table = 0x3ff991d0 ); PROVIDE ( _exit_r = 0x4000bd28 ); From 9aa0e290796c2ee2142dac2c92dcbb1dc8a2d9f3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:23:23 +0800 Subject: [PATCH 2/3] deep sleep: keep RTC_SLOW_MEM powered on if data is placed into RTC slow memory --- components/esp32/deep_sleep.c | 18 ++++++++++++------ components/esp32/include/esp_attr.h | 2 +- components/esp32/ld/esp32.common.ld | 2 ++ docs/api/deep_sleep.rst | 2 ++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 774fd4ce7a..3eef7ca29d 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -302,12 +302,18 @@ static uint32_t get_power_down_flags() { // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. - // RTC_SLOW_MEM is needed only for the ULP. - // If RTC_SLOW_MEM is Auto, and ULP wakeup isn't enabled, power down RTC_SLOW_MEM. - if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO) { - if (s_config.wakeup_triggers & RTC_SAR_TRIG_EN) { - s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; - } + // RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP + // is used and RTC_SLOW_MEM is Auto. + // If there is any data placed into .rtc.data or .rtc.bss segments, and + // RTC_SLOW_MEM is Auto, keep it powered up as well. + + // These labels are defined in the linker script: + extern int _rtc_data_start, _rtc_data_end, _rtc_bss_start, _rtc_bss_end; + + if (s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] == ESP_PD_OPTION_AUTO || + &_rtc_data_end > &_rtc_data_start || + &_rtc_bss_end > &_rtc_bss_start) { + s_config.pd_options[ESP_PD_DOMAIN_RTC_SLOW_MEM] = ESP_PD_OPTION_ON; } // RTC_FAST_MEM is needed for deep sleep stub. diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 7ef2920d98..911201aace 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -26,7 +26,7 @@ // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) -// Forces a string into DRAM instrad of flash +// Forces a string into DRAM instead of flash // Use as ets_printf(DRAM_STR("Hello world!\n")); #define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 833eb90969..8cc3b5b22e 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -19,9 +19,11 @@ SECTIONS */ .rtc.data : { + _rtc_data_start = ABSOLUTE(.); *(.rtc.data) *(.rtc.rodata) *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); } > rtc_slow_seg /* RTC bss, from any source file named rtc_wake_stub*.c */ diff --git a/docs/api/deep_sleep.rst b/docs/api/deep_sleep.rst index 3a458fb4a8..9e6642fd90 100644 --- a/docs/api/deep_sleep.rst +++ b/docs/api/deep_sleep.rst @@ -73,6 +73,8 @@ By default, ``esp_deep_sleep_start`` function will power down all RTC power doma Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. +If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired. + .. doxygenfunction:: esp_deep_sleep_pd_config .. doxygenenum:: esp_deep_sleep_pd_domain_t .. doxygenenum:: esp_deep_sleep_pd_option_t From b24ac487cb49148ec17f2ab077fe72439de0f8e1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 11 Jan 2017 17:28:09 +0800 Subject: [PATCH 3/3] newlib: use RTC_STORE registers to keep boot time instead of RTC_SLOW_MEM This allows RTC_SLOW_MEM to be powered down in deep sleep if no other variables are placed into RTC_SLOW_MEM. --- components/esp32/Kconfig | 2 ++ components/esp32/include/rom/rtc.h | 14 ++++++---- components/newlib/time.c | 44 +++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index bc35ca0e8f..f0f74e9895 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -381,6 +381,8 @@ choice ESP32_TIME_SYSCALL longer to run. - If no timers are used, gettimeofday and time functions return -1 and set errno to ENOSYS. + - When RTC is used for timekeeping, two RTC_STORE registers are + used to keep time in deep sleep mode. config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" diff --git a/components/esp32/include/rom/rtc.h b/components/esp32/include/rom/rtc.h index 1ff7f033b9..e3c031775d 100644 --- a/components/esp32/include/rom/rtc.h +++ b/components/esp32/include/rom/rtc.h @@ -53,16 +53,18 @@ extern "C" { * Rtc store registers usage * RTC_CNTL_STORE0_REG * RTC_CNTL_STORE1_REG - * RTC_CNTL_STORE2_REG - * RTC_CNTL_STORE3_REG - * RTC_CNTL_STORE4_REG Reserved - * RTC_CNTL_STORE5_REG External Xtal Frequency + * RTC_CNTL_STORE2_REG Boot time, low word + * RTC_CNTL_STORE3_REG Boot time, high word + * RTC_CNTL_STORE4_REG External XTAL frequency + * RTC_CNTL_STORE5_REG APB bus frequency * RTC_CNTL_STORE6_REG FAST_RTC_MEMORY_ENTRY * RTC_CNTL_STORE7_REG FAST_RTC_MEMORY_CRC ************************************************************************************* */ -#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG -#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG +#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG +#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG +#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG +#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG typedef enum { diff --git a/components/newlib/time.c b/components/newlib/time.c index 363e17b3eb..c9fa72eeee 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "esp_attr.h" #include "esp_intr_alloc.h" #include "soc/soc.h" @@ -58,9 +59,9 @@ static uint64_t get_rtc_time_us() // s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC -static RTC_DATA_ATTR struct timeval s_boot_time; +// when RTC is used to persist time, two RTC_STORE registers are used to store boot time #elif defined(WITH_FRC1) -static struct timeval s_boot_time; +static uint64_t s_boot_time; #endif #if defined(WITH_RTC) || defined(WITH_FRC1) @@ -88,6 +89,31 @@ static void IRAM_ATTR frc_timer_isr() #endif // WITH_FRC1 +static void set_boot_time(uint64_t time_us) +{ + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + REG_WRITE(RTC_BOOT_TIME_LOW_REG, (uint32_t) (time_us & 0xffffffff)); + REG_WRITE(RTC_BOOT_TIME_HIGH_REG, (uint32_t) (time_us >> 32)); +#else + s_boot_time = time_us; +#endif + _lock_release(&s_boot_time_lock); +} + +static uint64_t get_boot_time() +{ + uint64_t result; + _lock_acquire(&s_boot_time_lock); +#ifdef WITH_RTC + result = ((uint64_t) REG_READ(RTC_BOOT_TIME_LOW_REG)) + (((uint64_t) REG_READ(RTC_BOOT_TIME_HIGH_REG)) << 32); +#else + result = s_boot_time; +#endif + _lock_release(&s_boot_time_lock); + return result; +} + void esp_setup_time_syscalls() { #if defined( WITH_FRC1 ) @@ -148,13 +174,10 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) - uint64_t microseconds = get_time_since_boot(); if (tv) { - _lock_acquire(&s_boot_time_lock); - microseconds += s_boot_time.tv_usec; - tv->tv_sec = s_boot_time.tv_sec + microseconds / 1000000; + uint64_t microseconds = get_boot_time() + get_time_since_boot(); + tv->tv_sec = microseconds / 1000000; tv->tv_usec = microseconds % 1000000; - _lock_release(&s_boot_time_lock); } return 0; #else @@ -168,14 +191,9 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz) (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) if (tv) { - _lock_acquire(&s_boot_time_lock); uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; uint64_t since_boot = get_time_since_boot(); - uint64_t boot_time = now - since_boot; - - s_boot_time.tv_sec = boot_time / 1000000; - s_boot_time.tv_usec = boot_time % 1000000; - _lock_release(&s_boot_time_lock); + set_boot_time(now - since_boot); } return 0; #else