Debugging a Hard Fault in MQX

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

Debugging a Hard Fault in MQX

Debugging a Hard Fault in MQX

Several reasons may lead to a hard fault, for example, calling a function pointer with an invalid address, stack overflow, trying to read or write to an on-chip peripheral that is powered down or not being clocked, accessing non-mapped memory etc. It is often difficult to find out what caused the hard fault, fortunately the cortex-M stores some information in ARM fault status register, the hardware automatically pushes several CPU register on the stack before entering the Hard Fault Handler. This document below shows a way to debug a Hard Fault in MQX.

For Hard Fault, we can hook our exception handler into the vector table. we will find vector table in the vector.c source file in the BSP library. By default, isr is configured to DEFAULT_VECTOR, which is the _int_kernel_isr() function of the MQX dispatcher. We cam put our own function, like void my_kernel_isr (void), to vector table offset 0x0c.

 Inside my_kernel_isr(), we can get the interrupt stack frame, CPU register content at the time of exception and use directly the my_kernel_isr - just remove the static keyword, as we need the my_kernel_isr to be global, as it is referenced from vectors.c and it will be implemented in the application, main.c, for example.

 Now, put breakpoint to this my_kernel_isr and see the registers read by prvGetRegistersFromStack(). Typically at most interest would by "lr" and "pc". For example, "lr" would have an address of an instruction that a return from a function call would return to.

By looking into the .map file or using the debugger, we will know, which function has been executed by the CPU when exception occurred.

Hook our own kernel isr into the vector table

In file vectors.c,   the default ISR for Hard_Fault is DEFAULT_VECTOR

pastedImage_3.png

Now we modified it to my_kernel_isr, so when hard fault happens, my_kernel_isr will be executed.

pastedImage_4.png

Read the register value

The following code is for CodeWarrior/Kinetis Design Studio to a C module, for IAR/KEIL, we need to write the same function as an assembler source file.

void my_kernel_isr( void ) __attribute__( ( naked ) );

/* The fault handler implementation calls a function called

prvGetRegistersFromStack(). */

static void my_kernel_isr(void)

{

    __asm volatile

    (

        " tst lr, #4                                                \n"

        " ite eq                                                    \n"

        " mrseq r0, msp                                             \n"

        " mrsne r0, psp                                             \n"

        " ldr r1, [r0, #24]                                         \n"

        " ldr r2, handler2_address_const                            \n"

        " bx r2                                                     \n"

        " handler2_address_const: .word prvGetRegistersFromStack    \n"

    );

}

void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress )

{

/* These are volatile to try and prevent the compiler/linker optimising them

away as the variables never actually get used.  If the debugger won't show the

values of the variables, make them global my moving their declaration outside

of this function. */

volatile uint32_t r0;

volatile uint32_t r1;

volatile uint32_t r2;

volatile uint32_t r3;

volatile uint32_t r12;

volatile uint32_t lr; /* Link register. */

volatile uint32_t pc; /* Program counter. */

volatile uint32_t psr;/* Program status register. */

    r0 = pulFaultStackAddress[ 0 ];

    r1 = pulFaultStackAddress[ 1 ];

    r2 = pulFaultStackAddress[ 2 ];

    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];

    lr = pulFaultStackAddress[ 5 ];

    pc = pulFaultStackAddress[ 6 ];

    psr = pulFaultStackAddress[ 7 ];

    /* When the following line is hit, the variables contain the register values. */

    for( ;; );

}

Notes about Cortex M4 registers:

MQX sets active stack pointer to PSP in boot.s

The core automatically switches the active stack pointer to MSP in handler mode and switches back to PSP with exception return

pastedImage_6.png

Compare with .map file

we need to find out which function executes while hard fault occurs.

To determine application , Use trace debugging tool (DSTREAM, ULINK-PRO, J-Trace, Lauterbach uTrace for Cortex-M …) or enable Kernel log and explore its content with MQX TAD

#define MQX_KERNEL_LOGGING 1

 

_klog_create(2048,LOG_OVERWRITE);

  /* Enable kernel log */

  _klog_control(KLOG_ENABLED | KLOG_CONTEXT_ENABLED |

                KLOG_INTERRUPTS_ENABLED| KLOG_FUNCTIONS_ENABLED | KLOG_INTERRUPT_FUNCTIONS |

                KLOG_MEMORY_FUNCTIONS | KLOG_PARTITION_FUNCTIONS, TRUE);

By this we only find, which function causes the problem. Next, we will have to think why this occurs, and give more application specific debugging effort to find the root cause.

Comments

danielchen@fsl

The article mentions that the source code is for CodeWarrior/Kinetis Design Studio to a C module and I don't know if that's it but if you put static void my_kernel_isr(void)  in main.c for example (using MCUXpresso here), it should not be "static" or else you will get:

bsp.a(vectors.o):(.vectors_rom+0xc): undefined reference to `my_kernel_isr'

Other than that, thank you very much for the article!! It is really helpful.

I have an additional question, what should I do or configure in my debug session to hit the for( ;; );? Because execution stops in the __asm volatile instead of the breakpoint I placed in the for, also, I lose the debugger, I mean, I can't single-step anymore, it just freezes and I can't even restart. I have to finish the debug session and start a new one, seems like it crashed.

I'm using a PEmicro Multilink Universal in MCUXpresso, cortex-m4 processor and MQX 4.1.1

Thanks!

No ratings
Version history
Last update:
‎08-30-2017 10:37 PM
Updated by: