LPC11U68 HardFault on pc-relative ldr

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

LPC11U68 HardFault on pc-relative ldr

1,038 Views
dfenger
Contributor I

I'm using MCUXpresso IDE to develop for the LPC11U68, currently using a OM13058 LPCXpresso Board, Rev C2 as the development target.

I keep running into HardFaults when the compiler generates a "ldr r3, [pc, #20]" instruction at an address that's not a power of 4.  It seems to assume that the CPU will round down to the nearest power of 4, but that's not what I'm seeing in the debugger.

A representative dissasembly from USB_IRQHandler (taken directly from the usbd_rom_hid_generic project in LPCOpen for the LPC116x):

71 if ( LPC_USB->DEVCMDSTAT & USB_SETUP_RCVD ) { /* if setup packet is received */
00000452: ldr r3, [r3, #0]
00000454: lsls r3, r3, #23
00000456: bpl.n 0x460 <USB_IRQHandler+20>
72 addr[2] &= ~BUF_ACTIVE; /* clear EP0_IN ACTIVE bit*/
00000458: ldr r3, [r2, #8]
0000045a: lsls r3, r3, #1
0000045c: lsrs r3, r3, #1
0000045e: str r3, [r2, #8]

74 USBD_API->hw->ISR(g_hUsb);
00000460: ldr r3, [pc, #16] ; (0x474 <USB_IRQHandler+40>)
00000462: ldr r2, [pc, #20] ; (0x478 <USB_IRQHandler+44>)
00000464: ldr r3, [r3, #0]
00000466: ldr r0, [r2, #0]
00000468: ldr r3, [r3, #0]

0x474 contains 0x10000320.  0x478 contains 0x1000130.  So it all looks okay...

But the instruction at 462 loads garbage into r2 (0x68db681b), which causes the instruction in 0x466 to HardFault.

Hunting around for the 'random' data, I see it's living at 0x468.

I've tried compiling the code with -mno-unaligned-access, but it doesn't seem to help.

Obviously, I'm doing something terribly wrong, because if this issue was common nobody would be able to run anything on the LPC11U68 at all.  Worse, some of the time, I can get some builds to run, but I have no idea why.

Other cases I've seen make seems to be related to a branch instruction or other jump happening shortly before the ldr.  I've included some context to show where that would be the case, here.

Labels (1)
0 Kudos
3 Replies

779 Views
dfenger
Contributor I

I found one fix by reducing the clock rate.  That led to a suggestion to increase the FLASHTIM value, as for anything over 40MHz the LPC11U68 needs 3 clocks instead of 2.

So anyone else who runs into this, and is running at the default of 48MHz:

Chip_FMC_SetFLASHAccess(FLASHTIM_3CLK_CPU);

Fixed the problem for me, hopefully this will help someone else.

0 Kudos

779 Views
dfenger
Contributor I

As a workaround, it seems that adding

asm volatile("dsb" : : : "memory");

before the vulnerable instruction fixes the problem.  Possibly overkill, maybe just a nop is required... but adding a data synchronization barrier gets the PC back into a consistent state.

Not my first choice of a fix, I'd prefer to have the compiler do this.  But at least it lets me move forward until I figure out how to make the compiler do it for me...

EDIT: Unfortunately, the above doesn't work.  It only worked because I'd set a breakpoint on the line to see if it had changed the disassembly notably. So shortly after a breakpoint, it works.  But I couldn't get any combo of synchronization barriers (or a bunch in a row) to clear up the issue.

0 Kudos

779 Views
dfenger
Contributor I

I did have a little more luck with asm volatile("cmp r3,r3;" : : : "memory");

The problem is, it just pushes the issue into harder and harder to find places.  Ones where the spurious load doesn't HardFault immediately, but causes some kind of knock-on effect that end up crashing later.  (And it worries me that by being only 2 byes long, it might be 'fixing' the problem by pushing the offending load back to a divisible-by-4 address.)

0 Kudos