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 to
- 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 detection of the division by zero fault is disabled by default which means that such an operation returns zero and the fault is not detected. Similarly, the Cortex-M4 processor supports unaligned access for certain instructions. The detection on both the division by zero and the unaligned access (for every instruction) faults can be enabled in Configuration and Control Register (CCR).
Bus faults occur when a bus slave returns an error response while
- stacking for an exception entry
- unstacking for an exception return
- prefetching an instruction
- during floating-point lazy state preservation
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 performance.
Note: The S32K144 MCU has its own system Memory Protection Unit which is implemented on the bus. Therefore, any system MPU violation triggers precise/imprecise bus faults.
Typically, these exceptions rise on an attempt to access regions that are protected by the core ARM Cortex M4 Memory Protection Unit.
- attempt to load or store at a protected location
- instruction fetch from a protected location
- stacking/unstacking fault caused by violation of the memory protection
- protection violation during floating-point lazy state preservation
S32K144 MCU implements its own system Memory Protection Unit on the bus and therefore an attempt to access a protected region results in a bus fault exception instead. Nevertheless, the system MPU does not protect access to peripheral registers, and 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 a fault exception on a stack overflow.
This handler is the only one that has a fixed priority (-1) and is always enabled. If 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 or during a vector table read.
Priority of exception fault handlers
The fault exception handlers’ priorities, besides the HardFault handler (fixed 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 MCU 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 the exception handling on S32K144 MCU.
To demonstrate the debugging process, the following faults can be 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 on the return stack is available in EXC_RETURN value loaded into LR core register.
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.