Просмотр исходного кода

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

Jeroen Domburg 9 лет назад
Родитель
Сommit
75a11589a1

+ 8 - 4
components/esp32/Kconfig

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

+ 9 - 1
components/esp32/brownout.c

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

+ 8 - 4
components/esp32/int_wdt.c

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

+ 31 - 12
components/esp32/task_wdt.c

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

+ 1 - 0
components/freertos/include/freertos/FreeRTOSConfig.h

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

+ 13 - 0
components/freertos/include/freertos/panic.h

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

+ 53 - 1
components/freertos/panic.c

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

+ 0 - 1
components/freertos/tasks.c

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

+ 11 - 0
components/freertos/xtensa_vectors.S

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