ソースを参照

Merge branch 'bugfix/tickless_idle_single_core' into 'master'

freertos, pm: fix tickless idle not entered in single core mode

See merge request idf/esp-idf!3584
Ivan Grokhotkov 7 年 前
コミット
28f1e1597b
2 ファイル変更48 行追加8 行削除
  1. 45 8
      components/esp32/pm_esp32.c
  2. 3 0
      components/freertos/tasks.c

+ 45 - 8
components/esp32/pm_esp32.c

@@ -81,11 +81,20 @@ static uint32_t s_ccount_div;
 static uint32_t s_ccount_mul;
 static uint32_t s_ccount_mul;
 
 
 #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
 #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
  * This in turn gets used in IDLE hook to decide if `waiti` needs
  * to be invoked or not.
  * to be invoked or not.
  */
  */
-static bool s_entered_light_sleep[portNUM_PROCESSORS];
+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
 #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
 
 
 /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
 /* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
@@ -465,10 +474,14 @@ void esp_pm_impl_waiti()
 {
 {
 #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
 #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
     int core_id = xPortGetCoreID();
     int core_id = xPortGetCoreID();
-    if (!s_entered_light_sleep[core_id]) {
+    if (s_skipped_light_sleep[core_id]) {
         asm("waiti 0");
         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
 #else
     asm("waiti 0");
     asm("waiti 0");
@@ -477,10 +490,35 @@ void esp_pm_impl_waiti()
 
 
 #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
 #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
 
 
+static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
+{
+#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 )
 void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
 {
 {
     portENTER_CRITICAL(&s_switch_lock);
     portENTER_CRITICAL(&s_switch_lock);
-    if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) {
+    int core_id = xPortGetCoreID();
+    if (!should_skip_light_sleep(core_id)) {
         /* Calculate how much we can sleep */
         /* Calculate how much we can sleep */
         int64_t next_esp_timer_alarm = esp_timer_get_next_alarm();
         int64_t next_esp_timer_alarm = esp_timer_get_next_alarm();
         int64_t now = esp_timer_get_time();
         int64_t now = esp_timer_get_time();
@@ -494,7 +532,6 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
             esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
             esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 #endif
 #endif
             /* Enter sleep */
             /* Enter sleep */
-            int core_id = xPortGetCoreID();
             ESP_PM_TRACE_ENTER(SLEEP, core_id);
             ESP_PM_TRACE_ENTER(SLEEP, core_id);
             int64_t sleep_start = esp_timer_get_time();
             int64_t sleep_start = esp_timer_get_time();
             esp_light_sleep_start();
             esp_light_sleep_start();
@@ -516,7 +553,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
                     ;
                     ;
                 }
                 }
             }
             }
-            s_entered_light_sleep[core_id] = true;
+            other_core_should_skip_light_sleep(core_id);
         }
         }
     }
     }
     portEXIT_CRITICAL(&s_switch_lock);
     portEXIT_CRITICAL(&s_switch_lock);

+ 3 - 0
components/freertos/tasks.c

@@ -2147,6 +2147,8 @@ void vTaskSuspendAll( void )
 
 
 #if ( configUSE_TICKLESS_IDLE != 0 )
 #if ( configUSE_TICKLESS_IDLE != 0 )
 
 
+#if ( portNUM_PROCESSORS > 1 )
+
 	static BaseType_t xHaveReadyTasks()
 	static BaseType_t xHaveReadyTasks()
 	{
 	{
 		for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
 		for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
@@ -2163,6 +2165,7 @@ void vTaskSuspendAll( void )
 		return pdFALSE;
 		return pdFALSE;
 	}
 	}
 
 
+#endif // portNUM_PROCESSORS > 1
 
 
 	static TickType_t prvGetExpectedIdleTime( void )
 	static TickType_t prvGetExpectedIdleTime( void )
 	{
 	{