]> granicus.if.org Git - esp-idf/commitdiff
Disable brown-out WDT, fix thread WDT, add panic reason indication to _xt_panic()
authorJeroen Domburg <git@j0h.nl>
Tue, 25 Oct 2016 09:05:13 +0000 (17:05 +0800)
committerJeroen Domburg <git@j0h.nl>
Tue, 25 Oct 2016 09:05:13 +0000 (17:05 +0800)
components/esp32/Kconfig
components/esp32/brownout.c
components/esp32/int_wdt.c
components/esp32/task_wdt.c
components/freertos/include/freertos/FreeRTOSConfig.h
components/freertos/include/freertos/panic.h
components/freertos/panic.c
components/freertos/tasks.c
components/freertos/xtensa_vectors.S

index a2faa926a847173e365cc2eef806b5e4c3e813e0..dbd1cfb6f66eabd6219cb48c664697c8b73006b1 100644 (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
 
 
 
index 8e8805b8e5aea201c8a236defe39bc1a1433e868..97846ae8c0b835c3b53d1f3697a6a230dbfa0519 100644 (file)
 #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);
 
-}
\ No newline at end of file
+}
+
+#endif
\ No newline at end of file
index 2b4d9e08414584a79e203f97870e1b39eb89bf7a..5fb7a63bae5b666ab340838e6995d63e8cfb5a98 100644 (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;
 }
index 7ae3dfb2e8b4d3de9a631e19fb2ba00b74e1bf1b..3e4c436394ac2afe2e8561aad0ba34699ec78eb9 100644 (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();
index a5483d6bbddc84e7c90574afde5d1abe8bf1d29d..b732d1c07bb49a32208d2b17a3be9a68d59ec82c 100644 (file)
 #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
index 9e902ed20b2d296a53116329f053f81703f69f33..ba47e3d439022d4ba7110ceee3a3e939a494515e 100644 (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
\ No newline at end of file
index 6a87679d1344222d7a7db96f24aae4ea125bc698..0c912e56fc8cf57f4c537b03a6721f78501a6a91 100644 (file)
 #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
index 3cde3bf136ed17e7ac07c8e502271bf1fdbdfc27..221bf476c6fdf44592e65060f18224ab6c5f874b 100644 (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;
index 89a47f7415aa7a174cc1384471ef35cb7f0fcbf7..5b31eb0a87af3690a02c2ac9eee0c8925bdd8338 100644 (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