esp32: Bootloader wake deep sleep stub

App can contain a stub program resident in RTC fast memory. Bootloader
will load the stub on initial boot. If the device wakes from deep sleep,
the stub is run immediately (before any other data is loaded, etc.)

To implement a custom wake stub, implement a function in your program:

```
void RTC_IRAM_ATTR esp_wake_deep_sleep(void)
{
  esp_default_wake_deep_sleep();
  // other wake logic
}
```

... and it will replace the default implementation.
This commit is contained in:
Angus Gratton 2016-09-12 17:23:15 +10:00 committed by Wu Jian Gang
parent b77bf9bc90
commit 7c494055e3
12 changed files with 264 additions and 34 deletions

View file

@ -30,6 +30,10 @@ extern "C"
#define IROM_HIGH 0x40400000
#define DROM_LOW 0x3F400000
#define DROM_HIGH 0x3F800000
#define RTC_IRAM_LOW 0x400C0000
#define RTC_IRAM_HIGH 0x400C2000
#define RTC_DATA_LOW 0x50000000
#define RTC_DATA_HIGH 0x50002000
/*spi mode,saved in third byte in flash */
enum {

View file

@ -22,6 +22,7 @@
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
#include "rom/crc.h"
#include "rom/rtc.h"
#include "soc/soc.h"
#include "soc/cpu.h"
@ -362,11 +363,15 @@ void unpack_load_app(const partition_pos_t* partition)
uint32_t irom_load_addr = 0;
uint32_t irom_size = 0;
/* Reload the RTC memory sections whenever a non-deepsleep reset
is occuring */
bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET;
ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic,
image_header.blocks,
image_header.spi_mode,
image_header.spi_size,
(unsigned)image_header.entry_addr);
image_header.blocks,
image_header.spi_mode,
image_header.spi_size,
(unsigned)image_header.entry_addr);
for (uint32_t section_index = 0;
section_index < image_header.blocks;
@ -406,7 +411,18 @@ void unpack_load_app(const partition_pos_t* partition)
map = true;
}
ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos, section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":"");
if(!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) {
ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", pos);
load = false;
}
if(!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) {
ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", pos);
load = false;
}
ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos,
section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":"");
if (!load) {
pos += section_header.data_len;

View file

@ -42,6 +42,7 @@
#include "esp_spi_flash.h"
#include "esp_ipc.h"
#include "esp_log.h"
#include "esp_deepsleep.h"
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
void start_cpu0_default(void) IRAM_ATTR;

View file

@ -0,0 +1,45 @@
/* Wake from deep sleep stub
See esp_deepsleep.h esp_wake_deep_sleep() comments for details.
*/
#include <stddef.h>
#include <sys/lock.h>
#include "rom/cache.h"
#include "rom/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_attr.h"
#include "esp_deepsleep.h"
/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc()
is not thread-safe. */
static _lock_t lock_rtc_memory_crc;
esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void)
{
_lock_acquire(&lock_rtc_memory_crc);
uint32_t stored_crc = REG_READ(RTC_MEMORY_CRC_REG);
set_rtc_memory_crc();
uint32_t calc_crc = REG_READ(RTC_MEMORY_CRC_REG);
REG_WRITE(RTC_MEMORY_CRC_REG, stored_crc);
_lock_release(&lock_rtc_memory_crc);
if(stored_crc == calc_crc) {
return (esp_deep_sleep_wake_stub_fn_t)REG_READ(RTC_ENTRY_ADDR_REG);
} else {
return NULL;
}
}
void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub)
{
_lock_acquire(&lock_rtc_memory_crc);
REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)new_stub);
set_rtc_memory_crc();
_lock_release(&lock_rtc_memory_crc);
}
void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) {
mmu_init(0);
}
void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void);

View file

@ -17,8 +17,8 @@
#define ROMFN_ATTR
//Normally, the linker script will put all code and rodata in flash,
//and all variables in shared RAM. This can be redirected to IRAM if
//needed using these macros.
//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
#define IRAM_ATTR __attribute__((section(".iram1")))
@ -26,4 +26,16 @@
// Forces data into DRAM instead of flash
#define DRAM_ATTR __attribute__((section(".dram1")))
// Forces code into RTC fast memory
#define RTC_IRAM_ATTR __attribute__((section(".rtc.text")))
// Forces data into RTC slow memory
// 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)
#define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata")))
#endif /* __ESP_ATTR_H__ */

View file

@ -0,0 +1,137 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_DEEPSLEEP_H__
#define __ESP_DEEPSLEEP_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup Deep_Sleep_API Deep Sleep API
* @brief API for putting device into deep sleep
*/
/** @addtogroup Deep_Sleep_API
* @{
*/
/**
* @brief Set the chip to deep-sleep mode.
*
* The device will automatically wake up after the deep-sleep time set
* by the users. Upon waking up, the device boots up from user_init.
*
* @attention The parameter time_in_us to be "uint64" is for further development.
* Only the low 32 bits of parameter time_in_us are avalable now.
*
* @param uint64 time_in_us : deep-sleep time, only the low 32bits are avalable now. unit: microsecond
*
* @return null
*/
void system_deep_sleep(uint64_t time_in_us);
/**
* @brief Default stub to run on wake from deep sleep.
*
* Allows for executing code immediately on wake from sleep, before
* the software bootloader or esp-idf app has started up.
*
* This function is weak-linked, so you can implement your own version
* 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().
*/
void esp_wake_deep_sleep(void);
/**
* @brief Function type for stub to run on wake from sleep.
*
*/
typedef void (*esp_deep_sleep_wake_stub_fn_t)(void);
/**
* @brief Install a new stub at runtime to run on wake from deep sleep
*
* If implementing esp_wake_deep_sleep() then it is not necessary to
* call this function.
*
* However, it is possible to call this function to substitute a
* different deep sleep stub. Any function used as a deep sleep stub
* must be marked RTC_IRAM_ATTR, and must obey the same rules given
* for esp_wake_deep_sleep().
*/
void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub);
/**
* @brief Return current wake from deep sleep stub, or NULL if
* no stub is installed.
*/
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.
*/
void esp_default_wake_deep_sleep(void);
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __ESP_SYSTEM_H__ */

View file

@ -62,21 +62,6 @@ void system_restore(void);
*/
void system_restart(void);
/**
* @brief Set the chip to deep-sleep mode.
*
* The device will automatically wake up after the deep-sleep time set
* by the users. Upon waking up, the device boots up from user_init.
*
* @attention The parameter time_in_us to be "uint64" is for further development.
* Only the low 32 bits of parameter time_in_us are avalable now.
*
* @param uint64 time_in_us : deep-sleep time, only the low 32bits are avalable now. unit: microsecond
*
* @return null
*/
void system_deep_sleep(uint64_t time_in_us);
/**
* @brief Get system time, unit: microsecond.
*

View file

@ -51,18 +51,18 @@ extern "C" {
*
*************************************************************************************
* Rtc store registers usage
* RTC_STORE0
* RTC_STORE1
* RTC_STORE2
* RTC_STORE3
* RTC_STORE4 Reserved
* RTC_STORE5 External Xtal Frequency
* RTC_STORE6 FAST_RTC_MEMORY_ENTRY
* RTC_STORE7 FAST_RTC_MEMORY_CRC
* 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_STORE6_REG FAST_RTC_MEMORY_ENTRY
* RTC_CNTL_STORE7_REG FAST_RTC_MEMORY_CRC
*************************************************************************************
*/
#define RTC_ENTRY_ADDR RTC_STORE6
#define RTC_MEMORY_CRC RTC_STORE7
#define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG
#define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG
typedef enum {

View file

@ -9,6 +9,12 @@ MEMORY
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFC0000, len = 0x40000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
/* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000
/* RTC slow memory (data accessible). Persists over deep sleep. */
rtc_slow_seg(RW) : org = 0x50000000, len = 0x2000
}
_heap_end = 0x40000000;

View file

@ -9,6 +9,12 @@ MEMORY
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFC0000, len = 0x38000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
/* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000
/* RTC slow memory (data accessible). Persists over deep sleep. */
rtc_slow_seg(RW) : org = 0x50000000, len = 0x2000
}
_heap_end = 0x3FFF8000;

View file

@ -102,7 +102,7 @@ SECTIONS
_rodata_start = ABSOLUTE(.);
*(.rodata)
*(.rodata.*)
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
@ -132,7 +132,7 @@ SECTIONS
*(.dynamic)
*(.gnu.version_d)
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
@ -153,4 +153,16 @@ 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
}

View file

@ -9,6 +9,12 @@ MEMORY
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000 /* Even though the segment name is iram, it is actually mapped to flash */
dram0_0_seg (RW) : org = 0x3FFB0000, len = 0x50000 /* Shared RAM, minus rom bss/data/stack.*/
drom0_0_seg (R) : org = 0x3F400010, len = 0x800000
/* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000
/* RTC slow memory (data accessible). Persists over deep sleep. */
rtc_slow_seg(RW) : org = 0x50000000, len = 0x2000
}
_heap_end = 0x40000000;