mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
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:
@@ -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
|
||||
|
@@ -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));
|
||||
|
Reference in New Issue
Block a user