Merge branch 'feature/wdts' into 'master'

Feature/wdts

This adds two watchdogs to  esp-idf:

- An interrupt watchdog. Kicks in if the FreeRTOS timer interupt on either the PRO_CPU or (when configured) the APP CPU isn't called for a configurable time. Panics, displaying which CPU caused the problem and the registers that may lead to the offending code.
- A task watchdog. A task has to feed it every once in a while. If not, it will print the name of the offending tasks, as well as the tasks currently running on both CPUs, and optionally panic.

Also adds a panic reason to the panic call, as well as fixes the panic code a bit.

See merge request !148
This commit is contained in:
Ivan Grokhotkov
2016-10-27 17:09:35 +08:00
23 changed files with 881 additions and 4080 deletions
+169
View File
@@ -81,8 +81,10 @@ config TRACEMEM_RESERVE_DRAM
default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS
default 0x0
# Not implemented and/or needs new silicon rev to work
config MEMMAP_SPISRAM
bool "Use external SPI SRAM chip as main memory"
depends on ESP32_NEEDS_NEW_SILICON_REV
default "n"
help
The ESP32 can control an external SPI SRAM chip, adding the memory it contains to the
@@ -152,4 +154,171 @@ config ULP_COPROC_RESERVE_MEM
default 0
depends on !ULP_COPROC_ENABLED
choice ESP32_PANIC
prompt "Panic handler behaviour"
default FREERTOS_PANIC_PRINT_REBOOT
help
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
invoked. Configure the panic handlers action here.
config ESP32_PANIC_PRINT_HALT
bool "Print registers and halt"
help
Outputs the relevant registers over the serial port and halt the
processor. Needs a manual reset to restart.
config ESP32_PANIC_PRINT_REBOOT
bool "Print registers and reboot"
help
Outputs the relevant registers over the serial port and immediately
reset the processor.
config ESP32_PANIC_SILENT_REBOOT
bool "Silent reboot"
help
Just resets the processor without outputting anything
config ESP32_PANIC_GDBSTUB
bool "Invoke GDBStub"
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
endchoice
config ESP32_DEBUG_OCDAWARE
bool "Make exception and panic handlers JTAG/OCD aware"
default y
help
The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and
instead of panicking, have the debugger stop on the offending instruction.
config INT_WDT
bool "Interrupt watchdog"
default y
help
This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
either because a task turned off interrupts and did not turn them on for a long time, or because an
interrupt handler did not return. It will try to invoke the panic handler first and failing that
reset the SoC.
config INT_WDT_TIMEOUT_MS
int "Interrupt watchdog timeout (ms)"
depends on INT_WDT
default 10
range 10 10000
help
The timeout of the watchdog, in miliseconds. Make this higher than the FreeRTOS tick rate.
config INT_WDT_CHECK_CPU1
bool "Also watch CPU1 tick interrupt"
depends on INT_WDT && !FREERTOS_UNICORE
default y
help
Also detect if interrupts on CPU 1 are disabled for too long.
config TASK_WDT
bool "Task watchdog"
default y
help
This watchdog timer can be used to make sure individual tasks are still running.
config TASK_WDT_PANIC
bool "Invoke panic handler when Task Watchdog is triggered"
depends on TASK_WDT
default n
help
Normally, the Task Watchdog will only print out a warning if it detects it has not
been fed. If this is enabled, it will invoke the panic handler instead, which
can then halt or reboot the chip.
config TASK_WDT_TIMEOUT_S
int "Task watchdog timeout (seconds)"
depends on TASK_WDT
range 1 60
default 5
help
Timeout for the task WDT, in seconds.
config TASK_WDT_CHECK_IDLE_TASK
bool "Task watchdog watches CPU0 idle task"
depends on TASK_WDT
default y
help
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. Take Care: With this disabled, this
watchdog will trigger if no tasks register themselves within the timeout value.
config TASK_WDT_CHECK_IDLE_TASK_CPU1
bool "Task watchdog also watches CPU1 idle task"
depends on TASK_WDT_CHECK_IDLE_TASK && !FREERTOS_UNICORE
default y
help
Also check the idle task that runs on CPU1.
#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
behaviour.
choice BROWNOUT_DET_LVL_SEL
prompt "Brownout voltage level"
depends on BROWNOUT_DET
default BROWNOUT_DET_LVL_SEL_25
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
bool "2.2V"
config BROWNOUT_DET_LVL_SEL_2
bool "2.3V"
config BROWNOUT_DET_LVL_SEL_3
bool "2.4V"
config BROWNOUT_DET_LVL_SEL_4
bool "2.5V"
config BROWNOUT_DET_LVL_SEL_5
bool "2.6V"
config BROWNOUT_DET_LVL_SEL_6
bool "2.7V"
config BROWNOUT_DET_LVL_SEL_7
bool "2.8V"
endchoice
config BROWNOUT_DET_LVL
int
default 0 if BROWNOUT_DET_LVL_SEL_0
default 1 if BROWNOUT_DET_LVL_SEL_1
default 2 if BROWNOUT_DET_LVL_SEL_2
default 3 if BROWNOUT_DET_LVL_SEL_3
default 4 if BROWNOUT_DET_LVL_SEL_4
default 5 if BROWNOUT_DET_LVL_SEL_5
default 6 if BROWNOUT_DET_LVL_SEL_6
default 7 if BROWNOUT_DET_LVL_SEL_7
config BROWNOUT_DET_RESETDELAY
int "Brownout reset delay (in uS)"
depends on BROWNOUT_DET
range 0 6820
default 1000
help
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
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#if CONFIG_BROWNOUT_DET
/*
This file is 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
+17
View File
@@ -43,6 +43,9 @@
#include "esp_ipc.h"
#include "esp_log.h"
#include "esp_brownout.h"
#include "esp_int_wdt.h"
#include "esp_task_wdt.h"
#include "trax.h"
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
@@ -99,6 +102,10 @@ void IRAM_ATTR call_start_cpu0()
#if !CONFIG_FREERTOS_UNICORE
ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1);
//Un-stall the app cpu; the panic handler may have stalled it.
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M);
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M);
//Enable clock gating and reset the app cpu.
SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN);
CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL);
SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING);
@@ -144,10 +151,20 @@ void start_cpu0_default(void)
#endif
esp_set_cpu_freq(); // set CPU frequency configured in menuconfig
uart_div_modify(0, (APB_CLK_FREQ << 4) / 115200);
#if CONFIG_BROWNOUT_DET
esp_brownout_init();
#endif
#if CONFIG_INT_WDT
esp_int_wdt_init();
#endif
#if CONFIG_TASK_WDT
esp_task_wdt_init();
#endif
ets_setup_syscalls();
do_global_ctors();
esp_ipc_init();
spi_flash_init();
xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, 0);
+365
View File
@@ -0,0 +1,365 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/******************************************************************************
* Description: A stub to make the ESP32 debuggable by GDB over the serial
* port, at least enough to do a backtrace on panic. This gdbstub is read-only:
* it allows inspecting the ESP32 state
*******************************************************************************/
//ToDo: Clean up includes and sync to real rtos
#include "rom/ets_sys.h"
#include "soc/uart_reg.h"
#include "soc/io_mux_reg.h"
#include "esp_gdbstub.h"
//Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which
//implies a minimum size of about 320 bytes.
#define PBUFLEN 512
static unsigned char cmd[PBUFLEN]; //GDB command input buffer
static char chsum; //Running checksum of the output packet
#define ATTR_GDBFN
static void ATTR_GDBFN keepWDTalive() {
//ToDo for esp31/32
}
//Receive a char from the uart. Uses polling and feeds the watchdog.
static int ATTR_GDBFN gdbRecvChar() {
int i;
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) {
keepWDTalive();
}
i=READ_PERI_REG(UART_FIFO_REG(0));
return i;
}
//Send a char to the uart.
static void ATTR_GDBFN gdbSendChar(char c) {
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
WRITE_PERI_REG(UART_FIFO_REG(0), c);
}
//Send the start of a packet; reset checksum calculation.
static void ATTR_GDBFN gdbPacketStart() {
chsum=0;
gdbSendChar('$');
}
//Send a char as part of a packet
static void ATTR_GDBFN gdbPacketChar(char c) {
if (c=='#' || c=='$' || c=='}' || c=='*') {
gdbSendChar('}');
gdbSendChar(c^0x20);
chsum+=(c^0x20)+'}';
} else {
gdbSendChar(c);
chsum+=c;
}
}
//Send a string as part of a packet
static void ATTR_GDBFN gdbPacketStr(char *c) {
while (*c!=0) {
gdbPacketChar(*c);
c++;
}
}
//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent.
static void ATTR_GDBFN gdbPacketHex(int val, int bits) {
char hexChars[]="0123456789abcdef";
int i;
for (i=bits; i>0; i-=4) {
gdbPacketChar(hexChars[(val>>(i-4))&0xf]);
}
}
//Finish sending a packet.
static void ATTR_GDBFN gdbPacketEnd() {
gdbSendChar('#');
gdbPacketHex(chsum, 8);
}
//Error states used by the routines that grab stuff from the incoming gdb packet
#define ST_ENDPACKET -1
#define ST_ERR -2
#define ST_OK -3
#define ST_CONT -4
//Grab a hex value from the gdb packet. Ptr will get positioned on the end
//of the hex string, as far as the routine has read into it. Bits/4 indicates
//the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much
//hex chars as possible.
static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) {
int i;
int no;
unsigned int v=0;
char c;
no=bits/4;
if (bits==-1) no=64;
for (i=0; i<no; i++) {
c=**ptr;
(*ptr)++;
if (c>='0' && c<='9') {
v<<=4;
v|=(c-'0');
} else if (c>='A' && c<='F') {
v<<=4;
v|=(c-'A')+10;
} else if (c>='a' && c<='f') {
v<<=4;
v|=(c-'a')+10;
} else if (c=='#') {
if (bits==-1) {
(*ptr)--;
return v;
}
return ST_ENDPACKET;
} else {
if (bits==-1) {
(*ptr)--;
return v;
}
return ST_ERR;
}
}
return v;
}
//Swap an int into the form gdb wants it
static int ATTR_GDBFN iswap(int i) {
int r;
r=((i>>24)&0xff);
r|=((i>>16)&0xff)<<8;
r|=((i>>8)&0xff)<<16;
r|=((i>>0)&0xff)<<24;
return r;
}
//Read a byte from ESP32 memory.
static unsigned char ATTR_GDBFN readbyte(unsigned int p) {
int *i=(int*)(p&(~3));
if (p<0x20000000 || p>=0x80000000) return -1;
return *i>>((p&3)*8);
}
//Register file in the format exp108 gdb port expects it.
//Inspired by gdb/regformats/reg-xtensa.dat
typedef struct {
uint32_t pc;
uint32_t a[64];
uint32_t lbeg;
uint32_t lend;
uint32_t lcount;
uint32_t sar;
uint32_t windowbase;
uint32_t windowstart;
uint32_t configid0;
uint32_t configid1;
uint32_t ps;
uint32_t threadptr;
uint32_t br;
uint32_t scompare1;
uint32_t acclo;
uint32_t acchi;
uint32_t m0;
uint32_t m1;
uint32_t m2;
uint32_t m3;
uint32_t expstate; //I'm going to assume this is exccause...
uint32_t f64r_lo;
uint32_t f64r_hi;
uint32_t f64s;
uint32_t f[16];
uint32_t fcr;
uint32_t fsr;
} GdbRegFile;
GdbRegFile gdbRegFile;
/*
//Register format as the Xtensa HAL has it:
STRUCT_FIELD (long, 4, XT_STK_EXIT, exit)
STRUCT_FIELD (long, 4, XT_STK_PC, pc)
STRUCT_FIELD (long, 4, XT_STK_PS, ps)
STRUCT_FIELD (long, 4, XT_STK_A0, a0)
[..]
STRUCT_FIELD (long, 4, XT_STK_A15, a15)
STRUCT_FIELD (long, 4, XT_STK_SAR, sar)
STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg)
STRUCT_FIELD (long, 4, XT_STK_LEND, lend)
STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
// Temporary space for saving stuff during window spill
STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0)
STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1)
STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2)
STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri)
STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly)
#endif
STRUCT_END(XtExcFrame)
*/
static void dumpHwToRegfile(XtExcFrame *frame) {
int i;
long *frameAregs=&frame->a0;
gdbRegFile.pc=frame->pc;
for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i];
for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF;
gdbRegFile.lbeg=frame->lbeg;
gdbRegFile.lend=frame->lend;
gdbRegFile.lcount=frame->lcount;
gdbRegFile.sar=frame->sar;
//All windows have been spilled to the stack by the ISR routines. The following values should indicate that.
gdbRegFile.sar=frame->sar;
gdbRegFile.windowbase=0; //0
gdbRegFile.windowstart=0x1; //1
gdbRegFile.configid0=0xdeadbeef; //ToDo
gdbRegFile.configid1=0xdeadbeef; //ToDo
gdbRegFile.ps=frame->ps-PS_EXCM_MASK;
gdbRegFile.threadptr=0xdeadbeef; //ToDo
gdbRegFile.br=0xdeadbeef; //ToDo
gdbRegFile.scompare1=0xdeadbeef; //ToDo
gdbRegFile.acclo=0xdeadbeef; //ToDo
gdbRegFile.acchi=0xdeadbeef; //ToDo
gdbRegFile.m0=0xdeadbeef; //ToDo
gdbRegFile.m1=0xdeadbeef; //ToDo
gdbRegFile.m2=0xdeadbeef; //ToDo
gdbRegFile.m3=0xdeadbeef; //ToDo
gdbRegFile.expstate=frame->exccause; //ToDo
}
//Send the reason execution is stopped to GDB.
static void sendReason() {
//exception-to-signal mapping
char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7};
int i=0;
gdbPacketStart();
gdbPacketChar('T');
i=gdbRegFile.expstate&0x7f;
if (i<sizeof(exceptionSignal)) return gdbPacketHex(exceptionSignal[i], 8); else gdbPacketHex(11, 8);
gdbPacketEnd();
}
//Handle a command as received from GDB.
static int gdbHandleCommand(unsigned char *cmd, int len) {
//Handle a command
int i, j, k;
unsigned char *data=cmd+1;
if (cmd[0]=='g') { //send all registers to gdb
int *p=(int*)&gdbRegFile;
gdbPacketStart();
for (i=0; i<sizeof(GdbRegFile)/4; i++) gdbPacketHex(iswap(*p++), 32);
gdbPacketEnd();
} else if (cmd[0]=='G') { //receive content for all registers from gdb
int *p=(int*)&gdbRegFile;
for (i=0; i<sizeof(GdbRegFile)/4; i++) *p++=iswap(gdbGetHexVal(&data, 32));;
gdbPacketStart();
gdbPacketStr("OK");
gdbPacketEnd();
} else if (cmd[0]=='m') { //read memory to gdb
i=gdbGetHexVal(&data, -1);
data++;
j=gdbGetHexVal(&data, -1);
gdbPacketStart();
for (k=0; k<j; k++) {
gdbPacketHex(readbyte(i++), 8);
}
gdbPacketEnd();
} else if (cmd[0]=='?') { //Reply with stop reason
sendReason();
} else {
//We don't recognize or support whatever GDB just sent us.
gdbPacketStart();
gdbPacketEnd();
return ST_ERR;
}
return ST_OK;
}
//Lower layer: grab a command packet and check the checksum
//Calls gdbHandleCommand on the packet if the checksum is OK
//Returns ST_OK on success, ST_ERR when checksum fails, a
//character if it is received instead of the GDB packet
//start char.
static int gdbReadCommand() {
unsigned char c;
unsigned char chsum=0, rchsum;
unsigned char sentchs[2];
int p=0;
unsigned char *ptr;
c=gdbRecvChar();
if (c!='$') return c;
while(1) {
c=gdbRecvChar();
if (c=='#') { //end of packet, checksum follows
cmd[p]=0;
break;
}
chsum+=c;
if (c=='$') {
//Wut, restart packet?
chsum=0;
p=0;
continue;
}
if (c=='}') { //escape the next char
c=gdbRecvChar();
chsum+=c;
c^=0x20;
}
cmd[p++]=c;
if (p>=PBUFLEN) return ST_ERR;
}
//A # has been received. Get and check the received chsum.
sentchs[0]=gdbRecvChar();
sentchs[1]=gdbRecvChar();
ptr=&sentchs[0];
rchsum=gdbGetHexVal(&ptr, 8);
// ets_printf("c %x r %x\n", chsum, rchsum);
if (rchsum!=chsum) {
gdbSendChar('-');
return ST_ERR;
} else {
gdbSendChar('+');
return gdbHandleCommand(cmd, p);
}
}
void gdbstubPanicHandler(XtExcFrame *frame) {
dumpHwToRegfile(frame);
//Make sure txd/rxd are enabled
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
sendReason();
while(gdbReadCommand()!=ST_CONT);
while(1);
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_BROWNOUT_H
#define __ESP_BROWNOUT_H
void esp_brownout_init();
#endif
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GDBSTUB_H
#define GDBSTUB_H
#include <xtensa/config/core.h>
#include "freertos/xtensa_api.h"
void esp_gdbstub_panic_handler(XtExcFrame *frame);
#endif
+60
View File
@@ -0,0 +1,60 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_INT_WDT_H
#define __ESP_INT_WDT_H
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup Watchdog_APIs
* @{
*/
/*
This routine enables a watchdog to catch instances of processes disabling
interrupts for too long, or code within interrupt handlers taking too long.
It does this by setting up a watchdog which gets fed from the FreeRTOS
task switch interrupt. When this watchdog times out, initially it will call
a high-level interrupt routine that will panic FreeRTOS in order to allow
for forensic examination of the state of the CPU. When this interrupt
handler is not called and the watchdog times out a second time, it will
reset the SoC.
This uses the TIMERG1 WDT.
*/
/**
* @brief Initialize the interrupt watchdog. This is called in the init code if
* the interrupt watchdog is enabled in menuconfig.
*
* @param null
*
* @return null
*/
void esp_int_wdt_init();
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif
+21
View File
@@ -0,0 +1,21 @@
#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_CPU0 5
#define PANIC_RSN_INTWDT_CPU1 6
#define PANIC_RSN_MAX 6
#ifndef __ASSEMBLER__
void esp_set_breakpoint_if_jtag(void *fn);
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __ESP_TASK_WDT_H
#define __ESP_TASK_WDT_H
#ifdef __cplusplus
extern "C" {
#endif
/** \defgroup Watchdog_APIs Watchdog APIs
* @brief Watchdog APIs
*/
/** @addtogroup Watchdog_APIs
* @{
*/
/*
This routine enables a more general-purpose task watchdog: tasks can individually
feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
watchdog within the specified time. Optionally, the idle tasks can also configured
to feed the watchdog in a similar fashion, to detect CPU starvation.
This uses the TIMERG0 WDT.
*/
/**
* @brief Initialize the task watchdog. This is called in the init code, if the
* task watchdog is enabled in menuconfig.
*
* @param null
*
* @return null
*/
void esp_task_wdt_init();
/**
* @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling
* task to keep feeding the watchdog until task_wdt_delete() is called.
*
* @param null
*
* @return null
*/
void esp_task_wdt_feed();
/**
* @brief Delete the watchdog for the current task.
*
* @param null
*
* @return null
*/
void esp_task_wdt_delete();
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif
@@ -14,6 +14,9 @@
#ifndef _SOC_RTC_CNTL_REG_H_
#define _SOC_RTC_CNTL_REG_H_
/* The value that needs to be written to RTC_CNTL_WDT_WKEY to write-enable the wdt registers */
#define RTC_CNTL_WDT_WKEY_VALUE 0x50D83AA1
#include "soc.h"
#define RTC_CNTL_OPTIONS0_REG (DR_REG_RTCCNTL_BASE + 0x0)
@@ -15,6 +15,16 @@
#define __TIMG_REG_H__
#include "soc.h"
/* The value that needs to be written to TIMG_WDT_WKEY to write-enable the wdt registers */
#define TIMG_WDT_WKEY_VALUE 0x50D83AA1
/* Possible values for TIMG_WDT_STGx */
#define TIMG_WDT_STG_SEL_OFF 0
#define TIMG_WDT_STG_SEL_INT 1
#define TIMG_WDT_STG_SEL_RESET_CPU 2
#define TIMG_WDT_STG_SEL_RESET_SYSTEM 3
#define REG_TIMG_BASE(i) (DR_REG_TIMERGROUP0_BASE + i*0x1000)
#define TIMG_T0CONFIG_REG(i) (REG_TIMG_BASE(i) + 0x0000)
/* TIMG_T0_EN : R/W ;bitpos:[31] ;default: 1'h0 ; */
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_types.h>
#include "esp_err.h"
#include "esp_intr.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "esp_int_wdt.h"
#if CONFIG_INT_WDT
#define WDT_INT_NUM 24
void esp_int_wdt_init() {
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
TIMERG1.wdt_config0.level_int_en=1;
TIMERG1.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
TIMERG1.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
//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
//is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
//this interrupt.
ESP_INTR_ENABLE(WDT_INT_NUM);
}
//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 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 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
+283
View File
@@ -0,0 +1,283 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <xtensa/config/core.h>
#include "rom/rtc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "soc/uart_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/dport_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "esp_gdbstub.h"
#include "esp_panic.h"
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
task switching / interrupt code runs into an unrecoverable error. The default task stack
overflow handler also is in here.
*/
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
//printf may be broken, so we fix our own printing fns...
inline static void panicPutchar(char c) {
while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
WRITE_PERI_REG(UART_FIFO_REG(0), c);
}
inline static void panicPutStr(const char *c) {
int x=0;
while (c[x]!=0) {
panicPutchar(c[x]);
x++;
}
}
inline static void panicPutHex(int a) {
int x;
int c;
panicPutchar(' ');
for (x=0; x<8; x++) {
c=(a>>28)&0xf;
if (c<10) panicPutchar('0'+c); else panicPutchar('a'+c-10);
a<<=4;
}
}
inline static void panicPutDec(int a) {
int n1, n2;
n1=a%10;
n2=a/10;
panicPutchar(' ');
if (n2==0) panicPutchar(' '); else panicPutchar(n2+'0');
panicPutchar(n1+'0');
}
#else
//No printing wanted. Stub out these functions.
inline static void panicPutchar(char c) { }
inline static void panicPutStr(const char *c) { }
inline static void panicPutHex(int a) { }
inline static void panicPutDec(int a) { }
#endif
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) {
panicPutStr("***ERROR*** A stack overflow in task ");
panicPutStr((char*)pcTaskName);
panicPutStr(" has been detected.\r\n");
}
static const char *edesc[]={
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
"Privileged", "LoadStoreAlignment", "res", "res",
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
"InstrFetchProhibited", "res", "res", "res",
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
"LoadProhibited", "StoreProhibited", "res", "res",
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
};
void commonErrorHandler(XtExcFrame *frame);
//The fact that we've panic'ed probably means the other CPU is now running wild, possibly
//messing up the serial output, so we kill it here.
static void haltOtherCore() {
if (xPortGetCoreID()==0) {
//Kill app cpu
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M);
SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 0x21<<RTC_CNTL_SW_STALL_APPCPU_C1_S);
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 2<<RTC_CNTL_SW_STALL_APPCPU_C0_S);
} else {
//Kill pro cpu
CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_PROCPU_C1_M);
SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 0x21<<RTC_CNTL_SW_STALL_PROCPU_C1_S);
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C0_M);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 2<<RTC_CNTL_SW_STALL_PROCPU_C0_S);
}
}
//Returns true when a debugger is attached using JTAG.
static int inOCDMode() {
#if CONFIG_ESP32_DEBUG_OCDAWARE
int dcr;
int reg=0x10200C; //DSRSET register
asm("rer %0,%1":"=r"(dcr):"r"(reg));
return (dcr&0x1);
#else
return 0; //Always return no debugger is attached.
#endif
}
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 on CPU0",
"Interrupt wdt timeout on CPU1",
};
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 (");
panicPutStr(reason);
panicPutStr(")\r\n");
if (inOCDMode()) {
asm("break.n 1");
}
commonErrorHandler(frame);
}
static void setFirstBreakpoint(uint32_t pc) {
asm(
"wsr.ibreaka0 %0\n" \
"rsr.ibreakenable a3\n" \
"movi a4,1\n" \
"or a4, a4, a3\n" \
"wsr.ibreakenable a4\n" \
::"r"(pc):"a3","a4");
}
void xt_unhandled_exception(XtExcFrame *frame) {
int *regs=(int*)frame;
int x;
haltOtherCore();
panicPutStr("Guru Meditation Error of type ");
x=regs[20];
if (x<40) panicPutStr(edesc[x]); else panicPutStr("Unknown");
panicPutStr(" occurred on core ");
panicPutDec(xPortGetCoreID());
if (inOCDMode()) {
panicPutStr(" at pc=");
panicPutHex(regs[1]);
panicPutStr(". Setting bp and returning..\r\n");
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
//will kick in exactly at the context the error happened.
setFirstBreakpoint(regs[1]);
return;
}
panicPutStr(". Exception was unhandled.\r\n");
commonErrorHandler(frame);
}
/*
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
one second.
*/
static void reconfigureAllWdts() {
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
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=TIMG_WDT_STG_SEL_RESET_SYSTEM; //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=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.en=0;
TIMERG1.wdt_wprotect=0;
}
#if CONFIG_ESP32_PANIC_GDBSTUB || CONFIG_ESP32_PANIC_PRINT_HALT
/*
This disables all the watchdogs for when we call the gdbstub.
*/
static void disableAllWdts() {
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_config0.en=0;
TIMERG0.wdt_wprotect=0;
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.en=0;
TIMERG0.wdt_wprotect=0;
}
#endif
/*
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.
*/
void commonErrorHandler(XtExcFrame *frame) {
int *regs=(int*)frame;
int x, y;
const char *sdesc[]={
"PC ","PS ","A0 ","A1 ","A2 ","A3 ","A4 ","A5 ",
"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) {
for (y=0; y<4; y++) {
if (sdesc[x+y][0]!=0) {
panicPutStr(sdesc[x+y]);
panicPutStr(": ");
panicPutHex(regs[x+y+1]);
panicPutStr(" ");
}
}
panicPutStr("\r\n");
}
#if CONFIG_ESP32_PANIC_GDBSTUB
disableAllWdts();
panicPutStr("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler(frame);
#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
panicPutStr("Rebooting...\r\n");
for (x=0; x<100; x++) ets_delay_us(1000);
software_reset();
#else
disableAllWdts();
panicPutStr("CPU halted.\r\n");
while(1);
#endif
}
void esp_set_breakpoint_if_jtag(void *fn) {
if (!inOCDMode()) return;
setFirstBreakpoint((uint32_t)fn);
}
+173
View File
@@ -0,0 +1,173 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_types.h>
#include "esp_err.h"
#include "esp_intr.h"
#include "esp_attr.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "esp_log.h"
#include "esp_task_wdt.h"
#if CONFIG_TASK_WDT
static const char* TAG = "task_wdt";
typedef struct wdt_task_t wdt_task_t;
struct wdt_task_t {
TaskHandle_t task_handle;
bool fed_watchdog;
wdt_task_t *next;
};
static wdt_task_t *wdt_task_list=NULL;
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=TIMG_WDT_WKEY_VALUE;
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);
}
}
ets_printf("Tasks currently running:\n");
for (int x=0; x<portNUM_PROCESSORS; x++) {
ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
}
#if CONFIG_TASK_WDT_PANIC
ets_printf("Aborting.\n");
abort();
#endif
}
void esp_task_wdt_feed() {
wdt_task_t *wdttask=wdt_task_list;
bool found_task=false, do_feed_wdt=true;
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.
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;
found_task=true;
}
//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;
}
if (!found_task) {
//This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
//the linked list.
wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
memset(newtask, 0, sizeof(wdt_task_t));
newtask->task_handle=handle;
newtask->fed_watchdog=true;
if (wdt_task_list == NULL) {
wdt_task_list=newtask;
} else {
for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ;
wdttask->next=newtask;
}
}
if (do_feed_wdt) {
//All tasks have checked in; time to feed the hw watchdog.
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
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;
}
}
void esp_task_wdt_delete() {
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
wdt_task_t *wdttask=wdt_task_list;
//Wdt task list can't be empty
if (!wdt_task_list) {
ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
return;
}
if (handle==wdt_task_list) {
//Current task is first on list.
wdt_task_list=wdt_task_list->next;
free(wdttask);
} else {
//Find current task in list
while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
if (!wdttask->next) {
ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
return;
}
wdt_task_t *freeme=wdttask->next;
wdttask->next=wdttask->next->next;
free(freeme);
}
}
void esp_task_wdt_init() {
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
TIMERG0.wdt_config0.level_int_en=1;
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt
TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset
TIMERG0.wdt_config0.en=1;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
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);
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) {
#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
if (xPortGetCoreID()!=0) return;
#endif
esp_task_wdt_feed();
}
#endif
#endif