The S32K144 MCU ARM Cortex M4 core processor handles fault exceptions using four handlers.
Their CMSIS functions and corresponding faults are shown below:
Usage faults are caused by an application that incorrectly uses Cortex M4 processor trying
- execute an undefined instruction
- execute an instruction that makes illegal use of the Execution Program Status Register (EPSR), typically, this processor support only Thumb instruction set and it requires that all branch targets should be indicated as odd numbers, having bit set.
- perform an illegal load of EXC_RETURN to the PC
- access a coprocessor if the access is denied or privileged only (configurable in CPACR)
- make an unaligned memory access
- execute an SDIV or UDIV instruction with a divisor of 0
The Division by zero fault detection is disabled by default which means that such an operation returns zero. Similarly, the Cortex-M4 processor supports unaligned access for certain instruction. Exception trap on both division by zero and unaligned access (for every instruction) can be enabled in Configuration and Control Register (CCR).
Bus fault occurs when a bus slave returns an error response during
- floating-point lazy state preservation
- stacking for an exception entry
- unstack for an exception return
- prefetching an instruction
Beside these faults listed above, there are also bus faults labeled as Precise and Imprecise. Imprecise bus fault occurs when an application writes to buffered memory region and continues to execute subsequent instruction before the actual bus fault is detected. Therefore, at the time the exception rises the program counter doesn’t point to the instruction that has caused the bus fault. For debugging purposes, it is necessary to have “precise” program counter value to know which instruction has caused the fault exception. Imprecise bus fault can be forced to be precise by disabling the Write Buffer in ACTLR register. This however might decrease the CPU performance.
Typically, these exceptions rise on an attempt to access regions that are protected by core Memory Protection Unit.
- attempt to load or store at a protected location
- attempted an instruction fetch from a protected location
- protection violation during floating-point lazy state preservation
S32K144 MCU implements its own Memory Protection Unit and other protections for certain registers etc. All these protections have their own mechanisms for error handling and don’t trigger core fault exceptions. Nevertheless, as the attached example code shows, an attempt to fetch instruction from a peripheral memory region causes a MemManage fault exception.
Note: The Cortex M4 processor doesn’t implement fault exception on a stack overflow.
This handler is the only one that has a fixed priority (-1) and is always enabled. In case other handlers are disabled (in SHCSR register), all faults are escalated to this handler. The escalation take place also when a fault occurs during another fault handling execution. There is one fault that is always handled directly by HardFault handler and it is a fault on a vector table read during an exception processing.
Priority of exception fault handlers
Fault exception handlers priority, beside HardFault handler (priority -1), are configurable in fields PRI_4, PRI_5 and PRI_6 of SHPR1 register. These fields are byte-accessible and Cortex M4 support 255 priority levels. However, S32K144 device supports only 16 priority level. Therefore, priorities are configurable only in the four most significant bits of PRI_4, PRI_5 and PRI_6. It is similar to NVIC IPR registers as shown below.
The lower priority number is set, the higher priority. By default, all handlers have priority set to zero.
Status and address registers for fault exceptions
Configurable Fault Status Register (CSFR) consists from three status registers for Usage Fault (UFSR), Bus Fault (BFSR), and Memory Management Fault (MMFSR).
These registers are bit fields where each bit represents a particular fault exception, meaning that if an exception rises a corresponding bit is set and can be read by an exception handler or perhaps more convenient in a system register window in an IDE. Below is a screenshot from S32 Design Studio.
There are also two auxiliary address registers. Bus Fault Address Register (BFAR) holds an address that has caused a Bus Fault provided BFARVALID bit is set in BFSR register. Similarly, Memory Manage Address Register (MMAR) holds an address that has caused a MemManage Fault provided MMARVALID bit is set in MMFSR register.
The attached example code shows fault exception handling on S32K144 MCU.
To demonstrate fault handling, the following faults are forced:
- attempt to access an unimplemented memory area
- attempt to write to a non-gated peripheral register
- write to read only register
- fetching an instruction from a protected peripheral memory region
- division be zero
- unaligned memory access
- execution of a non-thumb instruction
- execution of an undefined instruction
The debugging of a fault exception starts with a reading CSFR register to determine which exception has occurred and possibly a memory address that has caused the exception. In case a bit in CSFR register is set the program stops at a software breakpoint.
At the time when the program branches to an exception handler, certain core registers are pushed onto the stack including the program counter that points to the fault program address that has caused the exception. The stack frame can be dump using a software function that takes a pointer to R0 core register as an argument. But first a content of the stack pointer must be moved to the R0 register. In case an application uses Process Stack Pointer (PSP) as well, it is necessary to find out whether the stack pointer comes from Main Stack Pointer (MSP) or PSP. The information about the return stack is available in EXC_RETURN value loaded into LR core register.
Note: The compiler only supports basic __asm statements in __attribute__((naked)) functions. Therefore, fault handlers are declared with this attribute.
After the stack frame is dumped, PC (offset R0 + 6) holds an address of the fault instruction. This applies for all exception except for Imprecise bus faults as it was explained above. In such a case, the Write buffer can be disabled which forces the exception to be precise.
Having a precise PC address, the fault instruction can be find in a disassembly window in an IDE, below S32 Design Studio.