diff --git a/Makefile b/Makefile
index 276d221..6e193c9 100644
--- a/Makefile
+++ b/Makefile
@@ -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)))
diff --git a/src/startup/fault.c b/src/startup/fault.c
new file mode 100644
index 0000000..291f94b
--- /dev/null
+++ b/src/startup/fault.c
@@ -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 .
+ *
+ * Copyright (C) 2016 ReservedField
+ */
+
+#include
+#include
+#include
+#include
+
+/**
+ * Hard fault:
+ * HARD
+ *
+ * Usage fault:
+ * USAGE
+ *
+ * Bus fault:
+ * BUS
+ *
+ * Memory management fault:
+ * MEM
+ *
+ * For all faults, register dump:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+// 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"
+ );
+}
diff --git a/tools/fault-decode b/tools/fault-decode
new file mode 100755
index 0000000..942851c
--- /dev/null
+++ b/tools/fault-decode
@@ -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 .
+#
+# 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])