From 0eb8d7e1850876a58188d459e03eff19096c7b49 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Sat, 16 Jan 2021 16:53:12 +1100 Subject: [PATCH 1/6] esp_hw_support esp32c3: Add deep sleep rtc crc calculation support Last step to enable deep sleep on ESP32-C3 in all configurations --- .../esp_hw_support/port/esp32c3/rtc_sleep.c | 91 ++++++++++++++++++- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/components/esp_hw_support/port/esp32c3/rtc_sleep.c b/components/esp_hw_support/port/esp32c3/rtc_sleep.c index 57dea490ea..c03b98295f 100644 --- a/components/esp_hw_support/port/esp32c3/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32c3/rtc_sleep.c @@ -24,8 +24,10 @@ #include "soc/nrx_reg.h" #include "soc/fe_reg.h" #include "soc/timer_group_reg.h" +#include "soc/system_reg.h" #include "soc/rtc.h" #include "esp32c3/rom/ets_sys.h" +#include "esp32c3/rom/rtc.h" #include "regi2c_ctrl.h" /** @@ -140,6 +142,8 @@ void rtc_sleep_set_wakeup_time(uint64_t t) WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t >> 32); } +static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu); + uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu) { REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); @@ -152,6 +156,88 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) { ; } + + return rtc_sleep_finish(lslp_mem_inf_fpu); +} + +#define STR2(X) #X +#define STR(X) STR2(X) + +uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) +{ + REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); + WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt); + + /* Calculate RTC Fast Memory CRC (for wake stub) & go to deep sleep + + Because we may be running from RTC memory as stack, we can't easily call any + functions to do this (as registers will spill to stack, corrupting the CRC). + + Instead, load all the values we need into registers then use register ops only to calculate + the CRC value, write it to the RTC CRC value register, and immediately go into deep sleep. + */ + + /* Values used to set the SYSTEM_RTC_FASTMEM_CONFIG_REG value */ + const unsigned CRC_START_ADDR = 0; + const unsigned CRC_LEN = 0x7ff; + + asm volatile( + /* Start CRC calculation */ + "sw %1, 0(%0)\n" // set RTC_MEM_CRC_ADDR & RTC_MEM_CRC_LEN + "or t0, %1, %2\n" + "sw t0, 0(%0)\n" // set RTC_MEM_CRC_START + + /* Wait for the CRC calculation to finish */ + ".Lwaitcrc:\n" + "fence\n" + "lw t0, 0(%0)\n" + "li t1, "STR(SYSTEM_RTC_MEM_CRC_FINISH)"\n" + "and t0, t0, t1\n" + "beqz t0, .Lwaitcrc\n" + "not %2, %2\n" // %2 -> ~DPORT_RTC_MEM_CRC_START + "and t0, t0, %2\n" + "sw t0, 0(%0)\n" // clear RTC_MEM_CRC_START + "fence\n" + "not %2, %2\n" // %2 -> DPORT_RTC_MEM_CRC_START, probably unnecessary but gcc assumes inputs unchanged + + /* Store the calculated value in RTC_MEM_CRC_REG */ + "lw t0, 0(%3)\n" + "sw t0, 0(%4)\n" + "fence\n" + + /* Set register bit to go into deep sleep */ + "lw t0, 0(%5)\n" + "or t0, t0, %6\n" + "sw t0, 0(%5)\n" + "fence\n" + + /* Wait for sleep reject interrupt (never finishes if successful) */ + ".Lwaitsleep:" + "fence\n" + "lw t0, 0(%7)\n" + "and t0, t0, %8\n" + "beqz t0, .Lwaitsleep\n" + + : + : + "r" (SYSTEM_RTC_FASTMEM_CONFIG_REG), // %0 + "r" ( (CRC_START_ADDR << SYSTEM_RTC_MEM_CRC_START_S) + | (CRC_LEN << SYSTEM_RTC_MEM_CRC_LEN_S)), // %1 + "r" (SYSTEM_RTC_MEM_CRC_START), // %2 + "r" (SYSTEM_RTC_FASTMEM_CRC_REG), // %3 + "r" (RTC_MEMORY_CRC_REG), // %4 + "r" (RTC_CNTL_STATE0_REG), // %5 + "r" (RTC_CNTL_SLEEP_EN), // %6 + "r" (RTC_CNTL_INT_RAW_REG), // %7 + "r" (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) // %8 + : "t0", "t1" // working registers + ); + + return rtc_sleep_finish(0); +} + +static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +{ /* In deep sleep mode, we never get here */ 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, @@ -164,8 +250,3 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp } return reject; } - -uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) -{ - abort(); // ESP32-C3 TODO IDF-2560 -} From 1cfd65f8c4bc26bc3a68c2d5b21be84c1a0fd1ae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Sat, 16 Jan 2021 16:53:43 +1100 Subject: [PATCH 2/6] examples: Update deep sleep example for ESP32-C3 --- examples/system/deep_sleep/main/Kconfig.projbuild | 3 +++ .../deep_sleep/main/deep_sleep_example_main.c | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/system/deep_sleep/main/Kconfig.projbuild b/examples/system/deep_sleep/main/Kconfig.projbuild index b8027878dc..98edb9769c 100644 --- a/examples/system/deep_sleep/main/Kconfig.projbuild +++ b/examples/system/deep_sleep/main/Kconfig.projbuild @@ -3,6 +3,7 @@ menu "Example Configuration" config EXAMPLE_TOUCH_WAKEUP bool "Enable touch wake up" default y + depends on !IDF_TARGET_ESP32C3 help This option enables wake up from deep sleep using touch pads TOUCH8 and TOUCH9, which correspond to GPIO33 and GPIO32. @@ -10,6 +11,7 @@ menu "Example Configuration" config EXAMPLE_ULP_TEMPERATURE_WAKEUP bool "Enable temperature monitoring by ULP" default y + depends on IDF_TARGET_ESP32 help This option enables wake up from deep sleep using ULP. ULP program monitors the on-chip temperature sensor and @@ -20,6 +22,7 @@ menu "Example Configuration" config EXAMPLE_EXT1_WAKEUP bool "Enable wakeup from GPIO" default y + depends on !IDF_TARGET_ESP32C3 help This option enables wake up from deep sleep from GPIO2 and GPIO4. They should be connected to LOW to avoid floating pins. When triggering a wake up, connect one or both of the pins to HIGH. Note that floating diff --git a/examples/system/deep_sleep/main/deep_sleep_example_main.c b/examples/system/deep_sleep/main/deep_sleep_example_main.c index f406b6e7de..035d0439b9 100644 --- a/examples/system/deep_sleep/main/deep_sleep_example_main.c +++ b/examples/system/deep_sleep/main/deep_sleep_example_main.c @@ -12,17 +12,25 @@ #include #include #include +#include "sdkconfig.h" +#include "soc/soc_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_sleep.h" #include "esp_log.h" -#include "esp32/ulp.h" -#include "driver/touch_pad.h" #include "driver/adc.h" #include "driver/rtc_io.h" -#include "soc/sens_periph.h" #include "soc/rtc.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/ulp.h" +#endif + +#if SOC_TOUCH_SENSOR_NUM > 0 +#include "soc/sens_periph.h" +#include "driver/touch_pad.h" +#endif + static RTC_DATA_ATTR struct timeval sleep_enter_time; #ifdef CONFIG_EXAMPLE_ULP_TEMPERATURE_WAKEUP From 4026e7b250847fa8c02f1824e7a1a907c803ee92 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Sat, 16 Jan 2021 16:58:55 +1100 Subject: [PATCH 3/6] esp_system esp32c3: Fix uart flush on entering deep sleep --- components/esp_system/sleep_modes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp_system/sleep_modes.c b/components/esp_system/sleep_modes.c index 7adcb468a9..8c1043fde3 100644 --- a/components/esp_system/sleep_modes.c +++ b/components/esp_system/sleep_modes.c @@ -227,7 +227,7 @@ static void IRAM_ATTR flush_uarts(void) for (int i = 0; i < SOC_UART_NUM; ++i) { #ifdef CONFIG_IDF_TARGET_ESP32 esp_rom_uart_tx_wait_idle(i); -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#else if (periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) { esp_rom_uart_tx_wait_idle(i); } From d3ffaf46840fee269b319ed4adb5b4490e22bd32 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Sat, 16 Jan 2021 17:08:34 +1100 Subject: [PATCH 4/6] esp_system: Add soc_caps guards in esp_sleep.h --- components/esp_system/include/esp_sleep.h | 11 +++++++++++ components/esp_system/sleep_modes.c | 8 ++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/components/esp_system/include/esp_sleep.h b/components/esp_system/include/esp_sleep.h index 6bc65b980a..4c247ce706 100644 --- a/components/esp_system/include/esp_sleep.h +++ b/components/esp_system/include/esp_sleep.h @@ -95,6 +95,7 @@ typedef esp_sleep_source_t esp_sleep_wakeup_cause_t; */ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source); +#if SOC_ULP_SUPPORTED /** * @brief Enable wakeup by ULP coprocessor * @note In revisions 0 and 1 of the ESP32, ULP wakeup source @@ -108,6 +109,8 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source); */ esp_err_t esp_sleep_enable_ulp_wakeup(void); +#endif // SOC_ULP_SUPPORTED + /** * @brief Enable wakeup by timer * @param time_in_us time before wakeup, in microseconds @@ -117,6 +120,8 @@ esp_err_t esp_sleep_enable_ulp_wakeup(void); */ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us); +#if SOC_TOUCH_SENSOR_NUM > 0 + /** * @brief Enable wakeup by touch sensor * @@ -144,6 +149,10 @@ esp_err_t esp_sleep_enable_touchpad_wakeup(void); */ touch_pad_t esp_sleep_get_touchpad_wakeup_status(void); +#endif // SOC_TOUCH_SENSOR_NUM > 0 + +#if SOC_PM_SUPPORT_EXT_WAKEUP + /** * @brief Returns true if a GPIO number is valid for use as wakeup source. * @@ -213,6 +222,8 @@ esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); */ esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode); +#endif // SOC_PM_SUPPORT_EXT_WAKEUP + /** * @brief Enable wakeup from light sleep using GPIOs * diff --git a/components/esp_system/sleep_modes.c b/components/esp_system/sleep_modes.c index 8c1043fde3..93e5ccd912 100644 --- a/components/esp_system/sleep_modes.c +++ b/components/esp_system/sleep_modes.c @@ -789,6 +789,7 @@ static void touch_wakeup_prepare(void) #endif #if SOC_TOUCH_SENSOR_NUM > 0 + esp_err_t esp_sleep_enable_touchpad_wakeup(void) { #if ((defined CONFIG_ESP32_RTC_EXT_CRYST_ADDIT_CURRENT) || (defined CONFIG_ESP32_RTC_EXT_CRYST_ADDIT_CURRENT_V2)) @@ -813,18 +814,20 @@ touch_pad_t esp_sleep_get_touchpad_wakeup_status(void) assert(ret == ESP_OK && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero"); return pad_num; } + #endif // SOC_TOUCH_SENSOR_NUM > 0 +#if SOC_PM_SUPPORT_EXT_WAKEUP + bool esp_sleep_is_valid_wakeup_gpio(gpio_num_t gpio_num) { #if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED return RTC_GPIO_IS_VALID_GPIO(gpio_num); #else return GPIO_IS_VALID_GPIO(gpio_num); -#endif +#endif // SOC_RTCIO_INPUT_OUTPUT_SUPPORTED } -#if SOC_PM_SUPPORT_EXT_WAKEUP esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) { if (level < 0 || level > 1) { @@ -931,6 +934,7 @@ uint64_t esp_sleep_get_ext1_wakeup_status(void) } return gpio_mask; } + #endif // SOC_PM_SUPPORT_EXT_WAKEUP esp_err_t esp_sleep_enable_gpio_wakeup(void) From b432fc8853dbf71e170886a7a910b9986b869013 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 18 Jan 2021 20:24:05 +1100 Subject: [PATCH 5/6] esp_system: Enable deep sleep reset reason test for ESP32-C3 --- components/esp_system/test/test_reset_reason.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp_system/test/test_reset_reason.c b/components/esp_system/test/test_reset_reason.c index 328bba4a41..4a0521c887 100644 --- a/components/esp_system/test/test_reset_reason.c +++ b/components/esp_system/test/test_reset_reason.c @@ -81,7 +81,7 @@ TEST_CASE("reset reason ESP_RST_POWERON", "[reset][ignore]") TEST_ASSERT_EQUAL(ESP_RST_POWERON, esp_reset_reason()); } -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3, ESP32C3) // TODO ESP32-C3 IDF-2560 +#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3) static void do_deep_sleep(void) { setup_values(); @@ -104,7 +104,7 @@ static void check_reset_reason_deep_sleep(void) TEST_CASE_MULTIPLE_STAGES("reset reason ESP_RST_DEEPSLEEP", "[reset_reason][reset="DEEPSLEEP"]", do_deep_sleep, check_reset_reason_deep_sleep); -#endif // TODO ESP32-C3 IDF-2560 +#endif // TEMPORARY_DISABLED_FOR_TARGETS static void do_exception(void) { From 241af87c5820dcccb4e5b3c734a71cbf998d3cf3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 18 Jan 2021 20:46:37 +1100 Subject: [PATCH 6/6] examples console: Fix parts of the code related to deep sleep on ESP32-C3 Example not yet working on ESP32-C3, crashes in _findenv_r --- .../console/components/cmd_system/cmd_system.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/system/console/components/cmd_system/cmd_system.c b/examples/system/console/components/cmd_system/cmd_system.c index c84847051a..6dbbedae61 100644 --- a/examples/system/console/components/cmd_system/cmd_system.c +++ b/examples/system/console/components/cmd_system/cmd_system.c @@ -190,8 +190,10 @@ static void register_tasks(void) static struct { struct arg_int *wakeup_time; +#if SOC_PM_SUPPORT_EXT_WAKEUP struct arg_int *wakeup_gpio_num; struct arg_int *wakeup_gpio_level; +#endif struct arg_end *end; } deep_sleep_args; @@ -208,6 +210,8 @@ static int deep_sleep(int argc, char **argv) ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout); ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) ); } + +#if SOC_PM_SUPPORT_EXT_WAKEUP if (deep_sleep_args.wakeup_gpio_num->count) { int io_num = deep_sleep_args.wakeup_gpio_num->ival[0]; if (!esp_sleep_is_valid_wakeup_gpio(io_num)) { @@ -226,26 +230,36 @@ static int deep_sleep(int argc, char **argv) io_num, level ? "HIGH" : "LOW"); ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) ); + ESP_LOGE(TAG, "GPIO wakeup from deep sleep currently unsupported on ESP32-C3"); } +#endif // SOC_PM_SUPPORT_EXT_WAKEUP rtc_gpio_isolate(GPIO_NUM_12); esp_deep_sleep_start(); } static void register_deep_sleep(void) { + int num_args = 1; deep_sleep_args.wakeup_time = arg_int0("t", "time", "", "Wake up time, ms"); +#if SOC_PM_SUPPORT_EXT_WAKEUP deep_sleep_args.wakeup_gpio_num = arg_int0(NULL, "io", "", "If specified, wakeup using GPIO with given number"); deep_sleep_args.wakeup_gpio_level = arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup"); - deep_sleep_args.end = arg_end(3); + num_args += 2; +#endif + deep_sleep_args.end = arg_end(num_args); const esp_console_cmd_t cmd = { .command = "deep_sleep", .help = "Enter deep sleep mode. " +#if SOC_PM_SUPPORT_EXT_WAKEUP "Two wakeup modes are supported: timer and GPIO. " +#else + "Timer wakeup mode is supported. " +#endif "If no wakeup option is specified, will sleep indefinitely.", .hint = NULL, .func = &deep_sleep,