Deep sleep: Any source named rtc_wake_stub* is linked as RTC wake stub code

Also move esp_deepsleep.h documentation out to docs/deep-sleep-stub.rst
This commit is contained in:
Angus Gratton 2016-10-13 11:46:51 +11:00
parent 19db9ed870
commit da70611196
7 changed files with 134 additions and 54 deletions

View file

@ -19,6 +19,7 @@
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "rom/rtc.h"
#include "soc/cpu.h"
#include "soc/dport_reg.h"
@ -59,6 +60,8 @@ extern void app_main(void);
extern int _bss_start;
extern int _bss_end;
extern int _rtc_bss_start;
extern int _rtc_bss_end;
extern int _init_start;
extern void (*__init_array_start)(void);
extern void (*__init_array_end)(void);
@ -89,6 +92,11 @@ void IRAM_ATTR call_start_cpu0()
memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
/* Unless waking from deep sleep (implying RTC memory is intact), clear RTC bss */
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) {
memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start));
}
// Initialize heap allocator
heap_alloc_caps_init();

View file

@ -40,8 +40,7 @@ void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub)
}
void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) {
//
//mmu_init(0);
/* Clear MMU for CPU 0 */
REG_SET_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MMU_IA_CLR);
REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MMU_IA_CLR);
}

View file

@ -20,22 +20,21 @@
//and all variables in shared RAM. These macros can be used to redirect
//particular functions/variables to other memory regions.
// Forces code into IRAM instead of flash
// Forces code into IRAM instead of flash.
#define IRAM_ATTR __attribute__((section(".iram1")))
// Forces data into DRAM instead of flash
#define DRAM_ATTR __attribute__((section(".dram1")))
// Forces code into RTC fast memory
// Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst"
#define RTC_IRAM_ATTR __attribute__((section(".rtc.text")))
// Forces data into RTC slow memory
// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst"
// Any variable marked with this attribute will keep its value
// during a deep sleep / wake cycle.
#define RTC_DATA_ATTR __attribute__((section(".rtc.data")))
// Forces read-only data into RTC slow memory
// Makes constant data available to RTC wake stubs (see esp_deepsleep.h)
// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst"
#define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata")))
#endif /* __ESP_ATTR_H__ */

View file

@ -54,37 +54,7 @@ void system_deep_sleep(uint64_t time_in_us);
* to run code immediately when the chip wakes from
* sleep.
*
* For example:
* @code
* void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
* esp_default_wake_deep_sleep();
* // Add additional functionality here
* }
*
* (Implementing this function is not required for normal operation,
* in the usual case your app will start normally when waking from
* deep sleep.)
*
* esp_wake_deep_sleep() functionality is limited:
*
* - Runs immediately on wake, so most of the SoC is freshly reset -
* flash is unmapped and hardware is otherwise uninitialised.
*
* - Can only call functions implemented in ROM, or marked RTC_IRAM_ATTR.
*
* - Static variables marked RTC_DATA_ATTR will have initial values on
* cold boot, and maintain these values between sleep/wake cycles.
*
* - Read-only data should be marked RTC_RODATA_ATTR. Strings must be
* declared as variables also using RTC_RODATA_ATTR, like this:
* RTC_RODATA_ATTR const char message[] = "Hello from very early boot!\n";
*
* - Any other static memory will not be initialised (either to zero,
* or to any predefined value).
*
*
* - If you implement your own stub, the first call the stub makes
should be to esp_default_wake_deep_sleep().
* See docs/deep-sleep-stub.rst for details.
*/
void esp_wake_deep_sleep(void);
@ -115,9 +85,7 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void);
/* The default esp-idf-provided esp_wake_deep_sleep() stub.
If you replace esp_wake_deep_sleep() in your program, or use
esp_set_deep_sleep_wake_stub(), then it is recommended you call
esp_default_wake_deep_sleep() as the first function in your stub.
See docs/deep-sleep-stub.rst for details.
*/
void esp_default_wake_deep_sleep(void);

View file

@ -16,6 +16,7 @@
#define __ESP_SYSTEM_H__
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "esp_deepsleep.h"

View file

@ -3,8 +3,38 @@ ENTRY(call_start_cpu0);
SECTIONS
{
/* RTC fast memory holds RTC wake stub code,
including from any source file named rtc_wake_stub*.c
*/
.rtc.text :
{
. = ALIGN(4);
*(.rtc.literal .rtc.text)
*rtc_wake_stub*.o(.literal .text .literal.* .text.*)
} >rtc_iram_seg
/* RTC slow memory holds RTC wake stub
data/rodata, including from any source file
named rtc_wake_stub*.c
*/
.rtc.data :
{
*(.rtc.data)
*(.rtc.rodata)
*rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
} > rtc_slow_seg
/* RTC bss, from any source file named rtc_wake_stub*.c */
.rtc.bss (NOLOAD) :
{
_rtc_bss_start = ABSOLUTE(.);
*rtc_wake_stub*.o(.bss .bss.*)
*rtc_wake_stub*.o(COMMON)
_rtc_bss_end = ABSOLUTE(.);
} > rtc_slow_seg
/* Send .iram0 code to iram */
.iram0.vectors :
.iram0.vectors :
{
/* Vectors go to IRAM */
_init_start = ABSOLUTE(.);
@ -153,16 +183,4 @@ SECTIONS
_text_end = ABSOLUTE(.);
_etext = .;
} >iram0_2_seg
.rtc.text :
{
. = ALIGN(4);
*(.rtc.literal .rtc.text)
} >rtc_iram_seg
.rtc.data :
{
*(.rtc.data)
*(.rtc.rodata)
} > rtc_slow_seg
}

87
docs/deep-sleep-stub.rst Normal file
View file

@ -0,0 +1,87 @@
Deep Sleep Wake Stubs
---------------------
ESP32 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 initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally.
Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep.
Rules for Wake Stubs
====================
Wake stub code must be carefully written:
* As the SoC has freshly woken from sleep, most of the peripherals are in reset states. The SPI flash is unmapped.
* The wake stub code can only call functions implemented in ROM or loaded into RTC Fast Memory (see below.)
* The wake stub code can only access data loaded in RTC memory. All other RAM will be unintiailised and have random contents. The wake stub can use other RAM for temporary storage, but the contents will be overwritten when the SoC goes back to sleep or starts ESP-IDF.
* RTC memory must include any read-only data (.rodata) used by the stub.
* Data in RTC memory is initialised whenever the SoC restarts, except when waking from deep sleep. When waking from deep sleep, the values which were present before going to sleep are kept.
* Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app.
Implementing A Stub
===================
The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()` then this will override the default.
If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``.
It is not necessary to implement ``esp_wake_deep_sleep()`` in your app in order to use deep sleep. It is only necessary if you want to have special behaviour immediately on wake.
If you want to swap between different deep sleep stubs at runtime, it is also possible to do this by calling the ``esp_set_deep_sleep_wake_stub()`` function. This is not necessary if you only use the default ``esp_wake_deep_sleep()`` function.
All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32.
Loading Code Into RTC Memory
============================
Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways.
The first way is to use the ``RTC_IRAM_ATTR`` attribute to place a function into RTC memory::
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
// Add additional functionality here
}
The second way is to place the function into any source file whose name starts with ``rtc_wake_stub``. Files names ``rtc_wake_stub*`` have their contents automatically put into RTC memory by the linker.
The first way is simpler for very short and simple code, or for source files where you want to mix "normal" and "RTC" code. The second way is simpler when you want to write longer pieces of code for RTC memory.
Loading Data Into RTC Memory
============================
Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP.
Specifying this data can be done in one of two ways:
The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectivley) which should be loaded into RTC slow memory::
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";
ets_printf(fmt_str, wake_count++);
}
Unfortunately, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above.
The second way is to place the data into any source file whose name starts with ``rtc_wake_stub``.
For example, the equivalent example in ``rtc_wake_stub_counter.c``::
int wake_count;
void RTC_IRAM_ATTR esp_wake_deep_sleep(void) {
esp_default_wake_deep_sleep();
ets_printf("Wake count %d\n", wake_count++);
}
The second way is a better option if you need to use strings, or write other more complex code.