mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(esp_hw_support): implement software CPU retention synchronization for SMP sleep modes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -69,14 +69,27 @@ esp_err_t esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uin
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
/**
|
||||
* Do sleep prepare for other smp cores
|
||||
* Start the software CPU retention synchronization.
|
||||
*
|
||||
* This waits until the other CPU retention state to IDLE before
|
||||
* the caller starts the software CPU retention.
|
||||
*/
|
||||
void sleep_smp_cpu_sleep_prepare(void);
|
||||
void sleep_cpu_retention_start(void);
|
||||
|
||||
/**
|
||||
* Do wakeup prepare for other smp cores
|
||||
* Run the software CPU retention flow on the stalled other CPU.
|
||||
*
|
||||
* @param arg Unused
|
||||
*/
|
||||
void sleep_smp_cpu_wakeup_prepare(void);
|
||||
void sleep_cpu_retention_execute(void* arg);
|
||||
|
||||
/**
|
||||
* Finish the software CPU retention synchronization.
|
||||
*
|
||||
* This is called by the initiating CPU after wakeup or sleep rejection to
|
||||
* synchronize with the other CPU retention state and return its own state to idle.
|
||||
*/
|
||||
void sleep_cpu_retention_finish(void);
|
||||
|
||||
/**
|
||||
* Notify the other core that this sleep does not require retention.
|
||||
|
||||
@@ -477,17 +477,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable)
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
static IRAM_ATTR void smp_core_do_retention(void)
|
||||
IRAM_ATTR void sleep_cpu_retention_execute(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
esp_cpu_branch_prediction_disable();
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
|
||||
if (core_id == 0) {
|
||||
WRITE_PERI_REG(INTPRI_CPU_INTR_FROM_CPU_2_REG, 0);
|
||||
} else {
|
||||
WRITE_PERI_REG(INTPRI_CPU_INTR_FROM_CPU_3_REG, 0);
|
||||
}
|
||||
|
||||
// Wait another core start to do retention
|
||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
|
||||
bool smp_skip_retention = false;
|
||||
@@ -560,19 +555,16 @@ IRAM_ATTR void esp_sleep_cpu_skip_retention(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void sleep_smp_cpu_sleep_prepare(void)
|
||||
void sleep_cpu_retention_start(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) {
|
||||
;
|
||||
}
|
||||
esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL);
|
||||
#else
|
||||
esp_ipc_isr_stall_other_cpu();
|
||||
#endif
|
||||
}
|
||||
|
||||
void sleep_smp_cpu_wakeup_prepare(void)
|
||||
void sleep_cpu_retention_finish(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
@@ -584,8 +576,6 @@ void sleep_smp_cpu_wakeup_prepare(void)
|
||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
|
||||
}
|
||||
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
|
||||
#else
|
||||
esp_ipc_isr_release_other_cpu();
|
||||
#endif
|
||||
}
|
||||
#endif //!CONFIG_FREERTOS_UNICORE
|
||||
|
||||
@@ -395,17 +395,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable)
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
static SPM_IRAM_ATTR void smp_core_do_retention(void)
|
||||
SPM_IRAM_ATTR void sleep_cpu_retention_execute(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
esp_cpu_branch_prediction_disable();
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
|
||||
if (core_id == 0) {
|
||||
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0);
|
||||
} else {
|
||||
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0);
|
||||
}
|
||||
|
||||
// Wait another core start to do retention
|
||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
|
||||
bool smp_skip_retention = false;
|
||||
@@ -475,19 +470,16 @@ SPM_IRAM_ATTR void esp_sleep_cpu_skip_retention(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void sleep_smp_cpu_sleep_prepare(void)
|
||||
void sleep_cpu_retention_start(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) {
|
||||
;
|
||||
}
|
||||
esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL);
|
||||
#else
|
||||
esp_ipc_isr_stall_other_cpu();
|
||||
#endif
|
||||
}
|
||||
|
||||
void sleep_smp_cpu_wakeup_prepare(void)
|
||||
void sleep_cpu_retention_finish(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
@@ -499,8 +491,6 @@ void sleep_smp_cpu_wakeup_prepare(void)
|
||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
|
||||
}
|
||||
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
|
||||
#else
|
||||
esp_ipc_isr_release_other_cpu();
|
||||
#endif
|
||||
}
|
||||
#endif //!CONFIG_FREERTOS_UNICORE
|
||||
|
||||
@@ -418,17 +418,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable)
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
static IRAM_ATTR void smp_core_do_retention(void)
|
||||
IRAM_ATTR void sleep_cpu_retention_execute(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
esp_cpu_branch_prediction_disable();
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
|
||||
if (core_id == 0) {
|
||||
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0);
|
||||
} else {
|
||||
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0);
|
||||
}
|
||||
|
||||
// Wait another core start to do retention
|
||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
|
||||
bool smp_skip_retention = false;
|
||||
@@ -496,19 +491,16 @@ IRAM_ATTR void esp_sleep_cpu_skip_retention(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void sleep_smp_cpu_sleep_prepare(void)
|
||||
void sleep_cpu_retention_start(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) {
|
||||
;
|
||||
}
|
||||
esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL);
|
||||
#else
|
||||
esp_ipc_isr_stall_other_cpu();
|
||||
#endif
|
||||
}
|
||||
|
||||
void sleep_smp_cpu_wakeup_prepare(void)
|
||||
void sleep_cpu_retention_finish(void)
|
||||
{
|
||||
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
|
||||
uint8_t core_id = esp_cpu_get_core_id();
|
||||
@@ -520,8 +512,6 @@ void sleep_smp_cpu_wakeup_prepare(void)
|
||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
|
||||
}
|
||||
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
|
||||
#else
|
||||
esp_ipc_isr_release_other_cpu();
|
||||
#endif
|
||||
}
|
||||
#endif //!CONFIG_FREERTOS_UNICORE
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user