mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 00:51:42 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			221 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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 "gdbstub.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_FREERTOS_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_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C1<<RTC_CNTL_SW_STALL_APPCPU_C1_S);
 | 
						|
		SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 0x21<<RTC_CNTL_SW_STALL_APPCPU_C1_S);
 | 
						|
		CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C0<<RTC_CNTL_SW_STALL_APPCPU_C0_S);
 | 
						|
		SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 2<<RTC_CNTL_SW_STALL_APPCPU_C0_S);
 | 
						|
	} else {
 | 
						|
		//Kill pro cpu
 | 
						|
		CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C1<<RTC_CNTL_SW_STALL_PROCPU_C1_S);
 | 
						|
		SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, 0x21<<RTC_CNTL_SW_STALL_PROCPU_C1_S);
 | 
						|
		CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_PROCPU_C0<<RTC_CNTL_SW_STALL_PROCPU_C0_S);
 | 
						|
		SET_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, 2<<RTC_CNTL_SW_STALL_PROCPU_C0_S);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//Returns true when a debugger is attached using JTAG.
 | 
						|
static int inOCDMode() {
 | 
						|
#if CONFIG_FREERTOS_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) {
 | 
						|
	haltOtherCore();
 | 
						|
	panicPutStr("Guru Meditation Error: Core ");
 | 
						|
	panicPutDec(xPortGetCoreID());
 | 
						|
	panicPutStr(" panic'ed.\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);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
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  "};
 | 
						|
 | 
						|
	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_FREERTOS_PANIC_GDBSTUB
 | 
						|
	panicPutStr("Entering gdb stub now.\r\n");
 | 
						|
	gdbstubPanicHandler(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);
 | 
						|
	software_reset();
 | 
						|
#else
 | 
						|
	panicPutStr("CPU halted.\r\n");
 | 
						|
	while(1);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void setBreakpointIfJtag(void *fn) {
 | 
						|
	if (!inOCDMode()) return;
 | 
						|
	setFirstBreakpoint((uint32_t)fn);
 | 
						|
}
 |