Skip to content

Commit

Permalink
Implement fault handler for debugging
Browse files Browse the repository at this point in the history
Compile the SDK with EVICSDK_FAULT_HANDLER=1 to enable it.
When you get a fault dump screen, copy what's shown into a file and
pipe it through tools/fault-decode.
  • Loading branch information
ReservedField committed Jun 29, 2016
1 parent 01a96cb commit 1efdc3a
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ AEABI_OBJS := src/aeabi/aeabi_memset-thumb2.o \
OUTDIR := lib
DOCDIR := doc

ifneq ($(EVICSDK_FAULT_HANDLER),)
OBJS_CRT0 += src/startup/fault.o
endif

# We need to find out if on cygwin or not
ifeq ($(OS),Windows_NT)
ifeq (, $(findstring cygwin, $(shell gcc -dumpmachine)))
Expand Down
124 changes: 124 additions & 0 deletions src/startup/fault.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* This file is part of eVic SDK.
*
* eVic SDK is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* eVic SDK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with eVic SDK. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2016 ReservedField
*/

#include <stdio.h>
#include <M451Series.h>
#include <Display.h>
#include <Font.h>

/**
* Hard fault:
* HARD
* <HFSR>
* Usage fault:
* USAGE
* <UFSR>
* Bus fault:
* BUS <BFSR>
* <BFAR>
* Memory management fault:
* MEM <MMFSR>
* <MMFAR>
* For all faults, register dump:
* <R0>
* <R1>
* <R2>
* <R3>
* <R12>
* <LR>
* <PC>
* <PSR>
*/

// MemManage fault status
#define FAULT_GET_MMFSR() (SCB->CFSR & 0xFF)
// Bus fault status
#define FAULT_GET_BFSR() ((SCB->CFSR >> 8) & 0xFF)
// Usage fault status
#define FAULT_GET_UFSR() ((SCB->CFSR >> 16) & 0xFFFF)

/**
* Dumps registers to display.
* This is an internal function.
*
* @param yOff Y coordinate to start putting text at.
* @param stack Pointer to the stack where the registers
* have been pushed.
*/
static void Fault_DumpRegisters(uint8_t y, uint32_t *stack) {
char buf[9];
uint8_t i;

for(i = 0; i < 8; i++) {
siprintf(buf, "%08lx", stack[i]);
Display_PutText(0, y, buf, FONT_DEJAVU_8PT);
y += FONT_DEJAVU_8PT->height;
}
}

/**
* Handles an hard fault.
* This is an internal function.
*
* @param stack Pointer to the stack where the registers
* have been pushed.
*/
void Fault_HandleHardFault(uint32_t *stack) {
char buf[16];
uint8_t mmfsr, bfsr;
uint16_t ufsr;

mmfsr = FAULT_GET_MMFSR();
bfsr = FAULT_GET_BFSR();
ufsr = FAULT_GET_UFSR();

if(!(SCB->HFSR & SCB_HFSR_FORCED_Msk)) {
siprintf(buf, "HARD\n%08lx", SCB->HFSR);
}
else if(ufsr != 0) {
siprintf(buf, "USAGE\n%04x", ufsr);
}
else if(bfsr != 0) {
siprintf(buf, "BUS %02x\n%08lx", bfsr, SCB->BFAR);
}
else if(mmfsr != 0) {
siprintf(buf, "MEM %02x\n%08lx", mmfsr, SCB->MMFAR);
}
else {
siprintf(buf, "???");
}

Display_Clear();
Display_PutText(0, 0, buf, FONT_DEJAVU_8PT);
Fault_DumpRegisters(FONT_DEJAVU_8PT->height * 2, stack);
Display_Update();

while(1);
}

__attribute__((naked)) void HardFault_Handler() {
asm volatile("@ HardFault_Handler\n\t"
"TST LR, #4\n\t"
"ITE EQ\n\t"
"MRSEQ R0, MSP\n\t"
"MRSNE R0, PSP\n\t"
"LDR R1, =Fault_HandleHardFault\n\t"
"BX R1"
);
}
153 changes: 153 additions & 0 deletions tools/fault-decode
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/python

# This file is part of eVic SDK.
#
# eVic SDK is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eVic SDK is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with eVic SDK. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2016 ReservedField

import sys

ISR_NAMES = [
'thread mode', '', 'NMI', 'HardFault', 'MemManage', 'BusFault', 'UsageFault',
'', '', '', '', 'SVCall', 'reserved (debug)', '', 'PendSV', 'SysTick', 'BOD',
'IRC', 'PWRWU', 'RAMPE', 'CLKFAIL', '', 'RTC', 'TAMPER', 'WDT', 'WWDT', 'EINT0',
'EINT1', 'EINT2', 'EINT3', 'EINT4', 'EINT5', 'GPA', 'GPB', 'GPC', 'GPD', 'GPE',
'GPF', 'SPI0', 'SPI1', 'BRAKE0', 'PWM0P0', 'PWM0P1', 'PWM0P2', 'BRAKE1', 'PWM1P0',
'PWM1P1', 'PWM1P2', 'TMR0', 'TMR1', 'TMR2', 'TMR3', 'UART0', 'UART1', 'I2C0',
'I2C1', 'PDMA', 'DAC', 'ADC00', 'ADC01', 'ACMP01', '', 'ADC02', 'ADC03', 'UART2',
'UART3', '', 'SPI2', '', 'USBD', 'USBH', 'USBOTG', 'CAN0', '', 'SC0', '', '', '',
'', 'TK'
]

def dump_hard(hfsr):
reason = '???'
if hfsr & (1 << 1):
reason = 'bus fault on vector table read'

forced = hfsr & (1 << 30)

print('Hard fault' + (' (forced)' if forced else '') + ': ' + reason)

def dump_usage(ufsr):
# We assume the fault resets the system, i.e. no sticky bits
reason = '???'
if ufsr & (1 << 0):
reason = 'undefined instruction'
elif ufsr & (1 << 1):
reason = 'invalid state'
elif ufsr & (1 << 2):
reason = 'invalid PC load'
elif ufsr & (1 << 3):
reason = 'no coprocessor'
elif ufsr & (1 << 8):
reason = 'unaligned memory access'
elif ufsr & (1 << 9):
reason = 'division by zero'

print('Usage fault: ' + reason)

def dump_bus(bfsr, bfar):
reason = '???'
if bfsr & (1 << 0):
reason = 'instruction'
elif bfsr & (1 << 1):
reason = 'data (precise)'
elif bfsr & (1 << 2):
reason = 'data (imprecise)'
elif bfsr & (1 << 3):
reason = 'exception return unstacking'
elif bfsr & (1 << 4):
reason = 'exception entry stacking'
elif bfsr & (1 << 5):
reason = 'FP lazy state preservation'

addr = '???'
if bfsr & (1 << 7):
addr = '0x{:08x}'.format(bfar)

print('Bus fault @ ' + addr + ': ' + reason)

def dump_mem(mmfsr, mmfar):
reason = '???'
if mmfsr & (1 << 0):
reason = 'instruction'
elif mmfsr & (1 << 1):
reason = 'data'
elif mmfsr & (1 << 3):
reason = 'exception return unstacking'
elif mmfsr & (1 << 4):
reason = 'exception entry stacking'
elif mmfsr & (1 << 5):
reason = 'FP lazy state preservation'

addr = '???'
if mmfsr & (1 << 7):
addr = '0x{:08x}'.format(mmfar)

print('Memory access violation @ ' + addr + ': ' + reason)

def dump_regs(regs):
reg_names = ['R0', 'R1', 'R2', 'R3', 'R12', 'LR', 'PC', 'PSR']

for i in range(len(reg_names)):
print('{:3} = 0x{:08x}'.format(reg_names[i], regs[i]))

def decode_psr(psr):
flags = []
if psr & (1 << 31):
flags.append('N')
if psr & (1 << 30):
flags.append('Z')
if psr & (1 << 29):
flags.append('C')
if psr & (1 << 28):
flags.append('V')
if psr & (1 << 27):
flags.append('Q')
flags.append('GE={:04b}'.format((psr >> 16) & 0xF))

isrnum = psr & 0x1FF
isr = 'reserved (!)'
if isrnum < len(ISR_NAMES) and ISR_NAMES[isrnum] != '':
isr = ISR_NAMES[isrnum]

iciit = ((psr >> 19) & 0xC0) | ((psr >> 10) & 0x3F)
iciit_label = 'ICI' if iciit & 0xC3 == 0 else 'IT'

thumb = psr & (1 << 24)

print('CPU flags : ' + ' '.join(flags))
print('Current ISR : ' + isr)
print('{:3} state : {:08b}'.format(iciit_label, iciit))
print('Thumb bit : ' + ('1' if thumb else '0 (!)'))


dump = [line.strip() for line in sys.stdin.readlines()]

header = dump[0].split(' ')
if header[0].lower() == 'hard':
dump_hard(int(dump[1], 16))
elif header[0].lower() == 'usage':
dump_usage(int(dump[1], 16))
elif header[0].lower() == 'bus':
dump_bus(int(header[1], 16), int(dump[1], 16))
elif header[0].lower() == 'mem':
dump_mem(int(header[1], 16), int(dump[1], 16))
else:
print('???')

regs = [int(reg, 16) for reg in dump[2:]]
dump_regs(regs)
decode_psr(regs[7])

0 comments on commit 1efdc3a

Please sign in to comment.