Fault handling on S32K14x

cancel
Showing results for 
Search instead for 
Did you mean: 

Fault handling on S32K14x

No ratings

Fault handling on S32K14x

The S32K14x MCU ARM Cortex M4F core processor handles fault exceptions using four handlers.

Handlers

UsageFault_Handler()

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[0] 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).

BusFault_Handler()

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 executing subsequent instructions 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_DISDEFWBUF = 1). 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 bus faults.

MemManage_Handler()

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

S32K1xx series 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.

HardFault_Handler()

This handler is the only one that has a fixed priority (-1) and is always enabled.

If other handlers are disabled (in the SHCSR register), all faults are escalated to this handler.

The escalation take place also when a fault occurs during another fault handling execution or while the vector table is 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, S32K14x MCUs support 16 priority levels only.

Therefore, priorities are configurable in the four most significant bits of PRI_4, PRI_5 and PRI_6 only, which is similar to other NVIC IPR registers as shown below.

NVIC IPR.png

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 bit fields for Usage Fault (UFSR), Bus Fault (BFSR), and Memory Management Fault (MMFSR) where each bit represents a fault exception.

CFSR_arm.png

pastedImage_3.png

There are also two auxiliary address registers. If BFARVALID is set in the BFSR register, Bus Fault Address Register (BFAR) holds the memory access location of a precise bus fault. Similarly, if MMARVALID bit is set in MMFSR register, Memory Manage Address Register (MMAR) holds the address of a MemManage fault.

Example code

To demonstrate the debugging process, the following exceptions 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

When the program enters an exception handler, the stack frame is pushed onto the stack including the program counter value of the fault instruction.
In this example, the exception handlers are declared with __attribute__((nake_)) (fault_exceptions.h), no prologue is generated and the program counter is always offset by 6 words (0x14) from the stack pointer that can be read in the handlers using either the debugger (memory view) or a SW pointer. If 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, this information is available in the EXC_RETURN value in the link register. Having a precise program counter address, we can find the fault instruction in Disassembly.

pastedImage_1.png

pastedImage_2.png

This applies to all exception except for imprecise bus faults as explained above, imprecise bus faults can be forced to be precise by disabling the Write buffer.

The CSFR register is read to determine which exception has occurred and, if available, the memory access location that has caused the exception. 

References

Cortex-M4 Devices Generic User Guide
Cortex-M4 Technical Reference Manual

Attachments
Comments

@ Daniel Martynek

Thanks,Daniel  

but i have a question for you, if I add some local variables in  fault handler(such as temp in code below), the way to read stacked PC from stack may be wrong?

because the local variable temp will be stacked in the stacked when call the function getStackFrame. am I right?

void UsageFault_Handler(void) {
uint8_t temp=0;
UFSR = (S32_SCB->CFSR & 0xFFFF0000);

if(UFSR & S32_SCB_CFSR_DIVBYZERO_MASK) asm("BKPT");
if(UFSR & S32_SCB_CFSR_UNALIGNED_MASK) asm("BKPT");
if(UFSR & S32_SCB_CFSR_NOCP_MASK) asm("BKPT");
if(UFSR & S32_SCB_CFSR_INVPC_MASK) asm("BKPT");
if(UFSR & S32_SCB_CFSR_INVSTATE_MASK) asm("BKPT");
if(UFSR & S32_SCB_CFSR_UNDEFINSTR_MASK) asm("BKPT");

asm("MOVS R0, #4");
asm("MOV R1, LR");
asm("TST R0, R1");
asm("BEQ ___MSP");
asm("MRS R0, PSP");
asm("B getStackFrame");
asm("___MSP:");
asm("MRS R0, MSP");
asm("B getStackFrame");
}

Hi Andy,

The exception handler functions are declared (fault_exception.h) with __attribute__((naked)).

The compiler does not generate prologue and epilogue sequences for functions declared with this attribute and no local variables are pushed on the stack.

This ensures that the program counter value is offset by 6 words from the stack pointer.

If the handlers were declared without the attribute, the prologue (depending on the local variables) would increase the stack pointer and offset the program counter value further.

I slightly simplified the code, the program counter is simply read by a pointer instead of using another function and R0 argument as a poiner to the stack.

BR, Daniel

Version history
Revision #:
1 of 1
Last update:
‎07-01-2017 08:08 AM
Updated by: