Fault handling on S32K14x

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Fault handling on S32K14x

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

Hello, Thank you for the example code.

I have some questions please:

1. will this example work also with s32k142 without changing anything on fault_exceptions.c and fault_exceptions.h
2. how can it be tested, because i tested the division by zero, but it doesn't go to the handler, instead it went to 0x0 address.
3. Where i can fine CSFR register, i couldn't find it in any of the "peripheral register" window from s32ds

Thank you

Hi @elb1,

1. Yes, it works with the S32K142 derivative too.

2. Do you call the enable_div_by_zero_trap()? Without it the result of the division would be 0.

3. In S32DS 3.4 and 3.5, use the Peripheral Registers view.

danielmartynek_0-1697545199511.png

danielmartynek_2-1697545437291.png

 

 

Hello Daniel, Thank you for your explanation.
Can you send me the link for S32DS 3.4 or 3.5 download, in nxp website i could found only 2.2
Thank you so much

Hi @elb1,

Please create a new thread if the question is not related to the topic,

 

Thank you,

BR, Daniel

Dear Daniel,

Excellent explanation; thank you. I have used your example code to create handlers for S32K144 for handling corrupted flash memory (say, during a program operation which fails). If I place a breakpoint at the BusFault_Handler, what I see is that when the exception is taken the state of CFSR and BFAR are exactly what I would expect (i.e., CFSR (=0x8200) tells me the address in BFAR is valid, and the contents of BFAR is the location of the corrupted flash). My handler intends to use this address to erase that segment, thus recovering the device.

However, if I step the MCU at that point using the debugger, all context is lost; CFAR = 0, and BFAR is loaded with a value which looks like the address of something at the end of the S32_SCB space (0xE000EDF8).

Can you think of any reason why these registers change value when I step the code into the __asm__ statements which come first in the handler?

As a test, I also implemented your exact example project on this same board - and the behaviour is identical; the exception is taken, but the context is lost when I step the code once inside your handlers, so writes to your HFSR, MMFSR, BFSR volatile variables etc., aren't correct.

This behaviour is observed on either S32DS 3.4 or 3.5. I use PEMicro debugger and GCC6.3.

Thanks for any suggestions anyone may have.

Kind Regards,

Andrew

 

.....UPDATE.......

There are settings in the debugger configuration which allow the device to affect the core register state when an exception is taken:

andrew_smith_0-1698623369930.png

All these check boxes were ticked in my debug configuration. Ensure all these catch check boxes are cleared if you're debugging exception code....then the core registers states are not altered by the debugger. This solved my problem.

Kind Regards,

Andrew

 

 

 

 

 

 

@danielmartynek hello,In S32DS 3.4,s32k144, I cannot find the arm core register term in Peripheral Registers, but there be an Arm System Registers, and I still cannot see anything when i click into it, what should i do next ?

THANKS

No ratings
Version history
Last update:
‎10-12-2021 05:01 AM
Updated by: