Created Tue, 09 Aug 2011 23:41:35 +0000 by WestfW
Tue, 09 Aug 2011 23:41:35 +0000
The PIC32 has several exceptions not present on simpler CPUs (bus error and address error particularly.) The current error handler goes into an infinite loop (intentionally) on these exceptions, which means that relatively minor pointer issues can cause mysterious "hangs."
const char * foo = "testing"; // put string in flash
pgm_read_word(foo+1); // attempt "unaligned access"
Can the default exception handler be replaced with something more informative? I tried the obvious hack of copying it into my sketch and adding Serial.print (since I'm using serial anyway), but it didn't seem to work; I'm not sure what state the CPU is in, or what state the arduino functions NEED the CPU to be in, to do stuff" during exceptions...
static unsigned int _epc_code;
static unsigned int _excep_addr;
void _general_exception_handler(void)
{
asm volatile("mfc0 %0,$13" : "=r" (_epc_code));
asm volatile("mfc0 %0,$14" : "=r" (_excep_addr));
_epc_code = (_epc_code & 0x0000007C) >> 2;
Serial.print("Exception: ");
Serial.print(_epc_code, HEX);
Serial.print(" @ ");
Serial.println(_excep_addr, HEX);
while (1)
{
// Examine _excep_code to identify the type of exception
// Examine _excep_addr to find the address that caused the exception
}
}
Sat, 13 Aug 2011 00:08:03 +0000
It would be nice to standardize this... I opened a feature request for this (along with fix that allows try/catch blocks):
Here's the enhancement...
exceptions.h
#include <setjmp.h>
#include "WProgram.h"
#include "wiring_private.h"
#define TRY if(!(_excep_code = setjmp(_excep_buf)))
#define CATCH else
#define EXCEPTION_NUM_EXCEPTIONS 14
// declared static in case exception condition would prevent
// auto variable being created
static enum {
EXCEP_IRQ = 0, // interrupt
EXCEP_AdEL = 4, // address error exception (load or ifetch)
EXCEP_AdES, // address error exception (store)
EXCEP_IBE, // bus error (ifetch)
EXCEP_DBE, // bus error (load/store)
EXCEP_Sys, // syscall
EXCEP_Bp, // breakpoint
EXCEP_RI, // reserved instruction
EXCEP_CpU, // coprocessor unusable
EXCEP_Overflow, // arithmetic overflow
EXCEP_Trap, // trap (possible divide by zero)
EXCEP_IS1 = 16, // implementation specfic 1
EXCEP_CEU, // CorExtend Unuseable
EXCEP_C2E // coprocessor 2
} _excep_codes;
static jmp_buf _excep_buf;
static unsigned int _excep_code; // exception code corresponds to _excep_codes
static unsigned int _excep_addr; // exception address
static unsigned int _excep_stat; // status register
#ifdef __cplusplus
extern "C" {
#endif
void _general_exception_handler(unsigned cause, unsigned status) {
_excep_code = (cause & 0x0000007C) >> 2;
_excep_stat = status;
_excep_addr = __builtin_mfc0(_CP0_EPC, _CP0_EPC_SELECT);
if ((cause & 0x80000000) != 0)
_excep_addr += 4;
longjmp(_excep_buf, _excep_code);
}
#ifdef __cplusplus
}
#endif
Here's an example...
#include <exceptions.h>
#define NUMERATOR 777
#define TEST_COUNT 3
char testC;
char *c1 = &testC;
char *c2;
char str[] = {"TEST STRING"};
int denominator = TEST_COUNT;
void setup() {
Serial.begin(115200);
testC = 48;
Serial.println("============ STARTING TESTS ============");
}
void loop() {
if (denominator >= 0) {
TRY {
Serial.print("============ Testing trap exception... denominator: ");
Serial.print(denominator);
int testTrap = NUMERATOR / denominator;
Serial.print(" Result: ");
Serial.println(testTrap);
Serial.println("Successful division... Testing address exception...");
if (denominator != 2) {
memcpy(c1, str, sizeof(str));
Serial.println(c1);
} else {
memcpy(c2, str, sizeof(str));
Serial.println(c2);
Serial.println("!!!!!!!!!!! SHOULD NEVER PRINT !!!!!!!!!!!");
}
Serial.println("============ No exceptions in this loop... moving on");
} CATCH {
Serial.println();
Serial.println("#######################################################");
if (_excep_code == EXCEP_Trap) {
Serial.print("Trap exception: ");
} else if (_excep_code == EXCEP_DBE) {
Serial.print("Bus error (load/store) exception: ");
} else {
Serial.print("Unknown exception: ");
}
Serial.print(_excep_code);
Serial.println();
Serial.println("#######################################################");
}
denominator--;
} else if (denominator == -1) {
Serial.print("============ ALL TESTS COMPLETE ============");
denominator--;
}
}
... and the example output:
============ STARTING TESTS ============
============ Testing trap exception... denominator: 3 Result: 259
Successful division... Testing address exception...
TEST STRING
============ No exceptions in this loop... moving on
============ Testing trap exception... denominator: 2 Result: 388
Successful division... Testing address exception...
#######################################################
Bus error (load/store) exception: 7
#######################################################
============ Testing trap exception... denominator: 1 Result: 777
Successful division... Testing address exception...
TEST STRING
============ No exceptions in this loop... moving on
============ Testing trap exception... denominator: 0
#######################################################
Trap exception: 13
#######################################################
============ ALL TESTS COMPLETE ============
Sat, 13 Aug 2011 20:45:58 +0000
Interesting! It looks like you guys have both put a lot of thought into general exception handling. Do you guys know of any tricks for recovering from an address error due to pointer misalignment? Obviously, the best way is to identify potential problems in my code and fix them, but is there a way to write a general exception handler to handle cases that I may miss?
Sat, 13 Aug 2011 22:47:42 +0000
The exeptions.h will recover from pointer errors. Put your suspect code within the TRY block and your recovery code in the CATCH block.
Mon, 16 Jan 2012 21:12:12 +0000
I just posted ( forum/viewtopic.php?f=19&t=775 ) seconding the idea that exceptions.h become part of the package. Thanks a lot!
-Scott
Mon, 06 Feb 2012 15:52:55 +0000
There may be some legitimate reasons why they don't want to add it to the standard package that I'm just not aware of. If you would like to comment of the closed issue it can be found here.
Sun, 02 Apr 2017 15:26:34 +0000
Hi, I realize that this is a really old thread so apologies in advance.... I am trying to track down a mysterious hang.
i am running Chipkit core 1.3.1 building on Arduino IDE 1.6.12 on a CMOD board.
When I run the code posted earlier I get the following output:
============ STARTING TESTS ============
============ Testing trap exception... denominator: 3 Result: 259
Successful division... Testing address exception...
TEST STRING
============ No exceptions in this loop... moving on
============ Testing trap exception... denominator: 2 Result: 388
Successful division... Testing address exception...
so it never seems to get to the catch block after executing this line.
memcpy(c2, str, sizeof(str));
Any thoughts?
Thanks in advance, Patrick