Deep Sleep Wake Stubs

Introduction

The wakeup time from Deep-sleep mode is much longer, compared to Light-sleep and Modem-sleep modes as ROM and RAM are both powered down in this case, and the CPU needs more time for SPI booting. However, ESP32-C3 supports running a “deep sleep wake stub” when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialization, bootloader, or ESP-IDF code has run.

Specifically, after waking up from Deep-sleep mode, ESP32-C3 starts partial initialization. Then RTC fast memory will be validated with CRC. If validation passes, the wake stub code will be executed.

As ESP32-C3 has just woken up from deep sleep, most of the peripherals are in the reset state. The SPI flash has not been mapped. Thus, wake stub code can only call functions implemented in ROM or loaded into RTC fast memory, which retains content during deep sleep.

From the above, utilizing the wake stub functionality in an application you can quickly run some code when waking up from Deep-sleep mode, without having to wait for the whole boot-up process. However, the stub size is restricted by the size of RTC fast memory.

ESP32-C3 only supports RTC fast memory. The wake stub code and data that it utilizes should be loaded into RTC fast memory.

Next we will introduce how to implement the wake stub code in an application.

Implement wake stub

The wake stub in esp-idf is realized by the function esp_wake_deep_sleep(). This function is executed whenever the SoC wakes from deep sleep. As this function is weakly-linked to the default function esp_default_wake_deep_sleep(), if your application contains a function with the name esp_wake_deep_sleep(), the default version esp_default_wake_deep_sleep() in esp-idf will be overridden.

Please note that implementing the function esp_wake_deep_sleep() in your application is not mandatory for utilizing the deep sleep functionality. It becomes necessary only if you want to introduce certain behavior immediately upon the SoC's wake-up.

When you develop a customized wake stub, the first step it should do is to call the default function esp_default_wake_deep_sleep().

In addition, you can switch between different wake stubs by calling the function esp_set_deep_sleep_wake_stub() during runtime.

Implementing the wake stub function in your application includes the following steps:

  • Loading wake stub code into RTC fast memory

  • Loading data into RTC fast memory

Load Wake Stub Code into RTC Fast Memory

The wake stub code can only call functions present in ROM or loaded into RTC fast memory. All other RAM locations are unintiailized and contain random data. While the wake stub code can use other RAM areas for temporary storage, the contents of these areas will be overwritten either upon returning to deep sleep mode or upon initiating esp-idf.

Wake stub code is a part of the main esp-idf application. During regular execution of esp-idf, functions can call the wake stub code or access RTC memory, treating them as a regular part of the application.

Wake stub code must reside in RTC fast memory. This can be realized in two ways.

void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
    esp_default_wake_deep_sleep();
    // Add additional functionality here
}

The first approach is suitable for short and simple code segments or for source files including both "normal" and "RTC" code.

  • Place the function esp_wake_deep_sleep() into any source file with name starting with rtc_wake_stub. For files with such names rtc_wake_stub*, their contents can be automatically put into RTC fast memory by the linker.

The second method is preferable when writing longer code segments in RTC fast memory.

However, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above.

if (s_count >= s_max_count) {
    // Reset s_count
    s_count = 0;

    // Set the default wake stub.
    // There is a default version of this function provided in esp-idf.
    esp_default_wake_deep_sleep();

    // Return from the wake stub function to continue
    // booting the firmware.
    return;
}

The second approach is advisable when incorporating strings or more complex code segments.

You can enable the Kconfig option CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP to reduce wake-up time. See more information in Fast boot from Deep Sleep.

All of the above functions are declared in esp_hw_support/include/esp_sleep.h.

Load Wake Stub Data into RTC Fast memory

Data used by wake stub code must be resident in RTC fast memory.

The data can be specified in the following two methods:

  • Use the RTC_DATA_ATTR and RTC_RODATA_ATTR to specify writable or read-only data, respectively.

RTC_DATA_ATTR int wake_count;

void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
    esp_default_wake_deep_sleep();
    static RTC_RODATA_ATTR const char fmt_str[] = "Wake count %d\n";
    esp_rom_printf(fmt_str, wake_count++);
}

The attributes RTC_FAST_ATTR and RTC_SLOW_ATTR can be used to specify data that will be force placed into RTC fast memory and RTC slow memory respectively. However, ESP32-C3 includes RTC fast memory only, so both these two attributes will map to this region.

Example

ESP-IDF provides an example to show how to implement the deep sleep wake stub.