This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Debugging a Usage Fault for an unaligned memory access

Hi,

I am experiencing a hard fault in Cortex M3 and bit#30 FORCED is set in the Hard Fault Status Register (0xE000ED2C). Referring to the Cortex M3 Technical Reference Manual:

[color="#0000FF"][30] FORCED Hard Fault activated because a Configurable Fault was received and cannot activate because of priority or because the Configurable Fault is disabled. The Hard Fault handler then has to read the other fault status registers to determine cause.[/color]

The value in the Configurable Fault Status Registers (0xE000ED28) is 0x01000000 which means that the bit#8 UNALIGNED is set in the Usage Fault Status Register (0xE000ED2A) . Again referring to the Cortex M3 Technical Reference Manual:

[color="#0000FF"][8] UNALIGNED When UNALIGN_TRP is enabled (see Configuration Control Register on page 8-25), and there is an attempt to make an unaligned memory access, then this fault occurs. Unaligned LDM/STM/LDRD/STRD instructions always fault irrespective of the setting of UNALIGN_TRP.[/color]

The value in the Configuration Control Register (0xE000ED14) is 0x00000200 which means that only bit#9 STKALIGN is set.

After reading through the relevant topics in this forum, I added the following code snippet to narrow down the problem.

[font="Courier New"]void hard_fault_handler_c(unsigned int * hardfault_args)
{
unsigned int stacked_r0;
unsigned int stacked_r1;
unsigned int stacked_r2;
unsigned int stacked_r3;
unsigned int stacked_r12;
unsigned int stacked_lr;
unsigned int stacked_pc;
unsigned int stacked_psr;

stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);

stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);

printf ("[Hard fault handler]\n");
printf ("R0 = %x\n", stacked_r0);
printf ("R1 = %x\n", stacked_r1);
printf ("R2 = %x\n", stacked_r2);
printf ("R3 = %x\n", stacked_r3);
printf ("R12 = %x\n", stacked_r12);
printf ("LR = %x\n", stacked_lr);
printf ("PC = %x\n", stacked_pc);
printf ("PSR = %x\n", stacked_psr);
printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38))));
printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28))));
printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C))));
printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30))));
printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));

while(1);
}

__asm void Hard_Fault_Handler(void)
{
IMPORT hard_fault_handler_c
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B hard_fault_handler_c
}[/font]

However, I am still not able to figure out and point out the instruction which is causing the issue .

Any help will be very much appreciated. I can provide more information if needed.

Thank you.

Regards,
  • Note: This was originally posted on 10th May 2010 at http://forums.arm.com

    You need to provide more information:

    Did you get any output from the hardfault handler? If not, did you enable retargetting so that you can see the printf messages?  Or if the value of SP is wrong when it enter the hardfault handler, the message disaply won't work. In such case you need to find why the SP value goes wrong.

    Which compiler/debugger you are using?
    Note : some debuggers might halt the core automatically when it enter hard fault handler.
  • Note: This was originally posted on 10th May 2010 at http://forums.arm.com

    Thanks for your prompt reply.

    I am using Lauterbach/TRACE32. Yes, I observed that the debugger automatically stops execution as soon as entering the asm hard default handler. Later I also added a breakpoint at the very first instruction of the asm hard fault handler. As I am working on the target board without any printf support, I had to step through after the breakpoint in order to view the values of the registers and the local variables of the hard_fault_handler_c() function. I am not sure whether the value of the SP register is correct, how to verify that?

    Forgot to mention in my first post that if I add a breakpoint at another place in the code and then step through a few times before pressing the Go button, I don't see a hard fault and it works.
  • Note: This was originally posted on 10th May 2010 at http://forums.arm.com

    If the value of SP is not in valid memory range, then printf might not work because it will need stack memory.  But since you are working on a board without any printf support, maybe it would be easier just to try to located the stacked PC manually:

    1) Look at LR value when the core enter hardfault, if bit 2 is 0, then read the value of MSP. Otherwise, read the value of PSP.
    2) Based on the MSP/PSP value, you should be able to locate the start of stack frame, stacked PC is in address SP+24.
    3) Generate a disassembled listing of the program you run, and try locate the stack PC address in the disassembled program list.
  • Note: This was originally posted on 11th May 2010 at http://forums.arm.com

    Thanks again for your reply.

    I followed your guidelines and am attaching registers' values (Registers_view_1.jpeg) with this post. Before I do so, let me refer to section [color="#000080"]5.5.1 Stacking[/color] in the Cortex M3 Technical Reference Manual.

    [color="#000080"]When the processor invokes an exception, it automatically pushes the following eight registers to the SP in the following order:
    "¢ Program Counter (PC)
    "¢ Processor Status Register (xPSR)
    "¢ r0-r3
    "¢ r12
    "¢ Link Register (LR)[/color]

    The above order mismatches with the order shown in Figure 5-1 (r0, r1, r2, r3, r12, LR, PC, xPSR). Can you kindly verify that?

    [color="#000080"]Note
    "¢ If STKALIGN is set in the Configuration Control Register then an extra word can be inserted before the stacking takes place. See Configuration Control Register on page 8-25.[/color]

    In my case, as STKALIGN is set, can you point out the extra word as mentioned above?

    Section 5.5.1 plus your guidelines ~= debug code in my first post. However, I am not clear about your step 2). Can you kindly elaborate?

    According to section 5.5.1, stacked PC is SP+18 = 0x1FF00742 and the instruction at this address is LDMIA  R12!,{R4-R11} which is part of the PendSV Exception Handler. Is this causing the issue?

    Please see the registers' values in the attached file. Many thanks.
  • Note: This was originally posted on 11th May 2010 at http://forums.arm.com

    Hi Brown,

    The order mentioned by the TRM is the sequence of stacking operation being carried out.
    However, the order in terms of timing is separated from the order in term of address.
    For locating the stacked PC value, please use the information based on figure 5.1.

    Yes, it seems the fault in in your PendSV handler.

    From the diagram you attached, assumed the screen is captured before the hardfault handler is executed:
    LR = 0xFFFFFFF1   => Stacked was using MSP
    MSP value is 0x2000F230 => Stacked PC is stored in 0x2000F230 + 0x18 = 0x2000F248
    (Note : 0x18 = 24)

    The stacked PC is 0x1FF00742 (approximately where the fault occurred)

    The stacked xPSR is 0x0100000E (the fault happened inside PendSV exception because stacked IPSR is 0xE).
    Bit 9 of the stacked xPSR is 0, so there is no padding at the stack frame.  So the value of SP before the fault exception is 0x2000F230+0x20 = 0x2000F250.  Otherwise, if bit 9 of the stack xPSR is 1, then the value of the SP before the fault exception is 0x2000F230+0x20+0x4 = 0x2000F254.

    Note : PRIMASK is set.

    If the faulting instruction is LDMIA R12!,{R4-R11}
    and R12 is 0x1FF00D23, yes, this trigger an unaligned fault.
    Multiple load store instructions can only handle aligned data transfers.

    Are you using an OS? If yes which one are you using?
    regards,
    Joseph
  • Note: This was originally posted on 14th May 2010 at http://forums.arm.com

    Hi Joseph,

    My apology for the late response. I was out-of-office for 2 days and couldn't access the Internet.

    Yes, the screen shot was captured before the hard fault handler was executed.

    I am using a custom OS. The strange thing is that the unaligned address in R12 is the address of the memory fault exception handler. This address is somehow populated into R12 when a prior instruction reads an address which is NULL (shouldn't be so). Actually this problem occurs, when after the watchdog reset, system reset ISR is being executed and later when the PSP is set and the application initialization function execution is in-progress (this is my guess), the PendSV exception handler is invoked. Is it due to the interrupts still enabled upon watchdog reset? I do disable interrupts in the beginning of the application initialization function. I understand that system reset ISR cannot be pre-empted by the PendSV handler. However, I am suspecting that the execution of system reset ISR + application initialization function is not atomic (should be so) when this happens and the PendSV exception handler is invoked before the interrupts are disabled inside the application initialization function. The application initialization function initializes a pointer which is read by the so-called prior instruction in the PendSV handler.

    I am thinking of 2 ways as a workaround. 1) Make sure that interrupts are never enabled until the execution of application initialization is complete. 2) Return the PendSV handler gracefully if the pointer is NULL.

    Please share your thoughts.

    Thanks a lot for your help.

    Regards,
    Brown
  • Note: This was originally posted on 14th May 2010 at http://forums.arm.com

    Hi Brown,

    > My apology for the late response. I was out-of-office for 2 days and couldn't access the Internet.

    No problem. I will be busy most time next week myself so it is unlike for me to do much followup on this next week.

    Assumed that the watchdog reset is working properly, then the interrupt enable for in the NVIC should be all cleared.
    Depends on the microcontroller you use, the watchdog reset might also reset the peripherals.
    However, PendSV exception do not have an enable control as interrupts do.  PendSV is triggered by software only.
    So it is unrelated to peripheral not being reset.

    However, from your descriptions I am wonder if you are trying to reset by executing reset handler rather than doing a proper reset.
    Or are you talking about using watchdog to reset the system? it is not clear to me.

    I guess you need to talk to your OS provider to try to figure out what is the best way to solve this problem.
    regards,
    Joseph