]> granicus.if.org Git - esp-idf/commitdiff
Add a more scalable mechanism for the FreeRTOS tick- and idle hooks; idle handler...
authorJeroen Domburg <git@j0h.nl>
Fri, 11 Nov 2016 11:20:54 +0000 (19:20 +0800)
committerJeroen Domburg <git@j0h.nl>
Fri, 11 Nov 2016 11:20:54 +0000 (19:20 +0800)
components/esp32/freertos_hooks.c [new file with mode: 0644]
components/esp32/include/esp_freertos_hooks.h [new file with mode: 0644]
components/esp32/int_wdt.c
components/esp32/task_wdt.c
components/freertos/Kconfig
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/tasks.c

diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c
new file mode 100644 (file)
index 0000000..50ebd3d
--- /dev/null
@@ -0,0 +1,81 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "esp_attr.h"
+#include "esp_freertos_hooks.h"
+
+//We use just a static array here because it's not expected many components will need
+//an idle or tick hook.
+#define MAX_HOOKS 8
+
+static esp_freertos_idle_cb_t idle_cb[MAX_HOOKS]={0};
+static esp_freertos_tick_cb_t tick_cb[MAX_HOOKS]={0};
+
+void IRAM_ATTR esp_vApplicationTickHook() 
+{
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (tick_cb[n]!=NULL) {
+            tick_cb[n]();
+        }
+    }
+}
+
+void esp_vApplicationIdleHook() 
+{
+    bool doWait=true;
+    bool r;
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (idle_cb[n]!=NULL) {
+            r=idle_cb[n]();
+            if (!r) doWait=false;
+        }
+    }
+    if (doWait) {
+        //Wait for whatever interrupt comes next... this should save some power.
+        asm("waiti 0");
+    }
+}
+
+
+esp_err_t esp_register_freertos_idle_hook(esp_freertos_idle_cb_t new_idle_cb) 
+{
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (idle_cb[n]==NULL) {
+            idle_cb[n]=new_idle_cb;
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NO_MEM;
+}
+
+esp_err_t esp_register_freertos_tick_hook(esp_freertos_tick_cb_t new_tick_cb) 
+{
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (tick_cb[n]==NULL) {
+            tick_cb[n]=new_tick_cb;
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NO_MEM;
+}
+
+void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb)
+{
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (idle_cb[n]==old_idle_cb) idle_cb[n]=NULL;
+    }
+}
+
+void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb)
+{
+    int n;
+    for (n=0; n<MAX_HOOKS; n++) {
+        if (tick_cb[n]==old_tick_cb) tick_cb[n]=NULL;
+    }
+}
+
diff --git a/components/esp32/include/esp_freertos_hooks.h b/components/esp32/include/esp_freertos_hooks.h
new file mode 100644 (file)
index 0000000..1bb5ab1
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef ESP_FREERTOS_HOOKS_H
+#define ESP_FREERTOS_HOOKS_H
+
+#include <stdbool.h>
+#include "esp_err.h"
+
+
+/*
+ Definitions for the tickhook and idlehook callbacks
+*/
+typedef bool (*esp_freertos_idle_cb_t)();
+typedef void (*esp_freertos_tick_cb_t)();
+
+/**
+  * @brief  Register a callback to be called on the freertos idle hook
+  *         The callback should return true if it's okay for the core to
+  *         sleep until an interrupt (or FreeRTOS tick) happens and false
+  *         if it should be called again as fast as possible.
+  *
+  * @param  esp_freertos_idle_cb_t new_idle_cb : Callback to be called
+  *
+  * @return ESP_OK : Callback registered
+  * @return ESP_ERR_NO_MEM : No more space to register hook
+  */
+esp_err_t esp_register_freertos_idle_hook(esp_freertos_idle_cb_t new_idle_cb);
+
+/**
+  * @brief  Register a callback to be called on the freertos tick hook
+  *
+  * @param  esp_freertos_tick_cb_t new_tick_cb : Callback to be called
+  *
+  * @return ESP_OK : Callback registered
+  * @return ESP_ERR_NO_MEM : No more space to register hook
+  */
+esp_err_t esp_register_freertos_tick_hook(esp_freertos_tick_cb_t tick_cb);
+
+
+/**
+  * @brief  Unregister an idle callback registered earlier
+  *
+  * @param  esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered
+  *
+  * @return void
+  */
+void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb);
+
+
+/**
+  * @brief  Unregister a tick callback registered earlier
+  *
+  * @param  esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered
+  *
+  * @return void
+  */
+void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb);
+
+
+#endif
\ No newline at end of file
index 11de8f20d2c6a8e7196a6a9f5ab60aa072d0c88b..fe3ddab370705383520401f9cd79d7302c196e35 100644 (file)
@@ -25,6 +25,7 @@
 #include "esp_err.h"
 #include "esp_intr.h"
 #include "esp_attr.h"
+#include "esp_freertos_hooks.h"
 #include "soc/timer_group_struct.h"
 #include "soc/timer_group_reg.h"
 
 #define WDT_INT_NUM 24
 
 
-void esp_int_wdt_init() {
-    TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
-    TIMERG1.wdt_config0.sys_reset_length=7;                 //3.2uS
-    TIMERG1.wdt_config0.cpu_reset_length=7;                 //3.2uS
-    TIMERG1.wdt_config0.level_int_en=1;
-    TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
-    TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
-    TIMERG1.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
-    //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets
-    //it to their actual value.
-    TIMERG1.wdt_config2=10000;
-    TIMERG1.wdt_config3=10000;
-    TIMERG1.wdt_config0.en=1;
-    TIMERG1.wdt_feed=1;
-    TIMERG1.wdt_wprotect=0;
-    TIMERG1.int_clr_timers.wdt=1;
-    TIMERG1.int_ena.wdt=1;
-    ESP_INTR_DISABLE(WDT_INT_NUM);
-    intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
-    //We do not register a handler for the interrupt because it is interrupt level 4 which
-    //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
-    //this interrupt.
-    ESP_INTR_ENABLE(WDT_INT_NUM);
-}
-
-
 //Take care: the tick hook can also be called before esp_int_wdt_init() is called.
 #if CONFIG_INT_WDT_CHECK_CPU1
 //Not static; the ISR assembly checks this.
 bool int_wdt_app_cpu_ticked=false;
 
-void IRAM_ATTR vApplicationTickHook(void) {
+static void IRAM_ATTR tick_hook(void) {
     if (xPortGetCoreID()!=0) {
         int_wdt_app_cpu_ticked=true;
     } else {
@@ -83,7 +58,7 @@ void IRAM_ATTR vApplicationTickHook(void) {
     }
 }
 #else
-void IRAM_ATTR vApplicationTickHook(void) {
+static void IRAM_ATTR tick_hook(void) {
     if (xPortGetCoreID()!=0) return;
     TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
     TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2;        //Set timeout before interrupt
@@ -93,4 +68,33 @@ void IRAM_ATTR vApplicationTickHook(void) {
 }
 #endif
 
+
+void esp_int_wdt_init() {
+    TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
+    TIMERG1.wdt_config0.sys_reset_length=7;                 //3.2uS
+    TIMERG1.wdt_config0.cpu_reset_length=7;                 //3.2uS
+    TIMERG1.wdt_config0.level_int_en=1;
+    TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
+    TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
+    TIMERG1.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
+    //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets
+    //it to their actual value.
+    TIMERG1.wdt_config2=10000;
+    TIMERG1.wdt_config3=10000;
+    TIMERG1.wdt_config0.en=1;
+    TIMERG1.wdt_feed=1;
+    TIMERG1.wdt_wprotect=0;
+    TIMERG1.int_clr_timers.wdt=1;
+    TIMERG1.int_ena.wdt=1;
+    esp_register_freertos_tick_hook(tick_hook);
+    ESP_INTR_DISABLE(WDT_INT_NUM);
+    intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
+    //We do not register a handler for the interrupt because it is interrupt level 4 which
+    //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
+    //this interrupt.
+    ESP_INTR_ENABLE(WDT_INT_NUM);
+}
+
+
+
 #endif
\ No newline at end of file
index bec1cadaa7cb00ea1940a4eb04c497c4267193ce..f8cfdef26e4525b2cdd25d6e566a10eda6ab4cc9 100644 (file)
@@ -26,6 +26,7 @@
 #include "esp_err.h"
 #include "esp_intr.h"
 #include "esp_attr.h"
+#include "esp_freertos_hooks.h"
 #include "soc/timer_group_struct.h"
 #include "soc/timer_group_reg.h"
 #include "esp_log.h"
@@ -140,6 +141,18 @@ void esp_task_wdt_delete() {
     }
 }
 
+
+#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
+static bool idle_hook(void) {
+#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
+    if (xPortGetCoreID()!=0) return;
+#endif
+    esp_task_wdt_feed();
+    return true;
+}
+#endif
+
+
 void esp_task_wdt_init() {
     TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
     TIMERG0.wdt_config0.sys_reset_length=7;                 //3.2uS
@@ -153,6 +166,9 @@ void esp_task_wdt_init() {
     TIMERG0.wdt_config0.en=1;
     TIMERG0.wdt_feed=1;
     TIMERG0.wdt_wprotect=0;
+#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
+    esp_register_freertos_idle_hook(idle_hook);
+#endif
     ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
     intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
     xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
@@ -161,13 +177,5 @@ void esp_task_wdt_init() {
     ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
 }
 
-#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
-void vApplicationIdleHook(void) {
-#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
-    if (xPortGetCoreID()!=0) return;
-#endif
-    esp_task_wdt_feed();
-}
-#endif
 
 #endif
\ No newline at end of file
index 91e824b2dcb2614a9fb77284f32e1cdfa28bdc15..b9db00e50babf23d29290a356a4610f17613359b 100644 (file)
@@ -141,6 +141,35 @@ config FREERTOS_ISR_STACKSIZE
         The interrupt handlers have their own stack. The size of the stack can be defined here. 
         Each processor has its own stack, so the total size occupied will be twice this.
 
+config FREERTOS_LEGACY_HOOKS
+    bool "Use FreeRTOS legacy hooks"
+    default n
+    help
+        FreeRTOS offers a number of hooks/callback functions that are called when a timer
+        tick happens, the idle thread runs etc. esp-idf replaces these by runtime registerable
+        hooks using the esp_register_freertos_xxx_hook system, but for legacy reasons the old
+        hooks can also still be enabled. Please enable this only if you have code that for some
+        reason can't be migrated to the esp_register_freertos_xxx_hook system.
+
+if FREERTOS_LEGACY_HOOKS
+
+config FREERTOS_LEGACY_IDLE_HOOK
+    bool "Enable legacy idle hook"
+    default n
+    help
+        If enabled, FreeRTOS will call a function called vApplicationIdleHook when the idle thread
+        on a CPU is running. Please make sure your code defines such a function.
+
+config FREERTOS_LEGACY_TICK_HOOK
+    bool "Enable legacy tick hook"
+    default n
+    help
+        If enabled, FreeRTOS will call a function called vApplicationTickHook when a FreeRTOS
+        tick is executed. Please make sure your code defines such a function.
+
+endif #FREERTOS_LEGACY_HOOKS
+
+
 menuconfig FREERTOS_DEBUG_INTERNALS
     bool "Debug FreeRTOS internals"
     default n
index 47566ab3b37fdffbedaf8bc2aff21a60b2e258c5..13ce73e0a8109ab6c8795775098493ae04a43e07 100644 (file)
  *----------------------------------------------------------*/
 
 #define configUSE_PREEMPTION                   1
-#define configUSE_IDLE_HOOK                            ( CONFIG_TASK_WDT_CHECK_IDLE_TASK )
+#define configUSE_IDLE_HOOK                            ( CONFIG_FREERTOS_LEGACY_IDLE_HOOK )
 
-#define configUSE_TICK_HOOK                            ( CONFIG_INT_WDT )
+#define configUSE_TICK_HOOK                            ( CONFIG_FREERTOS_LEGACY_TICK_HOOK )
 
 #define configTICK_RATE_HZ                             ( CONFIG_FREERTOS_HZ )
 
index b79d3a98badf63bd76d92f06d92ed5a2629d7f46..88aa8d3ef5dd7236defe6e1d416f1ea24b8326fe 100644 (file)
@@ -476,6 +476,7 @@ to its original value when it is released. */
 #if configUSE_TICK_HOOK > 0
        extern void vApplicationTickHook( void );
 #endif
+extern void esp_vApplicationTickHook( void );
 
 #if  portFIRST_TASK_HOOK
        extern void vPortFirstTaskHook(TaskFunction_t taskfn);
@@ -2360,22 +2361,21 @@ BaseType_t xSwitchRequired = pdFALSE;
                  We can't really calculate what we need, that's done on core 0... just assume we need a switch.
                  ToDo: Make this more intelligent? -- JD
                */
-               //We do need the tick hook to satisfy the int watchdog.
-               #if ( configUSE_TICK_HOOK == 1 )
                {
                        /* Guard against the tick hook being called when the pended tick
                        count is being unwound (when the scheduler is being unlocked). */
                        if( ( uxSchedulerSuspended[ xPortGetCoreID() ] != ( UBaseType_t ) pdFALSE ) || uxPendedTicks == ( UBaseType_t ) 0U )
                        {
+                               #if ( configUSE_TICK_HOOK == 1 )
                                vApplicationTickHook();
+                               #endif /* configUSE_TICK_HOOK */
+                               esp_vApplicationTickHook();
                        }
                        else
                        {
                                mtCOVERAGE_TEST_MARKER();
                        }
                }
-               #endif /* configUSE_TICK_HOOK */
-
 
                return pdTRUE;
        }
@@ -2506,20 +2506,21 @@ BaseType_t xSwitchRequired = pdFALSE;
                }
                #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
 
-               #if ( configUSE_TICK_HOOK == 1 )
                {
                        /* Guard against the tick hook being called when the pended tick
                        count is being unwound (when the scheduler is being unlocked). */
                        if( uxPendedTicks == ( UBaseType_t ) 0U )
                        {
+                               #if ( configUSE_TICK_HOOK == 1 )
                                vApplicationTickHook();
+                               #endif /* configUSE_TICK_HOOK */
+                               esp_vApplicationTickHook();
                        }
                        else
                        {
                                mtCOVERAGE_TEST_MARKER();
                        }
                }
-               #endif /* configUSE_TICK_HOOK */
                taskEXIT_CRITICAL_ISR(&xTaskQueueMutex);
        }
        else
@@ -2533,6 +2534,7 @@ BaseType_t xSwitchRequired = pdFALSE;
                        vApplicationTickHook();
                }
                #endif
+               esp_vApplicationTickHook();
        }
 
        #if ( configUSE_PREEMPTION == 1 )
@@ -3270,6 +3272,12 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                        vApplicationIdleHook();
                }
                #endif /* configUSE_IDLE_HOOK */
+               {
+                       /* Call the esp-idf hook system */
+                       extern void esp_vApplicationIdleHook( void );
+                       esp_vApplicationIdleHook();
+               }
+
 
                /* This conditional compilation should use inequality to 0, not equality
                to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when