Add a more scalable mechanism for the FreeRTOS tick- and idle hooks; idle handler now uses WAITI to reduce power

This commit is contained in:
Jeroen Domburg 2016-11-11 19:20:54 +08:00
parent f5cebd2666
commit 57009aaa7f
7 changed files with 234 additions and 46 deletions

View file

@ -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;
}
}

View file

@ -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

View 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"
@ -36,6 +37,38 @@
#define WDT_INT_NUM 24
//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;
static void IRAM_ATTR tick_hook(void) {
if (xPortGetCoreID()!=0) {
int_wdt_app_cpu_ticked=true;
} else {
//Only feed wdt if app cpu also ticked.
if (int_wdt_app_cpu_ticked) {
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
TIMERG1.wdt_feed=1;
TIMERG1.wdt_wprotect=0;
int_wdt_app_cpu_ticked=false;
}
}
}
#else
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
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
TIMERG1.wdt_feed=1;
TIMERG1.wdt_wprotect=0;
}
#endif
void esp_int_wdt_init() {
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
@ -53,6 +86,7 @@ void esp_int_wdt_init() {
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
@ -62,35 +96,5 @@ void esp_int_wdt_init() {
}
//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) {
if (xPortGetCoreID()!=0) {
int_wdt_app_cpu_ticked=true;
} else {
//Only feed wdt if app cpu also ticked.
if (int_wdt_app_cpu_ticked) {
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
TIMERG1.wdt_feed=1;
TIMERG1.wdt_wprotect=0;
int_wdt_app_cpu_ticked=false;
}
}
}
#else
void IRAM_ATTR vApplicationTickHook(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
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
TIMERG1.wdt_feed=1;
TIMERG1.wdt_wprotect=0;
}
#endif
#endif

View 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

View 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

View file

@ -152,9 +152,9 @@
*----------------------------------------------------------*/
#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 )

View 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