Просмотр исходного кода

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
Angus Gratton 5 лет назад
Родитель
Сommit
ff8d05466e

+ 47 - 20
components/esp_system/sleep_modes.c

@@ -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

+ 61 - 0
components/esp_system/test/test_sleep.c

@@ -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));

+ 23 - 0
components/soc/soc/esp32/include/soc/rtc.h

@@ -581,6 +581,29 @@ void rtc_sleep_set_wakeup_time(uint64_t t);
  */
 uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt);
 
+/**
+ * @brief Enter deep sleep mode
+ *
+ * Similar to rtc_sleep_start(), but additionally uses hardware to calculate the CRC value
+ * of RTC FAST memory. On wake, this CRC is used to determine if a deep sleep wake
+ * stub is valid to execute (if a wake address is set).
+ *
+ * No RAM is accessed while calculating the CRC and going into deep sleep, which makes
+ * this function safe to use even if the caller's stack is in RTC FAST memory.
+ *
+ * @note If no deep sleep wake stub address is set then calling rtc_sleep_start() will
+ * have the same effect and takes less time as CRC calculation is skipped.
+ *
+ * @note This function should only be called after rtc_sleep_init() has been called to
+ * configure the system for deep sleep.
+ *
+ * @param wakeup_opt - same as for rtc_sleep_start
+ * @param reject_opt - same as for rtc_sleep_start
+ *
+ * @return non-zero if sleep was rejected by hardware
+ */
+uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt);
+
 /**
  * RTC power and clock control initialization settings
  */

+ 26 - 0
components/soc/soc/esp32s2/include/soc/rtc.h

@@ -726,10 +726,36 @@ void rtc_sleep_init(rtc_sleep_config_t cfg);
  *                      - RTC_CNTL_SDIO_REJECT_EN
  *                    These flags are used to prevent entering sleep when e.g.
  *                    an external host is communicating via SDIO slave
+ * @param lslp_mem_inf_fpu If non-zero then the low power config is restored
+ *                         immediately on wake. Recommended for light sleep,
+ *                         has no effect if the system goes into deep sleep.
  * @return non-zero if sleep was rejected by hardware
  */
 uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu);
 
+/**
+ * @brief Enter deep sleep mode
+ *
+ * Similar to rtc_sleep_start(), but additionally uses hardware to calculate the CRC value
+ * of RTC FAST memory. On wake, this CRC is used to determine if a deep sleep wake
+ * stub is valid to execute (if a wake address is set).
+ *
+ * No RAM is accessed while calculating the CRC and going into deep sleep, which makes
+ * this function safe to use even if the caller's stack is in RTC FAST memory.
+ *
+ * @note If no deep sleep wake stub address is set then calling rtc_sleep_start() will
+ * have the same effect and takes less time as CRC calculation is skipped.
+ *
+ * @note This function should only be called after rtc_sleep_init() has been called to
+ * configure the system for deep sleep.
+ *
+ * @param wakeup_opt - same as for rtc_sleep_start
+ * @param reject_opt - same as for rtc_sleep_start
+ *
+ * @return non-zero if sleep was rejected by hardware
+ */
+uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt);
+
 /**
  * RTC power and clock control initialization settings
  */

+ 27 - 0
components/soc/soc/esp32s3/include/soc/rtc.h

@@ -706,10 +706,37 @@ void rtc_sleep_set_wakeup_time(uint64_t t);
  *                      - RTC_CNTL_SDIO_REJECT_EN
  *                    These flags are used to prevent entering sleep when e.g.
  *                    an external host is communicating via SDIO slave
+ * @param lslp_mem_inf_fpu If non-zero then the low power config is restored
+ *                         immediately on wake. Recommended for light sleep,
+ *                         has no effect if the system goes into deep sleep.
+ *
  * @return non-zero if sleep was rejected by hardware
  */
 uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu);
 
+/**
+ * @brief Enter deep sleep mode
+ *
+ * Similar to rtc_sleep_start(), but additionally uses hardware to calculate the CRC value
+ * of RTC FAST memory. On wake, this CRC is used to determine if a deep sleep wake
+ * stub is valid to execute (if a wake address is set).
+ *
+ * No RAM is accessed while calculating the CRC and going into deep sleep, which makes
+ * this function safe to use even if the caller's stack is in RTC FAST memory.
+ *
+ * @note If no deep sleep wake stub address is set then calling rtc_sleep_start() will
+ * have the same effect and takes less time as CRC calculation is skipped.
+ *
+ * @note This function should only be called after rtc_sleep_init() has been called to
+ * configure the system for deep sleep.
+ *
+ * @param wakeup_opt - same as for rtc_sleep_start
+ * @param reject_opt - same as for rtc_sleep_start
+ *
+ * @return non-zero if sleep was rejected by hardware
+ */
+uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt);
+
 /**
  * RTC power and clock control initialization settings
  */

+ 89 - 1
components/soc/src/esp32/rtc_sleep.c

@@ -24,7 +24,9 @@
 #include "soc/fe_reg.h"
 #include "soc/rtc.h"
 #include "esp32/rom/ets_sys.h"
+#include "esp32/rom/rtc.h"
 #include "hal/rtc_cntl_ll.h"
+#include "esp_rom_sys.h"
 
 #define MHZ (1000000)
 
@@ -223,6 +225,9 @@ void rtc_sleep_set_wakeup_time(uint64_t t)
     rtc_cntl_ll_set_wakeup_timer(t);
 }
 
+/* Read back 'reject' status when waking from light or deep sleep */
+static uint32_t rtc_sleep_finish(void);
+
 uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt)
 {
     REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt);
@@ -232,9 +237,92 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt)
     SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
 
     while (GET_PERI_REG_MASK(RTC_CNTL_INT_RAW_REG,
-            RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) {
+                             RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) {
         ;
     }
+
+    return rtc_sleep_finish();
+}
+
+#define STR2(X) #X
+#define STR(X) STR2(X)
+
+uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt)
+{
+    REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt);
+    WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt);
+
+    /* Calculate RTC Fast Memory CRC (for wake stub) & go to deep sleep
+
+       Because we may be running from RTC memory as stack, we can't easily call any
+       functions to do this (as registers may spill to stack, corrupting the CRC).
+
+       Instead, load all the values we need into registers (triggering any stack spills)
+       then use register ops only to calculate the CRC value, write it to the RTC CRC value
+       register, and immediately go into deep sleep.
+     */
+
+    /* Values used to set the RTC_MEM_CONFG value */
+    const unsigned CRC_START_ADDR = 0;
+    const unsigned CRC_LEN = 0x7ff;
+    const unsigned RTC_MEM_PID = 1;
+
+    asm volatile(
+                 "movi a2, 0\n" // trigger a stack spill on working register if needed
+
+                 /* Start CRC calculation */
+                 "s32i %1, %0, 0\n" // set RTC_MEM_CRC_ADDR & RTC_MEM_CRC_LEN
+                 "or a2, %1, %2\n"
+                 "s32i a2, %0, 0\n" // set RTC_MEM_CRC_START
+
+                 /* Wait for the CRC calculation to finish */
+                 ".Lwaitcrc:\n"
+                 "memw\n"
+                 "l32i a2, %0, 0\n"
+                 "bbci a2, "STR(RTC_MEM_CRC_FINISH_S)", .Lwaitcrc\n"
+                 "and a2, a2, %3\n" // clear RTC_MEM_CRC_START
+                 "s32i a2, %0, 0\n"
+                 "memw\n"
+
+                 /* Store the calculated value in RTC_MEM_CRC_REG */
+                 "l32i a2, %4, 0\n"
+                 "s32i a2, %5, 0\n"
+                 "memw\n"
+
+                 /* Set register bit to go into deep sleep */
+                 "l32i a2, %6, 0\n"
+                 "or   a2, a2, %7\n"
+                 "s32i a2, %6, 0\n"
+                 "memw\n"
+
+                 /* Set wait cycle for touch or COCPU after deep sleep. */
+                 ".Lwaitsleep:"
+                 "memw\n"
+                 "l32i a2, %8, 0\n"
+                 "and a2, a2, %9\n"
+                 "beqz a2, .Lwaitsleep\n"
+
+                 :
+                 : "r" (RTC_MEM_CONF), // %0
+                   "r" ( (CRC_START_ADDR << RTC_MEM_CRC_ADDR_S)
+                         | (CRC_LEN << RTC_MEM_CRC_LEN_S)
+                         | (RTC_MEM_PID << RTC_MEM_PID_CONF_S) ), // %1
+                   "r" (RTC_MEM_CRC_START), // %2
+                   "r" (~RTC_MEM_CRC_START), // %3
+                   "r" (RTC_MEM_CRC_RES), // %4
+                   "r" (RTC_MEMORY_CRC_REG), // %5
+                   "r" (RTC_CNTL_STATE0_REG), // %6
+                   "r" (RTC_CNTL_SLEEP_EN), // %7
+                   "r" (RTC_CNTL_INT_RAW_REG), // %8
+                   "r" (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) // %9
+                 : "a2" // working register
+                 );
+
+    return rtc_sleep_finish();
+}
+
+static uint32_t rtc_sleep_finish(void)
+{
     /* In deep sleep mode, we never get here */
     uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW);
     SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG,

+ 101 - 3
components/soc/src/esp32s2/rtc_sleep.c

@@ -26,6 +26,7 @@
 #include "soc/fe_reg.h"
 #include "soc/rtc.h"
 #include "esp32s2/rom/ets_sys.h"
+#include "esp32s2/rom/rtc.h"
 #include "hal/rtc_cntl_ll.h"
 
 /**
@@ -131,6 +132,11 @@ void rtc_sleep_set_wakeup_time(uint64_t t)
     rtc_cntl_ll_set_wakeup_timer(t);
 }
 
+/* Read back 'reject' status when waking from light or deep sleep */
+static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu);
+
+static const unsigned DEEP_SLEEP_TOUCH_WAIT_CYCLE = 0xFF;
+
 uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu)
 {
     REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt);
@@ -139,16 +145,108 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp
         REG_SET_BIT(RTC_CNTL_SLP_REJECT_CONF_REG, RTC_CNTL_LIGHT_SLP_REJECT_EN);
     }
 
+    /* Set wait cycle for touch or COCPU after deep sleep. */
+    REG_SET_FIELD(RTC_CNTL_TIMER2_REG, RTC_CNTL_ULPCP_TOUCH_START_WAIT, DEEP_SLEEP_TOUCH_WAIT_CYCLE);
+
     /* Start entry into sleep mode */
     SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
 
-    /* Set wait cycle for touch or COCPU after deep sleep. */
-    REG_SET_FIELD(RTC_CNTL_TIMER2_REG, RTC_CNTL_ULPCP_TOUCH_START_WAIT, 0xFF);
-
     while (GET_PERI_REG_MASK(RTC_CNTL_INT_RAW_REG,
                              RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) {
         ;
     }
+
+    return rtc_sleep_finish(lslp_mem_inf_fpu);
+}
+
+#define STR2(X) #X
+#define STR(X) STR2(X)
+
+uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt)
+{
+    REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt);
+    WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt);
+
+    /* Calculate RTC Fast Memory CRC (for wake stub) & go to deep sleep
+
+       Because we may be running from RTC memory as stack, we can't easily call any
+       functions to do this (as registers may spill to stack, corrupting the CRC).
+
+       Instead, load all the values we need into registers (triggering any stack spills)
+       then use register ops only to calculate the CRC value, write it to the RTC CRC value
+       register, and immediately go into deep sleep.
+     */
+
+    /* Values used to set the DPORT_RTC_FASTMEM_CONFIG_REG value */
+    const unsigned CRC_START_ADDR = 0;
+    const unsigned CRC_LEN = 0x7ff;
+
+    asm volatile(
+                 "movi a2, 0\n" // trigger a stack spill on working register if needed
+
+                 /* Start CRC calculation */
+                 "s32i %1, %0, 0\n" // set RTC_MEM_CRC_ADDR & RTC_MEM_CRC_LEN
+                 "or a2, %1, %2\n"
+                 "s32i a2, %0, 0\n" // set RTC_MEM_CRC_START
+
+                 /* Wait for the CRC calculation to finish */
+                 ".Lwaitcrc:\n"
+                 "memw\n"
+                 "l32i a2, %0, 0\n"
+                 "bbci a2, "STR(DPORT_RTC_MEM_CRC_FINISH_S)", .Lwaitcrc\n"
+                 "xor %2, %2, %2\n" // %2 -> ~DPORT_RTC_MEM_CRC_START
+                 "and a2, a2, %2\n"
+                 "s32i a2, %0, 0\n"  // clear RTC_MEM_CRC_START
+                 "memw\n"
+                 "xor %2, %2, %2\n" // %2 -> DPORT_RTC_MEM_CRC_START, probably unnecessary but gcc assumes inputs unchanged
+
+                 /* Store the calculated value in RTC_MEM_CRC_REG */
+                 "l32i a2, %3, 0\n"
+                 "s32i a2, %4, 0\n"
+                 "memw\n"
+
+                 /* Set wait cycle for touch or COCPU after deep sleep (can be moved to C code part?) */
+                 "l32i a2, %5, 0\n"
+                 "and a2, a2, %6\n"
+                 "or a2, a2, %7\n"
+                 "s32i a2, %5, 0\n"
+
+                 /* Set register bit to go into deep sleep */
+                 "l32i a2, %8, 0\n"
+                 "or   a2, a2, %9\n"
+                 "s32i a2, %8, 0\n"
+                 "memw\n"
+
+                 /* Wait for sleep reject interrupt (never finishes if successful) */
+                 ".Lwaitsleep:"
+                 "memw\n"
+                 "l32i a2, %10, 0\n"
+                 "and a2, a2, %11\n"
+                 "beqz a2, .Lwaitsleep\n"
+
+                 :
+                 : /* Note, at -O0 this is the limit of available registers in this function */
+                   "r" (DPORT_RTC_FASTMEM_CONFIG_REG), // %0
+                   "r" ( (CRC_START_ADDR << DPORT_RTC_MEM_CRC_START_S)
+                         | (CRC_LEN << DPORT_RTC_MEM_CRC_LEN_S)), // %1
+                   "r" (DPORT_RTC_MEM_CRC_START), // %2
+                   "r" (DPORT_RTC_FASTMEM_CRC_REG), // %3
+                   "r" (RTC_MEMORY_CRC_REG), // %4
+                   "r" (RTC_CNTL_TIMER2_REG), // %5
+                   "r" (~RTC_CNTL_ULPCP_TOUCH_START_WAIT_M), // %6
+                   "r" (DEEP_SLEEP_TOUCH_WAIT_CYCLE << RTC_CNTL_ULPCP_TOUCH_START_WAIT_S), // %7
+                   "r" (RTC_CNTL_STATE0_REG), // %8
+                   "r" (RTC_CNTL_SLEEP_EN), // %9
+                   "r" (RTC_CNTL_INT_RAW_REG), // %10
+                   "r" (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) // %11
+                 : "a2" // working register
+                 );
+
+    return rtc_sleep_finish(0);
+}
+
+static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu)
+{
     /* In deep sleep mode, we never get here */
     uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW);
     SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG,