Disable brown-out WDT, fix thread WDT, add panic reason indication to _xt_panic()

This commit is contained in:
Jeroen Domburg
2016-10-25 17:05:13 +08:00
parent ae5c563080
commit 75a11589a1
9 changed files with 134 additions and 23 deletions

View File

@@ -140,7 +140,6 @@ config ULP_COPROC_RESERVE_MEM
default 0
depends on !ULP_COPROC_ENABLED
menu "Watchdogs & brown-out detection"
config INT_WDT
bool "Interrupt watchdog"
@@ -155,7 +154,7 @@ config INT_WDT_TIMEOUT_MS
int "Interrupt watchdog timeout (ms)"
depends on INT_WDT
default 10
range INT_WDT_TIMEOUT_MIN 10000
range 10 10000
help
The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate.
@@ -190,11 +189,15 @@ config TASK_WDT_CHECK_IDLE_TASK
With this turned on, the task WDT can detect if the idle task is not called within the task
watchdog timeout period. The idle task not being called usually is a symptom of another
task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
idle task getting some runtime every now and then.
idle task getting some runtime every now and then. Take Care: With this disabled, this
watchdog will trigger if no tasks register themselves within the timeout value.
#The brownout detector code is disabled (by making it depend on a nonexisting symbol) because the current revision of ESP32
#silicon has a bug in the brown-out detector, rendering it unusable for resetting the CPU.
config BROWNOUT_DET
bool "Hardware brownout detect & reset"
default y
depends on NEEDS_ESP32_NEW_SILICON_REV
help
The ESP32 has a built-in brownout detector which can detect if the voltage is lower than
a specific value. If this happens, it will reset the chip in order to prevent unintended
@@ -207,6 +210,8 @@ choice BROWNOUT_DET_LVL_SEL
help
The brownout detector will reset the chip when the supply voltage is below this level.
#The voltage levels here are estimates, more work needs to be done to figure out the exact voltages
#of the brownout threshold levels.
config BROWNOUT_DET_LVL_SEL_0
bool "2.1V"
config BROWNOUT_DET_LVL_SEL_1
@@ -246,7 +251,6 @@ config BROWNOUT_DET_RESETDELAY
The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed
before trying to restart the chip. You can set the delay here.
endmenu

View File

@@ -22,10 +22,18 @@
#include "soc/rtc_cntl_reg.h"
#if CONFIG_BROWNOUT_DET
/*
This file ins included in esp-idf, but the menuconfig option for this is disabled because a silicon bug
prohibits the brownout detector from functioning correctly on the ESP32.
*/
void esp_brownout_init() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) |
RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
}
}
#endif

View File

@@ -44,7 +44,6 @@ This uses the TIMERG1 WDT.
#define WDT_INT_NUM 24
#define WDT_WRITE_KEY 0x50D83AA1
void int_wdt_init() {
@@ -55,11 +54,15 @@ void int_wdt_init() {
TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt
TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system
TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
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
//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
@@ -69,9 +72,10 @@ void int_wdt_init() {
}
void vApplicationTickHook(void) {
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
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;
}

View File

@@ -33,6 +33,7 @@ This uses the TIMERG0 WDT.
#include <esp_types.h>
#include "esp_err.h"
#include "esp_intr.h"
#include "esp_attr.h"
#include "soc/timer_group_struct.h"
#include "esp_log.h"
@@ -42,7 +43,6 @@ This uses the TIMERG0 WDT.
static const char* TAG = "task_wdt";
typedef struct wdt_task_t wdt_task_t;
struct wdt_task_t {
TaskHandle_t task_handle;
@@ -50,16 +50,35 @@ struct wdt_task_t {
wdt_task_t *next;
};
static wdt_task_t *wdt_task_list=NULL;
//We use this interrupt number on whatever task calls task_wdt_init.
#define WDT_INT_NUM 24
#define WDT_WRITE_KEY 0x50D83AA1
static void task_wdt_isr(void *arg) {
static void IRAM_ATTR task_wdt_isr(void *arg) {
wdt_task_t *wdttask;
const char *cpu;
//Feed the watchdog so we do not reset
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
//Ack interrupt
TIMERG0.int_clr_timers.wdt=1;
//Watchdog got triggered because at least one task did not report in.
ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
if (!wdttask->fed_watchdog) {
cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1";
if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1";
printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
}
}
#if CONFIG_TASK_WDT_PANIC
ets_printf("Aborting.\n");
abort();
#endif
}
@@ -69,7 +88,7 @@ void task_wdt_feed() {
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
//Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
//the real watchdog timer.
while (wdttask!=NULL) {
for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) {
//See if we are at the current task.
if (wdttask->task_handle == handle) {
wdttask->fed_watchdog=true;
@@ -77,8 +96,6 @@ void task_wdt_feed() {
}
//If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
if (!wdttask->fed_watchdog) do_feed_wdt=false;
//Next entry.
wdttask=wdttask->next;
}
if (!found_task) {
@@ -91,9 +108,8 @@ void task_wdt_feed() {
if (wdt_task_list == NULL) {
wdt_task_list=newtask;
} else {
wdttask=wdt_task_list;
while (!(wdttask->next == NULL)) wdttask=wdttask->next;
wdttask->next=wdttask;
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ;
wdttask->next=newtask;
}
}
if (do_feed_wdt) {
@@ -101,6 +117,8 @@ void task_wdt_feed() {
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
//Reset fed_watchdog status
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false;
}
}
@@ -143,12 +161,13 @@ void task_wdt_init() {
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, 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);
TIMERG0.int_clr_timers.wdt=1;
TIMERG0.int_ena.wdt=1;
ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
}
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
void vApplicationIdleHook(void) {
task_wdt_feed();