mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-05 04:15:21 +02:00
Move panic handler and gdbstub into esp32 component, clean up wdt according to merge req suggestions
This commit is contained in:
@@ -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
|
||||
@@ -153,6 +155,45 @@ config ULP_COPROC_RESERVE_MEM
|
||||
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
|
||||
|
||||
@@ -147,19 +147,19 @@ 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);
|
||||
ets_setup_syscalls();
|
||||
do_global_ctors();
|
||||
esp_ipc_init();
|
||||
spi_flash_init();
|
||||
#if CONFIG_BROWNOUT_DET
|
||||
esp_brownout_init();
|
||||
#endif
|
||||
#if CONFIG_INT_WDT
|
||||
int_wdt_init();
|
||||
esp_int_wdt_init();
|
||||
#endif
|
||||
#if CONFIG_TASK_WDT
|
||||
task_wdt_init();
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -23,15 +23,29 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
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, no need to
|
||||
* call it explicitly.
|
||||
* @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 int_wdt_init();
|
||||
void esp_int_wdt_init();
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
void setBreakpointIfJtag(void *fn);
|
||||
void esp_set_breakpoint_if_jtag(void *fn);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -28,15 +28,25 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
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, no need to
|
||||
* call it explicitly.
|
||||
* @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 task_wdt_init();
|
||||
void esp_task_wdt_init();
|
||||
|
||||
/**
|
||||
* @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling
|
||||
@@ -47,7 +57,7 @@ void task_wdt_init();
|
||||
* @return null
|
||||
*/
|
||||
|
||||
void task_wdt_feed();
|
||||
void esp_task_wdt_feed();
|
||||
|
||||
|
||||
/**
|
||||
@@ -57,7 +67,7 @@ void task_wdt_feed();
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
void task_wdt_delete();
|
||||
void esp_task_wdt_delete();
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#ifndef _SOC_RTC_CNTL_REG_H_
|
||||
#define _SOC_RTC_CNTL_REG_H_
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
|
||||
|
||||
#include "soc.h"
|
||||
#define RTC_CNTL_OPTIONS0_REG (DR_REG_RTCCNTL_BASE + 0x0)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define __TIMG_REG_H__
|
||||
#include "soc.h"
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
|
||||
#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 ; */
|
||||
|
||||
@@ -13,18 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <stdint.h>
|
||||
@@ -37,6 +25,7 @@ This uses the TIMERG1 WDT.
|
||||
#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"
|
||||
|
||||
@@ -45,9 +34,8 @@ This uses the TIMERG1 WDT.
|
||||
|
||||
#define WDT_INT_NUM 24
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
|
||||
void int_wdt_init() {
|
||||
void esp_int_wdt_init() {
|
||||
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
@@ -73,6 +61,7 @@ void int_wdt_init() {
|
||||
}
|
||||
|
||||
|
||||
//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;
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
|
||||
#include "gdbstub.h"
|
||||
#include "panic.h"
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
#include "esp_gdbstub.h"
|
||||
#include "esp_panic.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -196,7 +195,13 @@ 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.
|
||||
/*
|
||||
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=WDT_WRITE_KEY;
|
||||
TIMERG0.wdt_feed=1;
|
||||
@@ -213,6 +218,9 @@ static void reconfigureAllWdts() {
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
|
||||
/*
|
||||
This disables all the watchdogs for when we call the gdbstub.
|
||||
*/
|
||||
static void disableAllWdts() {
|
||||
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG0.wdt_config0.en=0;
|
||||
@@ -254,7 +262,7 @@ void commonErrorHandler(XtExcFrame *frame) {
|
||||
#if CONFIG_FREERTOS_PANIC_GDBSTUB
|
||||
disableAllWdts();
|
||||
panicPutStr("Entering gdb stub now.\r\n");
|
||||
gdbstubPanicHandler(frame);
|
||||
esp_gdbstub_panic_handler(frame);
|
||||
#elif CONFIG_FREERTOS_PANIC_PRINT_REBOOT || CONFIG_FREERTOS_PANIC_SILENT_REBOOT
|
||||
panicPutStr("Rebooting...\r\n");
|
||||
for (x=0; x<100; x++) ets_delay_us(1000);
|
||||
@@ -267,7 +275,7 @@ void commonErrorHandler(XtExcFrame *frame) {
|
||||
}
|
||||
|
||||
|
||||
void setBreakpointIfJtag(void *fn) {
|
||||
void esp_set_breakpoint_if_jtag(void *fn) {
|
||||
if (!inOCDMode()) return;
|
||||
setFirstBreakpoint((uint32_t)fn);
|
||||
}
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -35,6 +27,7 @@ This uses the TIMERG0 WDT.
|
||||
#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"
|
||||
@@ -52,11 +45,6 @@ struct wdt_task_t {
|
||||
|
||||
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 IRAM_ATTR task_wdt_isr(void *arg) {
|
||||
wdt_task_t *wdttask;
|
||||
const char *cpu;
|
||||
@@ -87,7 +75,7 @@ static void IRAM_ATTR task_wdt_isr(void *arg) {
|
||||
}
|
||||
|
||||
|
||||
void task_wdt_feed() {
|
||||
void esp_task_wdt_feed() {
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
bool found_task=false, do_feed_wdt=true;
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
@@ -127,7 +115,7 @@ void task_wdt_feed() {
|
||||
}
|
||||
}
|
||||
|
||||
void task_wdt_delete() {
|
||||
void esp_task_wdt_delete() {
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
//Wdt task list can't be empty
|
||||
@@ -152,7 +140,7 @@ void task_wdt_delete() {
|
||||
}
|
||||
}
|
||||
|
||||
void task_wdt_init() {
|
||||
void esp_task_wdt_init() {
|
||||
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
@@ -178,7 +166,7 @@ void vApplicationIdleHook(void) {
|
||||
#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
|
||||
if (xPortGetCoreID()!=0) return;
|
||||
#endif
|
||||
task_wdt_feed();
|
||||
esp_task_wdt_feed();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user