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();

View File

@ -231,6 +231,7 @@
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_pcTaskGetTaskName 1
#if CONFIG_ENABLE_MEMORY_DEBUG
#define configENABLE_MEMORY_DEBUG 1

View File

@ -1,7 +1,20 @@
#ifndef PANIC_H
#define PANIC_H
#define PANIC_RSN_NONE 0
#define PANIC_RSN_DEBUGEXCEPTION 1
#define PANIC_RSN_DOUBLEEXCEPTION 2
#define PANIC_RSN_KERNELEXCEPTION 3
#define PANIC_RSN_COPROCEXCEPTION 4
#define PANIC_RSN_INTWDT 5
#define PANIC_RSN_MAX 5
#ifndef __ASSEMBLER__
void setBreakpointIfJtag(void *fn);
#endif
#endif

View File

@ -25,8 +25,13 @@
#include "soc/io_mux_reg.h"
#include "soc/dport_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/timer_group_struct.h"
#include "gdbstub.h"
#include "panic.h"
#define WDT_WRITE_KEY 0x50D83AA1
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
@ -130,10 +135,25 @@ static int inOCDMode() {
}
void panicHandler(XtExcFrame *frame) {
int *regs=(int*)frame;
//Please keep in sync with PANIC_RSN_* defines
const char *reasons[]={
"Unknown reason",
"Unhandled debug exception",
"Double exception",
"Unhandled kernel exception",
"Coprocessor exception",
"Interrupt wdt timeout"
};
const char *reason=reasons[0];
//The panic reason is stored in the EXCCAUSE register.
if (regs[20]<=PANIC_RSN_MAX) reason=reasons[regs[20]];
haltOtherCore();
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(xPortGetCoreID());
panicPutStr(" panic'ed.\r\n");
panicPutStr(" panic'ed (");
panicPutStr(reason);
panicPutStr(")\r\n");
if (inOCDMode()) {
asm("break.n 1");
@ -175,6 +195,33 @@ void xt_unhandled_exception(XtExcFrame *frame) {
}
//Disables all but one WDT, and allows enough time on that WDT to do what we need to do.
static void reconfigureAllWdts() {
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
TIMERG0.wdt_config0.stg0=3; //1st stage timeout: reset system
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
TIMERG0.wdt_config2=2000; //1 second before reset
TIMERG0.wdt_config0.en=1;
TIMERG0.wdt_wprotect=0;
//Disable wdt 1
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
TIMERG1.wdt_config0.en=0;
TIMERG1.wdt_wprotect=0;
}
static void disableAllWdts() {
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
TIMERG0.wdt_config0.en=0;
TIMERG0.wdt_wprotect=0;
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
TIMERG1.wdt_config0.en=0;
TIMERG0.wdt_wprotect=0;
}
/*
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
serial port and either jump to the gdb stub, halt the CPU or reboot.
@ -187,6 +234,9 @@ void commonErrorHandler(XtExcFrame *frame) {
"A6 ","A7 ","A8 ","A9 ","A10 ","A11 ","A12 ","A13 ",
"A14 ","A15 ","SAR ","EXCCAUSE","EXCVADDR","LBEG ","LEND ","LCOUNT "};
//Feed the watchdogs, so they will give us time to print out debug info
reconfigureAllWdts();
panicPutStr("Register dump:\r\n");
for (x=0; x<24; x+=4) {
@ -201,6 +251,7 @@ void commonErrorHandler(XtExcFrame *frame) {
panicPutStr("\r\n");
}
#if CONFIG_FREERTOS_PANIC_GDBSTUB
disableAllWdts();
panicPutStr("Entering gdb stub now.\r\n");
gdbstubPanicHandler(frame);
#elif CONFIG_FREERTOS_PANIC_PRINT_REBOOT || CONFIG_FREERTOS_PANIC_SILENT_REBOOT
@ -208,6 +259,7 @@ void commonErrorHandler(XtExcFrame *frame) {
for (x=0; x<100; x++) ets_delay_us(1000);
software_reset();
#else
disableAllWdts();
panicPutStr("CPU halted.\r\n");
while(1);
#endif

View File

@ -1832,7 +1832,6 @@ UBaseType_t uxTaskGetNumberOfTasks( void )
/*-----------------------------------------------------------*/
#if ( INCLUDE_pcTaskGetTaskName == 1 )
char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
TCB_t *pxTCB;

View File

@ -91,6 +91,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/
#include "xtensa_rtos.h"
#include "panic.h"
/*
Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used.
@ -462,6 +463,8 @@ _DebugExceptionVector:
jx a3
#else
wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */
movi a0,PANIC_RSN_DEBUGEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */
#endif
@ -489,6 +492,8 @@ _DoubleExceptionVector:
#if XCHAL_HAVE_DEBUG
break 1, 4 /* unhandled double exception */
#endif
movi a0,PANIC_RSN_DOUBLEEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfde /* make a0 point here not later */
@ -522,6 +527,8 @@ _xt_kernel_exc:
#if XCHAL_HAVE_DEBUG
break 1, 0 /* unhandled kernel exception */
#endif
movi a0,PANIC_RSN_KERNELEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfe /* make a0 point here not there */
@ -1024,6 +1031,8 @@ _xt_coproc_exc:
#if XCHAL_HAVE_DEBUG
break 1, 1 /* unhandled user exception */
#endif
movi a0,PANIC_RSN_COPROCEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* not in a thread (invalid) */
/* never returns */
@ -1611,6 +1620,8 @@ _xt_highint4:
off and the CPU should panic. */
rsr a0, EXCSAVE_4
wsr a0, EXCSAVE_1 /* panic handler reads this register */
movi a0,PANIC_RSN_INTWDT
wsr a0,EXCCAUSE
call0 _xt_panic