Cortex-M Fault

From SEGGER Wiki
Revision as of 19:42, 4 March 2020 by Johannes (talk | contribs) (Created page with "Cortex-M CPUs raise a fault exception on a crash of the system. Illegal memory writes and reads, access to unpowered peripherals, execution of invalid instructions, division b...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Cortex-M CPUs raise a fault exception on a crash of the system. Illegal memory writes and reads, access to unpowered peripherals, execution of invalid instructions, division by zero, and other issues can cause such a crash. Usually in all cases a HardFault exception is raised. For certain faults a different exception can be enabled to specifically handle these cases.

Cortex-M Fault Exceptions

Cortex-M processors implement different fault exceptions.

HardFault Exception

The HardFault is the default exception, raised on any error which is not associated with another (enabled) exception.

The HardFault has a fixed priority of -1, i.e. it has a higher priority than all other interrupts and exceptions except for NMI. Therefore a HardFault exception handler can always be entered when an error happens in application code, an interrupt, or another exception. The HardFault is exception number 3 in the vector table with IRQ number -13.

MemManage Exception

The MemManage exception is available with the use of a Memory Protection Unit (MPU) to raise an exception on memory access violations.

The MemManage is exception number 4 in the vector table, IRQ Number -12, and has a configurable priority.

BusFault Exception

The BusFault exception is raised on any memory access error. E.g. by illegal read, write, or vector catch.

The BusFault is exception number 5 in the vector table, IRQ number -11, and has configurable priority. BusFaults can explicitly be enabled in the system control block (SCB). When BusFault is not enabled, a HardFault is raised.

UsageFault Exception

The UsageFault exception is raised on execution errors. Unaligned access on load/store multiple instructions are always caught. Exceptions on other unaligned access, as well as division by zero can be additionally enabled in the SCB.

The UsageFault is exception number 6 in the vector table, IRQ number -10, and has configurable priority. When UsageFault is not enabled, a HardFault is raised instead.

Exception Handling

On any exception the exception handler as read from the vector table is called, just like on interrupts. Exception handling is usually done differently during development and in production firmware.

Exception Handling during Development

During development of a firmware, crashes can happen. Developers usually want to analyze what went wrong to resolve the error.

The Cortex-M NVIC provides various registers to analyze the reason of a crash. Additionally, in most cases the call stack and the register contents of where the crash happened can be restored.

The exception handler can contain development code to gather the required information, to easily make them available in a debugger.

SEGGER HardFault Handler

;/*********************************************************************
;*
;*      HardFault_Handler()
;*
;*  Function description
;*    Evaluates the used stack (MSP, PSP) and passes the appropiate
;*    stack pointer to the HardFaultHandler "C"-routine.
;*
;*  Notes
;*    (1) Ensure that HardFault_Handler is part of the exception table
;*/
HardFault_Handler:
BusFault_Handler:
UsageFault_Handler:
MemManage_Handler:
#if (defined (__IAR_SYSTEMS_ASM__) && (__ARM6M__) && (__CORE__ == __ARM6M__)) || \
    (defined(__CC_ARM) || (defined __clang__)) && (__TARGET_ARCH_6S_M)        || \
    (defined (__GNUC__) && ((__ARM_ARCH_6M__) || (__ARM_ARCH_8M_BASE__)))
        ;// This version is for Cortex M0
        movs   R0, #4
        mov    R1, LR
        tst    R0, R1            ;// Check EXC_RETURN in Link register bit 2.
        bne    Uses_PSP
        mrs    R0, MSP           ;// Stacking was using MSP.
        b      Pass_StackPtr
Uses_PSP:
        mrs    R0, PSP           ;// Stacking was using PSP.
Pass_StackPtr:
        ldr    R2,=HardFaultHandler
        bx     R2                ;// Stack pointer passed through R0. 
#else
        ;// This version is for Cortex M3, Cortex M4 and Cortex M4F
        tst    LR, #4            ;// Check EXC_RETURN in Link register bit 2.
        ite    EQ
        mrseq  R0, MSP           ;// Stacking was using MSP.
        mrsne  R0, PSP           ;// Stacking was using PSP.
        b      HardFaultHandler  ;// Stack pointer passed through R0.
#endif
        .end

/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*            (c) 2014 - 2020 SEGGER Microcontroller GmbH             *
*                                                                    *
*           www.segger.com     Support: support@segger.com           *
*                                                                    *
**********************************************************************
*                                                                    *
* All rights reserved.                                               *
*                                                                    *
* Redistribution and use in source and binary forms, with or         *
* without modification, are permitted provided that the following    *
* conditions are met:                                                *
*                                                                    *
* - Redistributions of source code must retain the above copyright   *
*   notice, this list of conditions and the following disclaimer.    *
*                                                                    *
* - Neither the name of SEGGER Microcontroller GmbH                  *
*   nor the names of its contributors may be used to endorse or      *
*   promote products derived from this software without specific     *
*   prior written permission.                                        *
*                                                                    *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND             *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,        *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF           *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE           *
* DISCLAIMED.                                                        *
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR        *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR           *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;    *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF      *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
* DAMAGE.                                                            *
*                                                                    *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------
Purpose : Generic SEGGER HardFault handler for Cortex-M
Literature:
  [1]  Analyzing HardFaults on Cortex-M CPUs (https://www.segger.com/downloads/appnotes/AN00016_AnalyzingHardFaultsOnCortexM.pdf)

Additional information:
  This HardFault handler enables user-friendly analysis of hard faults
  in debug configurations.
  If a release configuration requires a HardFault handler,
  a specific HardFault handler should be included instead,
  which for example issues a reset or lits an error LED.
--------  END-OF-HEADER  ---------------------------------------------
*/

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
#define SYSHND_CTRL  (*(volatile unsigned int*)  (0xE000ED24u))  // System Handler Control and State Register
#define NVIC_MFSR    (*(volatile unsigned char*) (0xE000ED28u))  // Memory Management Fault Status Register
#define NVIC_BFSR    (*(volatile unsigned char*) (0xE000ED29u))  // Bus Fault Status Register
#define NVIC_UFSR    (*(volatile unsigned short*)(0xE000ED2Au))  // Usage Fault Status Register
#define NVIC_HFSR    (*(volatile unsigned int*)  (0xE000ED2Cu))  // Hard Fault Status Register
#define NVIC_DFSR    (*(volatile unsigned int*)  (0xE000ED30u))  // Debug Fault Status Register
#define NVIC_BFAR    (*(volatile unsigned int*)  (0xE000ED38u))  // Bus Fault Manage Address Register
#define NVIC_AFSR    (*(volatile unsigned int*)  (0xE000ED3Cu))  // Auxiliary Fault Status Register

#ifndef   DEBUG           // Should be overwritten by project settings
  #define DEBUG      (0)  // in debug builds
#endif

/*********************************************************************
*
*       Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
  extern "C" {
#endif
void HardFaultHandler(unsigned int* pStack);
#ifdef __cplusplus
  }
#endif

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
#if DEBUG
static volatile unsigned int _Continue;  // Set this variable to 1 to run further

static struct {
  struct {
    volatile unsigned int r0;            // Register R0
    volatile unsigned int r1;            // Register R1
    volatile unsigned int r2;            // Register R2
    volatile unsigned int r3;            // Register R3
    volatile unsigned int r12;           // Register R12
    volatile unsigned int lr;            // Link register
    volatile unsigned int pc;            // Program counter
    union {
      volatile unsigned int byte;
      struct {
        unsigned int IPSR : 8;           // Interrupt Program Status register (IPSR)
        unsigned int EPSR : 19;          // Execution Program Status register (EPSR)
        unsigned int APSR : 5;           // Application Program Status register (APSR)
      } bits;
    } psr;                               // Program status register.
  } SavedRegs;

  union {
    volatile unsigned int byte;
    struct {
      unsigned int MEMFAULTACT    : 1;   // Read as 1 if memory management fault is active
      unsigned int BUSFAULTACT    : 1;   // Read as 1 if bus fault exception is active
      unsigned int UnusedBits1    : 1;
      unsigned int USGFAULTACT    : 1;   // Read as 1 if usage fault exception is active
      unsigned int UnusedBits2    : 3;
      unsigned int SVCALLACT      : 1;   // Read as 1 if SVC exception is active
      unsigned int MONITORACT     : 1;   // Read as 1 if debug monitor exception is active
      unsigned int UnusedBits3    : 1;
      unsigned int PENDSVACT      : 1;   // Read as 1 if PendSV exception is active
      unsigned int SYSTICKACT     : 1;   // Read as 1 if SYSTICK exception is active
      unsigned int USGFAULTPENDED : 1;   // Usage fault pended; usage fault started but was replaced by a higher-priority exception
      unsigned int MEMFAULTPENDED : 1;   // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception
      unsigned int BUSFAULTPENDED : 1;   // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
      unsigned int SVCALLPENDED   : 1;   // SVC pended; SVC was started but was replaced by a higher-priority exception
      unsigned int MEMFAULTENA    : 1;   // Memory management fault handler enable
      unsigned int BUSFAULTENA    : 1;   // Bus fault handler enable
      unsigned int USGFAULTENA    : 1;   // Usage fault handler enable
    } bits;
  } syshndctrl;                          // System Handler Control and State Register (0xE000ED24)

  union {
    volatile unsigned char byte;
    struct {
      unsigned char IACCVIOL    : 1;     // Instruction access violation
      unsigned char DACCVIOL    : 1;     // Data access violation
      unsigned char UnusedBits  : 1;
      unsigned char MUNSTKERR   : 1;     // Unstacking error
      unsigned char MSTKERR     : 1;     // Stacking error
      unsigned char UnusedBits2 : 2;
      unsigned char MMARVALID   : 1;     // Indicates the MMAR is valid
    } bits;
  } mfsr;                                // Memory Management Fault Status Register (0xE000ED28)

  union {
    volatile unsigned int byte;
    struct {
      unsigned int IBUSERR    : 1;       // Instruction access violation
      unsigned int PRECISERR  : 1;       // Precise data access violation
      unsigned int IMPREISERR : 1;       // Imprecise data access violation
      unsigned int UNSTKERR   : 1;       // Unstacking error
      unsigned int STKERR     : 1;       // Stacking error
      unsigned int UnusedBits : 2;
      unsigned int BFARVALID  : 1;       // Indicates BFAR is valid
    } bits;
  } bfsr;                                // Bus Fault Status Register (0xE000ED29)
  volatile unsigned int bfar;            // Bus Fault Manage Address Register (0xE000ED38)

  union {
    volatile unsigned short byte;
    struct {
      unsigned short UNDEFINSTR : 1;     // Attempts to execute an undefined instruction
      unsigned short INVSTATE   : 1;     // Attempts to switch to an invalid state (e.g., ARM)
      unsigned short INVPC      : 1;     // Attempts to do an exception with a bad value in the EXC_RETURN number
      unsigned short NOCP       : 1;     // Attempts to execute a coprocessor instruction
      unsigned short UnusedBits : 4;
      unsigned short UNALIGNED  : 1;     // Indicates that an unaligned access fault has taken place
      unsigned short DIVBYZERO  : 1;     // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
    } bits;
  } ufsr;                                // Usage Fault Status Register (0xE000ED2A)

  union {
    volatile unsigned int byte;
    struct {
      unsigned int UnusedBits  : 1;
      unsigned int VECTBL      : 1;      // Indicates hard fault is caused by failed vector fetch
      unsigned int UnusedBits2 : 28;
      unsigned int FORCED      : 1;      // Indicates hard fault is taken because of bus fault/memory management fault/usage fault
      unsigned int DEBUGEVT    : 1;      // Indicates hard fault is triggered by debug event
    } bits;
  } hfsr;                                // Hard Fault Status Register (0xE000ED2C)

  union {
    volatile unsigned int byte;
    struct {
      unsigned int HALTED   : 1;         // Halt requested in NVIC
      unsigned int BKPT     : 1;         // BKPT instruction executed
      unsigned int DWTTRAP  : 1;         // DWT match occurred
      unsigned int VCATCH   : 1;         // Vector fetch occurred
      unsigned int EXTERNAL : 1;         // EDBGRQ signal asserted
    } bits;
  } dfsr;                                // Debug Fault Status Register (0xE000ED30)

  volatile unsigned int afsr;            // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
} HardFaultRegs;
#endif

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       HardFaultHandler()
*
*  Function description
*    C part of the hard fault handler which is called by the assembler
*    function HardFault_Handler
*/
void HardFaultHandler(unsigned int* pStack) {
  //
  // In case we received a hard fault because of a breakpoint instruction, we return.
  // This may happen when using semihosting for printf outputs and no debugger is connected,
  // i.e. when running a "Debug" configuration in release mode.
  //
  if (NVIC_HFSR & (1u << 31)) {
    NVIC_HFSR |=  (1u << 31);     // Reset Hard Fault status
    *(pStack + 6u) += 2u;         // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction.
    return;                       // Return to interrupted application
  }
#if DEBUG
  //
  // Read NVIC registers
  //
  HardFaultRegs.syshndctrl.byte = SYSHND_CTRL;  // System Handler Control and State Register
  HardFaultRegs.mfsr.byte       = NVIC_MFSR;    // Memory Fault Status Register
  HardFaultRegs.bfsr.byte       = NVIC_BFSR;    // Bus Fault Status Register
  HardFaultRegs.bfar            = NVIC_BFAR;    // Bus Fault Manage Address Register
  HardFaultRegs.ufsr.byte       = NVIC_UFSR;    // Usage Fault Status Register
  HardFaultRegs.hfsr.byte       = NVIC_HFSR;    // Hard Fault Status Register
  HardFaultRegs.dfsr.byte       = NVIC_DFSR;    // Debug Fault Status Register
  HardFaultRegs.afsr            = NVIC_AFSR;    // Auxiliary Fault Status Register
  //
  // Halt execution
  // If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution.
  //
//  _Continue = 0u;
//  while (_Continue == 0u) {
//  }
  //
  // Read saved registers from the stack.
  //
  HardFaultRegs.SavedRegs.r0       = pStack[0];  // Register R0
  HardFaultRegs.SavedRegs.r1       = pStack[1];  // Register R1
  HardFaultRegs.SavedRegs.r2       = pStack[2];  // Register R2
  HardFaultRegs.SavedRegs.r3       = pStack[3];  // Register R3
  HardFaultRegs.SavedRegs.r12      = pStack[4];  // Register R12
  HardFaultRegs.SavedRegs.lr       = pStack[5];  // Link register LR
  HardFaultRegs.SavedRegs.pc       = pStack[6];  // Program counter PC
  HardFaultRegs.SavedRegs.psr.byte = pStack[7];  // Program status word PSR
  //
  // Halt execution
  // To step out of the HardFaultHandler, change the variable value to != 0.
  //
  _Continue = 0u;
  while (_Continue == 0u) {
  }
#else
  //
  // If this module is included in a release configuration, simply stay in the HardFault handler
  //
  (void)pStack;
  do {
  } while (1);
#endif
}

/*************************** End of file ****************************/

Exception Handling in productive Firmware

Even well tested firmwares might crash. Therefore exceptions should also be caught in release firmwares. They can be handled to recover, by simply resetting the system. In more advanced scenarios, a crash dump could be generated before the reset, to be sent or saved on reboot.

Exception Analysis

The system stat that led to an exception can be analyzed with the HardFault Handler described above. Additionally some debuggers, such as Ozone provide special features to simplify the analysis.

With Ozone no additional exception handler code is required. When Ozone detects that the target system has crashed, it automatically analyzes the target state and provides all necessary information. The Exception Window displays the reason for the crash along with where it happened and the additional NVIC registers. The Call Stack Window can also unwind from an exception to enable easy navigation to the location of error, even across multiple exceptions.

For more information refer to https://work.segger.com/products/development-tools/ozone-j-link-debugger/technology/fault-analysis

Example Exception Test Application

The following example application can cause different crashes. It can be used as a reference for analysis, tests of handling in development firmware, or tests with the debugger.

/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*            (c) 2014 - 2020 SEGGER Microcontroller GmbH             *
*                                                                    *
*           www.segger.com     Support: support@segger.com           *
*                                                                    *
**********************************************************************
*                                                                    *
* All rights reserved.                                               *
*                                                                    *
* Redistribution and use in source and binary forms, with or         *
* without modification, are permitted provided that the following    *
* conditions are met:                                                *
*                                                                    *
* - Redistributions of source code must retain the above copyright   *
*   notice, this list of conditions and the following disclaimer.    *
*                                                                    *
* - Neither the name of SEGGER Microcontroller GmbH                  *
*   nor the names of its contributors may be used to endorse or      *
*   promote products derived from this software without specific     *
*   prior written permission.                                        *
*                                                                    *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND             *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,        *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF           *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE           *
* DISCLAIMED.                                                        *
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR        *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR           *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;    *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF      *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
* DAMAGE.                                                            *
*                                                                    *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------

Purpose : Cortex-M Fault Exception test application.

*/

#include <stdio.h>
#include <stdlib.h>
#include "STM32F4xx.h"


#define _USE_DISTINCT_FAULTS    // If defined, enable usage fault, bus fault, and mem manage fault
#define _NICE_CALL_STACK        // If defined, generate a "useful" call stack to test stack unwinding


/*********************************************************************
*
*       Local functions, generate fault with nice call stack
*
**********************************************************************
*/
#ifdef _NICE_CALL_STACK
#include <string.h>

static const char _abLogo[128*32];
static const char _abIcon[32*32];

#define _NUM_IMAGES 2
static struct {
  const char* sName;
  int         Width;
  int         Height;
  const char* pData;
} _aImg[_NUM_IMAGES] = {
  {"Logo.bmp", 128, 32, _abLogo},
  {"Icon.bmp", 32,  32, _abIcon}
};

/*********************************************************************
*
*       _GetPixel()
*
*  Function description
*   Get one pixel within an image.
*/
static char _GetPixel(const char* pImg, int x, int y, int Width, int Height) {
  return *(pImg +  (y * Width) + x);
}

/*********************************************************************
*
*       _GetImage()
*
*  Function description
*   Find the given image and get its informaiton.
*
*  Return value
*    == NULL: Image not found. Width and height not valid.
*    != NULL: Pointer to the image data.
*/
static const char* _GetImage(const char* sName, int* pWidth, int* pHeight) {
  int i;

  for (i = 0; i < _NUM_IMAGES; i++) {
    if (strcmp(sName, _aImg[i].sName) == 0) {
      *pWidth   = _aImg[i].Width;
      *pHeight  = _aImg[i].Height;
      return  _aImg[i].pData;
    }
  }

  return NULL;
}

/*********************************************************************
*
*       _GetFrameBuf()
*
*  Function description
*   Get the frame buffer and its dimensions.
*/
static char* _GetFrameBuf(int* pWidth, int* pHeight) {
  
  *pWidth = 1920;
  *pHeight = 1080;

  return (char*)0x20000000;
}

/*********************************************************************
*
*       _RenderImage()
*
*  Function description
*   Render an image to be written to the display.
*/
static void _RenderImage(void) {
  const char* pImg;
  char* pFrameBuf;
  char  Pixel;
  unsigned Width;
  unsigned Height;
  unsigned x;
  unsigned y;

  pFrameBuf = _GetFrameBuf(&Width, &Height);
  pImg = _GetImage("logo.bmp", &Width, &Height);

  for (y = 0; y < Height; y++) {
    for (x = 0; x < Width; x++) {
      Pixel = _GetPixel(pImg, x, y, Width, Height);
      *pFrameBuf = Pixel;
    }
  }
}
#endif

/*********************************************************************
*
*       Local functions, Bus faults
*
**********************************************************************
*/

/*********************************************************************
*
*       _IllegalRead()
*
*  Function description
*   Trigger a bus fault or hard fault by reading from a reserved address.
*
*  Additional Information
*    Bus fault is immediately triggered on the read instruction.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.BFSR = 0x00000082
*        PRECISERR = 1        - Precise data access violation
*        BFARVALID = 1        - BFAR is valid
*      NVIC.BFAR = 0x00100000 - The address read from
*/
static int _IllegalRead(void) {
  int r;
  volatile unsigned int* p;

  p = (unsigned int*)0x00100000;  // 0x00100000-0x07FFFFFF is reserved on STM32F4
  r = *p;

  return r;
}

/*********************************************************************
*
*       _IllegalWrite()
*
*  Function description
*   Trigger a bus fault or hard fault by writing to a reserved address.
*
*  Additional Information
*    Bus fault is triggered some instructions after the write instruction.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.BFSR = 0x00000004
*        IMPREISERR = 1       - Imprecise data access violation
*        BFARVALID = 0        - BFAR not valid
*/
static int _IllegalWrite(void) {
  int r;
  volatile unsigned int* p;

  p = (unsigned int*)0x00100000;  // 0x00100000-0x07FFFFFF is reserved on STM32F4
  *p = 0x00BADA55;

  return r;
}

/*********************************************************************
*
*       _IllegalFunc()
*
*  Function description
*   Trigger a bus fault or hard fault by executing at a reserved address.
*
*  Additional Information
*    Bus fault is triggered on execution at the invalid address.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.BFSR = 0x00000001
*        IBUSERR = 1          - Bus fault on instruction prefetch
*/
static int _IllegalFunc(void) {
  int r;
  int (*pF)(void);

  pF = (int(*)(void))0x00100000;  // 0x00100000-0x07FFFFFF is reserved on STM32F4
  
  r = pF();

  return r;
}

/*********************************************************************
*
*       Local functions, Usage Faults
*
**********************************************************************
*/

/*********************************************************************
*
*       _NoThumbFunc()
*
*  Function description
*   Trigger a usage fault or hard fault by executing an address without thumb bit set.
*
*  Additional Information
*    Usage fault is triggered on execution at the invalid address.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.UFSR = 0x0002
*        INVSTATE = 1         - Instruction execution with invalid state
*/
static int _NoThumbFunc(void) {
  int r;
  int (*pF)(void);

  pF = (int(*)(void))0x00000000;
  
  r = pF();

  return r;
}

/*********************************************************************
*
*       _UndefInst()
*
*  Function description
*   Trigger a usage fault or hard fault by executing an undefined instruction.
*
*  Additional Information
*    Usage fault is triggered on execution at the invalid address.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.UFSR = 0x0001
*        UNDEFINSTR = 1       - Undefined instruction executed
*/
static int _UndefInst(void) {
  static const unsigned short _UDF[4] = {0xDEAD, 0xDEAD, 0xDEAD, 0xDEAD};
  int r;
  int (*pF)(void);

  pF = (int(*)(void))(((char*)&_UDF) + 1);
  
  r = pF();

  return r;
}

/*********************************************************************
*
*       _UnalignedAccess()
*
*  Function description
*   Trigger a usage fault or hard fault by an unaligned word access.
*
*  Additional Information
*    Usage fault is triggered immediately on the read or write instruction.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.UFSR = 0x0100
*        UNALIGNED = 1        - Unaligned memory access
*/
static int _UnalignedAccess(void) {
  int r;
  volatile unsigned int* p;

  p = (unsigned int*)0x20000002;  // 0x00100000-0x07FFFFFF is reserved on STM32F4
  r = *p;

  return r;
}

/*********************************************************************
*
*       _DivideByZero()
*
*  Function description
*   Trigger a usage fault or hard fault by dividing by zero.
*
*  Additional Information
*    Usage fault is triggered immediately on the divide instruction.
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x40000000
*        FORCED = 1           - bus fault/memory management fault/usage fault escalated to hard fault
*      NVIC.UFSR = 0x0200
*        DIVBYZERO = 1        - Divide-by-zero fault
*/
static int _DivideByZero(void) {
  int r;
  volatile unsigned int a;
  volatile unsigned int b;

  a = 1;
  b = 0;
  r = a / b;

  return r;
}

/*********************************************************************
*
*       Local functions, Hard faults
*
**********************************************************************
*/

/*********************************************************************
*
*       _IllegalVector()
*
*  Function description
*   Trigger a hard fault by interrupt with illegal vector table.
*
*  Additional Information
*    Related NVIC registers on hard fault:
*      NVIC.HFSR = 0x00000002
*        VECTTBL = 1           - Vector table read fault
*/
static int _IllegalVector(void) {
  int r;

  SCB->VTOR = 0x001000000;            // Relocate vector table to illegal address  
  SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; // Trigger PendSV exception to read invalid vector

  __ISB();
  __DSB();

  return r;
}

/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/
/*********************************************************************
*
*       main()
*
*  Function description
*   Application entry point.
*/
#include <time.h>

void main(void) {
  int r;
  //
  // Enable fault on divide-by-zero and unaligned access
  //
  SCB->CCR   |= SCB_CCR_DIV_0_TRP_Msk
             |  SCB_CCR_UNALIGN_TRP_Msk;

#ifdef _USE_DISTINCT_FAULTS
  //
  // Enable usage fault, bus fault, and mem manage fault
  //
  SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk 
             |  SCB_SHCSR_BUSFAULTENA_Msk
             |  SCB_SHCSR_MEMFAULTENA_Msk; // enable Usage-/Bus-/MPU Fault
#endif

#ifdef _NICE_CALL_STACK // Generate a "useful" call stack to test stack unwinding
  _RenderImage();
#endif
  //
  // Un-comment one of the following function calls to test the corresponding fault
  //
  //r = _UndefInst();
  //r = _NoThumbFunc();
  //r = _IllegalFunc();
  //r = _UnalignedAccess();
  //r = _DivideByZero();
  //r = _IllegalWrite();
  //r = _IllegalRead();
  //r = _IllegalVector();

  do {
    r++;
  } while (1);
}

/*************************** End of file ****************************/