Merge branch 'bugfix/deep_sleep_stub_heap_rtc_fast_mem' into 'master'

deep sleep: Calculate RTC CRC without using any stack or other RTC heap memory

Closes IDF-2242

See merge request espressif/esp-idf!10741
This commit is contained in:
Angus Gratton
2020-10-13 06:17:50 +08:00
7 changed files with 374 additions and 24 deletions

View File

@@ -135,8 +135,8 @@ static sleep_config_t s_config = {
static bool s_light_sleep_wakeup = false;
/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc()
is not thread-safe. */
static _lock_t lock_rtc_memory_crc;
is not thread-safe, so we need to disable interrupts before going to deep sleep. */
static portMUX_TYPE spinlock_rtc_deep_sleep = portMUX_INITIALIZER_UNLOCKED;
static const char* TAG = "sleep";
@@ -155,16 +155,6 @@ static void touch_wakeup_prepare(void);
*/
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 NULL;
}
esp_deep_sleep_wake_stub_fn_t stub_ptr = (esp_deep_sleep_wake_stub_fn_t) REG_READ(RTC_ENTRY_ADDR_REG);
if (!esp_ptr_executable(stub_ptr)) {
return NULL;
@@ -174,10 +164,7 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void)
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) {
@@ -260,12 +247,25 @@ static void IRAM_ATTR resume_uarts(void)
}
}
inline static uint32_t IRAM_ATTR call_rtc_sleep_start(uint32_t reject_triggers);
static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
{
// Stop UART output so that output is not lost due to APB frequency change.
// For light sleep, suspend UART output — it will resume after wakeup.
// For deep sleep, wait for the contents of UART FIFO to be sent.
if (pd_flags & RTC_SLEEP_PD_DIG) {
bool deep_sleep = pd_flags & RTC_SLEEP_PD_DIG;
#if !CONFIG_FREERTOS_UNICORE && ESP32S3_ALLOW_RTC_FAST_MEM_AS_HEAP
/* Currently only safe to use deep sleep wake stub & RTC memory as heap in single core mode.
For ESP32-S3, either disable ESP32S3_ALLOW_RTC_FAST_MEM_AS_HEAP in config or find a way to set the
deep sleep wake stub to NULL.
*/
assert(!deep_sleep || esp_get_deep_sleep_wake_stub() == NULL);
#endif
if (deep_sleep) {
flush_uarts();
} else {
suspend_uarts();
@@ -320,12 +320,30 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
timer_wakeup_prepare();
}
#ifdef CONFIG_IDF_TARGET_ESP32
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, reject_triggers);
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, reject_triggers, 1);
uint32_t result;
if (deep_sleep) {
/* Disable interrupts in case another task writes to RTC memory while we
* calculate RTC memory CRC
*
* Note: for ESP32-S3 running in dual core mode this is currently not enough,
* see the assert at top of this function.
*/
portENTER_CRITICAL(&spinlock_rtc_deep_sleep);
#if !CONFIG_ESP32_ALLOW_RTC_FAST_MEM_AS_HEAP && !CONFIG_ESP32S2_ALLOW_RTC_FAST_MEM_AS_HEAP && !CONFIG_ESP32S3_ALLOW_RTC_FAST_MEM_AS_HEAP
/* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */
set_rtc_memory_crc();
result = call_rtc_sleep_start(reject_triggers);
#else
/* Otherwise, need to call the dedicated soc function for this */
result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers);
#endif
portEXIT_CRITICAL(&spinlock_rtc_deep_sleep);
} else {
result = call_rtc_sleep_start(reject_triggers);
}
// Restore CPU frequency
rtc_clk_cpu_freq_set_config(&cpu_freq_config);
@@ -335,6 +353,15 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
return result;
}
inline static uint32_t IRAM_ATTR call_rtc_sleep_start(uint32_t reject_triggers)
{
#ifdef CONFIG_IDF_TARGET_ESP32
return rtc_sleep_start(s_config.wakeup_triggers, reject_triggers);
#else
return rtc_sleep_start(s_config.wakeup_triggers, reject_triggers, 1);
#endif
}
void IRAM_ATTR esp_deep_sleep_start(void)
{
// record current RTC time

View File

@@ -287,6 +287,67 @@ TEST_CASE_MULTIPLE_STAGES("can set sleep wake stub", "[deepsleep][reset=DEEPSLEE
check_wake_stub);
#if CONFIG_ESP32_ALLOW_RTC_FAST_MEM_AS_HEAP || CONFIG_ESP32S2_ALLOW_RTC_FAST_MEM_AS_HEAP \
|| CONFIG_ESP32S3_ALLOW_RTC_FAST_MEM_AS_HEAP
#if CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION
/* Version of prepare_wake_stub() that sets up the deep sleep call while running
from RTC memory as stack, with a high frequency timer also writing RTC FAST
memory.
This is important because the ROM code (ESP32 & ESP32-S2) requires software
trigger a CRC calculation (done in hardware) for the entire RTC FAST memory
before going to deep sleep and if it's invalid then the stub is not
run. Also, while the CRC is being calculated the RTC FAST memory is not
accesible by the CPU (reads all zeros).
*/
static void increment_rtc_memory_cb(void *arg)
{
static volatile RTC_FAST_ATTR unsigned counter;
counter++;
}
static void prepare_wake_stub_from_rtc(void)
{
/* RTC memory can be used as heap, however there is no API call that returns this as
a memory capability (as it's an implementation detail). So to test this we need to allocate
the stack statically.
*/
static RTC_FAST_ATTR uint8_t sleep_stack[1024];
static RTC_FAST_ATTR StaticTask_t sleep_task;
/* normally BSS like sleep_stack will be cleared on reset, but RTC memory is not cleared on
* wake from deep sleep. So to ensure unused stack is different if test is re-run without a full reset,
* fill with some random bytes
*/
esp_fill_random(sleep_stack, sizeof(sleep_stack));
/* to make things extra sure, start a periodic timer to write to RTC FAST RAM at high frequency */
const esp_timer_create_args_t timer_args = {
.callback = increment_rtc_memory_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK,
.name = "Write RTC MEM"
};
esp_timer_handle_t timer;
ESP_ERROR_CHECK( esp_timer_create(&timer_args, &timer) );
ESP_ERROR_CHECK( esp_timer_start_periodic(timer, 200) );
printf("Creating test task with stack %p\n", sleep_stack);
TEST_ASSERT_NOT_NULL(xTaskCreateStatic( (void *)prepare_wake_stub, "sleep", sizeof(sleep_stack), NULL,
UNITY_FREERTOS_PRIORITY, sleep_stack, &sleep_task));
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_FAIL_MESSAGE("Should be asleep by now");
}
TEST_CASE_MULTIPLE_STAGES("can set sleep wake stub from stack in RTC RAM", "[deepsleep][reset=DEEPSLEEP_RESET]",
prepare_wake_stub_from_rtc,
check_wake_stub);
#endif // CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION
#endif // CONFIG_xyz_ALLOW_RTC_FAST_MEM_AS_HEAP
TEST_CASE("wake up using ext0 (13 high)", "[deepsleep][ignore]")
{
ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13));