From ae08bdcc31284aeb9032b87de05b1cc96b33f7e0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 30 Oct 2018 16:05:45 +0800 Subject: [PATCH 1/3] freertos: fix compilation warning in single core mode When tickless idle is enabled --- components/freertos/tasks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 0f42f3c47..94dbce867 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -2153,6 +2153,8 @@ void vTaskSuspendAll( void ) #if ( configUSE_TICKLESS_IDLE != 0 ) +#if ( portNUM_PROCESSORS > 1 ) + static BaseType_t xHaveReadyTasks() { for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i) @@ -2169,6 +2171,7 @@ void vTaskSuspendAll( void ) return pdFALSE; } +#endif // portNUM_PROCESSORS > 1 static TickType_t prvGetExpectedIdleTime( void ) { From 22dd3103bd0f919347404d65379d11d7a46bfbc7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 30 Oct 2018 16:05:06 +0800 Subject: [PATCH 2/3] pm: fix entering light sleep in single core mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tickless idle/light sleep procedure had a bug in single core mode. Consider the flow of events: 1. Idle task runs and calls vApplicationIdleHook 2. This calls esp_vApplicationIdleHook, which calls esp_pm_impl_idle_hook, and pm lock for RTOS on the current core is released. 3. Then esp_vApplicationIdleHook calls esp_pm_impl_waiti, which checks that s_entered_light_sleep[core_id]==false and goes into waiti state. 4. Some interrupt happens, calls esp_pm_impl_isr_hook, which takes pm lock for RTOS. PM state goes back to CPU_FREQ_MAX. 5. Once the interrupt is over, vApplicationIdleHook returns, and Idle task continues to run, finally reaching the call to vApplicationSleep. 6. vApplicationSleep does not enter light sleep, because esp_pm_impl_isr_hook has already changed PM state from IDLE to CPU_FREQ_MAX. This didn’t happen in dual core mode, because waiti state of one CPU was interrupted by CCOMPARE update interrupt from the other CPU, in which case PM lock for FreeRTOS was not taken. Fix by inverting the meaning of the flag (for convenience) and only setting it to true when vApplicationSleep actually fails to enter light sleep. --- components/esp32/pm_esp32.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/components/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index af25f4770..b53ea079b 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -81,11 +81,11 @@ static uint32_t s_ccount_div; static uint32_t s_ccount_mul; #if CONFIG_FREERTOS_USE_TICKLESS_IDLE -/* Indicates if light sleep entry was successful for given CPU. +/* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU. * This in turn gets used in IDLE hook to decide if `waiti` needs * to be invoked or not. */ -static bool s_entered_light_sleep[portNUM_PROCESSORS]; +static bool s_skipped_light_sleep[portNUM_PROCESSORS]; #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU. @@ -465,10 +465,14 @@ void esp_pm_impl_waiti() { #if CONFIG_FREERTOS_USE_TICKLESS_IDLE int core_id = xPortGetCoreID(); - if (!s_entered_light_sleep[core_id]) { + if (s_skipped_light_sleep[core_id]) { asm("waiti 0"); - } else { - s_entered_light_sleep[core_id] = false; + /* Interrupt took the CPU out of waiti and s_rtos_lock_handle[core_id] + * is now taken. However since we are back to idle task, we can release + * the lock so that vApplicationSleep can attempt to enter light sleep. + */ + esp_pm_impl_idle_hook(); + s_skipped_light_sleep[core_id] = false; } #else asm("waiti 0"); @@ -480,7 +484,11 @@ void esp_pm_impl_waiti() void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) { portENTER_CRITICAL(&s_switch_lock); - if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) { + int core_id = xPortGetCoreID(); + if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) { + s_skipped_light_sleep[core_id] = true; + } else { + s_skipped_light_sleep[core_id] = false; /* Calculate how much we can sleep */ int64_t next_esp_timer_alarm = esp_timer_get_next_alarm(); int64_t now = esp_timer_get_time(); @@ -494,7 +502,6 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); #endif /* Enter sleep */ - int core_id = xPortGetCoreID(); ESP_PM_TRACE_ENTER(SLEEP, core_id); int64_t sleep_start = esp_timer_get_time(); esp_light_sleep_start(); @@ -516,7 +523,6 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) ; } } - s_entered_light_sleep[core_id] = true; } } portEXIT_CRITICAL(&s_switch_lock); From 0716e65955708c77b221b6c1357926f19ec0eb27 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 1 Nov 2018 19:38:48 +0800 Subject: [PATCH 3/3] pm: prevent entering light sleep again immediately after wakeup When light sleep is finished on one CPU, it is possible that the other CPU will enter light sleep again very soon, before interrupts on the first CPU get a chance to run. To avoid such situation, set a flag for the other CPU to skip light sleep attempt. --- components/esp32/pm_esp32.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/components/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index b53ea079b..0cf7e2d20 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -86,6 +86,15 @@ static uint32_t s_ccount_mul; * to be invoked or not. */ static bool s_skipped_light_sleep[portNUM_PROCESSORS]; + +#if portNUM_PROCESSORS == 2 +/* When light sleep is finished on one CPU, it is possible that the other CPU + * will enter light sleep again very soon, before interrupts on the first CPU + * get a chance to run. To avoid such situation, set a flag for the other CPU to + * skip light sleep attempt. + */ +static bool s_skip_light_sleep[portNUM_PROCESSORS]; +#endif // portNUM_PROCESSORS == 2 #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU. @@ -481,14 +490,35 @@ void esp_pm_impl_waiti() #if CONFIG_FREERTOS_USE_TICKLESS_IDLE -void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +static inline bool IRAM_ATTR should_skip_light_sleep(int core_id) { - portENTER_CRITICAL(&s_switch_lock); - int core_id = xPortGetCoreID(); +#if portNUM_PROCESSORS == 2 + if (s_skip_light_sleep[core_id]) { + s_skip_light_sleep[core_id] = false; + s_skipped_light_sleep[core_id] = true; + return true; + } +#endif // portNUM_PROCESSORS == 2 if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) { s_skipped_light_sleep[core_id] = true; } else { s_skipped_light_sleep[core_id] = false; + } + return s_skipped_light_sleep[core_id]; +} + +static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id) +{ +#if portNUM_PROCESSORS == 2 + s_skip_light_sleep[!core_id] = true; +#endif +} + +void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +{ + portENTER_CRITICAL(&s_switch_lock); + int core_id = xPortGetCoreID(); + if (!should_skip_light_sleep(core_id)) { /* Calculate how much we can sleep */ int64_t next_esp_timer_alarm = esp_timer_get_next_alarm(); int64_t now = esp_timer_get_time(); @@ -523,6 +553,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) ; } } + other_core_should_skip_light_sleep(core_id); } } portEXIT_CRITICAL(&s_switch_lock);