feat(esp_hw_support): implement software CPU retention synchronization for SMP sleep modes

This commit is contained in:
wuzhenghui
2026-06-18 09:41:41 +08:00
parent d4e3109c1b
commit 0c7caf79b5
5 changed files with 92 additions and 86 deletions
+62 -39
View File
@@ -351,10 +351,6 @@ static sleep_config_t s_config = {
expected when determining wakeup cause. */
static bool s_light_sleep_wakeup = false;
/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc()
is not thread-safe, so we need to disable interrupts before going to deep sleep. */
static portMUX_TYPE __attribute__((unused)) spinlock_rtc_deep_sleep = portMUX_INITIALIZER_UNLOCKED;
ESP_LOG_ATTR_TAG(TAG, "sleep");
/* APP core of esp32 can't access to RTC FAST MEMORY, do not define it with RTC_IRAM_ATTR,
@@ -525,28 +521,28 @@ esp_err_t esp_deep_sleep_try(uint64_t time_in_us)
static esp_err_t s_sleep_hook_register(esp_deep_sleep_cb_t new_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS])
{
esp_os_enter_critical(&spinlock_rtc_deep_sleep);
esp_os_enter_critical(&s_config.lock);
for (int n = 0; n < MAX_DSLP_HOOKS; n++) {
if (s_cb_array[n]==NULL || s_cb_array[n]==new_cb) {
s_cb_array[n]=new_cb;
esp_os_exit_critical(&spinlock_rtc_deep_sleep);
esp_os_exit_critical(&s_config.lock);
return ESP_OK;
}
}
esp_os_exit_critical(&spinlock_rtc_deep_sleep);
esp_os_exit_critical(&s_config.lock);
ESP_LOGE(TAG, "Registered deepsleep callbacks exceeds MAX_DSLP_HOOKS");
return ESP_ERR_NO_MEM;
}
static void s_sleep_hook_deregister(esp_deep_sleep_cb_t old_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS])
{
esp_os_enter_critical(&spinlock_rtc_deep_sleep);
esp_os_enter_critical(&s_config.lock);
for (int n = 0; n < MAX_DSLP_HOOKS; n++) {
if(s_cb_array[n] == old_cb) {
s_cb_array[n] = NULL;
}
}
esp_os_exit_critical(&spinlock_rtc_deep_sleep);
esp_os_exit_critical(&s_config.lock);
}
esp_err_t esp_deep_sleep_register_hook(esp_deep_sleep_cb_t new_dslp_cb)
@@ -1251,7 +1247,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
/* Disable interrupts and stall another core in case another task writes
* to RTC memory while we calculate RTC memory CRC.
*/
esp_os_enter_critical(&spinlock_rtc_deep_sleep);
esp_os_enter_critical(&s_config.lock);
esp_ipc_isr_stall_other_cpu();
esp_ipc_isr_stall_pause();
/* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe
@@ -1356,7 +1352,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
xPortThreadSafeDisclaim();
esp_ipc_isr_stall_resume();
esp_ipc_isr_release_other_cpu();
esp_os_exit_critical(&spinlock_rtc_deep_sleep);
esp_os_exit_critical(&s_config.lock);
return err;
}
@@ -1428,6 +1424,51 @@ static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint3
return reject;
}
#if !CONFIG_FREERTOS_UNICORE
static SLEEP_FN_ATTR esp_err_t sleep_smp_cpu_sleep_prepare(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
sleep_cpu_retention_start();
#endif
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
// The other core will be stalled by high-priority interrupt and spins on variables in internal RAM,
// which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed.
esp_int_wdt_livelock_workaround(false);
#endif
esp_err_t ipc_isr_err = esp_ipc_isr_stall_other_cpu_safe();
if (ipc_isr_err == ESP_OK) {
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
// Run CPU retention in the context of the other safely stalled CPU.
esp_ipc_isr_call(sleep_cpu_retention_execute, NULL);
#endif
esp_ipc_isr_stall_pause();
} else {
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
sleep_cpu_retention_finish();
#endif
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
// Configure WDT to use livelock workaround timeout after releasing other CPU
esp_int_wdt_livelock_workaround(true);
#endif
return ESP_ERR_SLEEP_REJECT;
}
return ESP_OK;
}
static SLEEP_FN_ATTR void sleep_smp_cpu_wakeup_prepare(void)
{
esp_ipc_isr_stall_resume();
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
sleep_cpu_retention_finish();
#endif
esp_ipc_isr_release_other_cpu();
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
// Configure WDT to use livelock workaround timeout after releasing other CPU
esp_int_wdt_livelock_workaround(true);
#endif
}
#endif // !CONFIG_FREERTOS_UNICORE
/**
* vddsdio is used for power supply of spi flash
*
@@ -1448,14 +1489,7 @@ esp_err_t esp_light_sleep_start(void)
{
s_config.ccount_ticks_record = esp_cpu_get_cycle_count();
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_GOTO_SLEEP, (void *)0);
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_err_t timerret = ESP_OK;
/* If a task watchdog timer is running, we have to stop it. */
timerret = esp_task_wdt_stop();
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_timer_private_lock();
esp_os_enter_critical(&s_config.lock);
s_config.rtc_ticks_at_sleep_start = rtc_time_get();
uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count();
@@ -1470,23 +1504,22 @@ esp_err_t esp_light_sleep_start(void)
#endif
#if !CONFIG_FREERTOS_UNICORE
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
sleep_smp_cpu_sleep_prepare();
#else
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
// The other core will be stalled by high-priority interrupt and spins on variables in internal RAM,
// which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed.
esp_int_wdt_livelock_workaround(false);
#endif
esp_ipc_isr_stall_other_cpu();
#endif
esp_ipc_isr_stall_pause();
if (sleep_smp_cpu_sleep_prepare() != ESP_OK) {
esp_os_exit_critical(&s_config.lock);
return ESP_ERR_SLEEP_REJECT;
}
#endif
/* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe
critical section to avoid deadlocks and fastup the sleep process.
*/
xPortThreadSafeClaim();
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
/* If a task watchdog timer is running, we have to stop it. */
esp_err_t timerret = esp_task_wdt_stop();
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT
/* Cache Suspend 0: if CONFIG_PM_SLP_IRAM_OPT is enabled, suspend cache here so that the access to flash
* during the sleep process can be explicitly exposed.
@@ -1704,18 +1737,8 @@ esp_err_t esp_light_sleep_start(void)
/* Restore port critical before unstalling other CPU */
xPortThreadSafeDisclaim();
esp_timer_private_unlock();
#if !CONFIG_FREERTOS_UNICORE
esp_ipc_isr_stall_resume();
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW
sleep_smp_cpu_wakeup_prepare();
#else
esp_ipc_isr_release_other_cpu();
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
// Configure WDT to use livelock workaround timeout after releasing other CPU
esp_int_wdt_livelock_workaround(true);
#endif
#endif
#endif
#if SOC_RTC_WDT_SUPPORTED