3
votes

I've also posted this on ARM Developer forum, but experience says I'll get a better response here :)

I'm struggling to track down a problem here. It appears to be that the stmdb instruction isn't pushing all of the requested registers, so that when the corresponding ldmia.w instruction executes, it pops a PC value that sends the processor executing code from RAM.

I'm using GNU MCU Eclipse tools for development, with SEGGER debugging. My target is a SAM4L from Microchip. Also running FreeRTOS.

arm-none-eabi-gcc (GNU MCU Eclipse ARM Embedded GCC, 64-bit) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]

I can single-step over the offending instruction, and I can't see that it is doing what the ARM documentation says it should.

The offending instruction is executed on entry to a function. The previously executed instruction is (unsurprisingly) the call to the function bl 0x3b38.

0003b38: stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}

Before executing this instruction SP has the value 0x20001b88. After using the debugger to single-step over this instruction SP has the value 0x20001b74. My expectation would be that after pushing 10 registers, SP would have the value 0x200001b60.

Examining the stack memory shows that registers R12, R11, R10, and R9 were pushed to locations 0x20001b80 through 0x20001b74 respectively. Location 0x20001B84 gets the value that is in the PC after the instruction executed. Again this is contrary to my expectation which is that the registers listed in the instruction would be the ones that were pushed to the stack (especially not R12), and that LR would be pushed rather than PC.

I've examined the memory location where the Assembly instruction is stored, and it appears to be coded correctly, with R3-R11 and LR (=R14) coded. The destination register is 0b1101 = Stack Pointer.

00003b38: 2DE9F84F  
     E9    |   2D   |   4F   |   F8   
=> 11101001 00101101 01001111 11111000

At the end of the called function, the "pop" instruction appears to work as advertised, loading a RAM address into the PC. Things don't go well from here on.
00003bca: ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, pc}

I've attached Debugger Screenshots from Before and after the step through the offending instruction illustrating my comments above. Debugger Before Executing STMDB instruction Debugger After  Executing STMDB instruction

This one is killing me. Any clues would be greatly appreciated.

Thanks,

2
This is indeed very strange. Could it be that the code on your microcontroller is not in sync with what the debugger thinks it should be?fuz
Wondering why r3 is saved & restored?Erik Eidt
how did you get the code in ram and how does the debugger see it?old_timer
Thanks @fuz - Looks like you got it. The Eclipse MCU debugger is somehow confused about the content of that memory location. When I used J-Link to view the memory location, the instruction was 0xE92DBE00 - Which (unsurprisingly) matches the behavior I saw. The debugger in Eclipse MCU still shows 0xE92D4FF8.harry courtice
@harrycourtice How strange.fuz

2 Answers

2
votes

I coded a test and ran it on an m4, I do not have the same chip you have.

.thumb_func
.globl TEST
TEST:
    str r3,[r1],#4
    str r4,[r1],#4
    str r5,[r1],#4
    str r6,[r1],#4
    str r7,[r1],#4
    str r8,[r1],#4
    str r9,[r1],#4
    str r10,[r1],#4
    str r11,[r1],#4
    str lr,[r1],#4
    mov r1,sp
    mov sp,r2
    stmdb sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
    mov r0,sp
    mov sp,r1
    bx lr

testing in flash

ra=TEST(0,0x20000100,0x20000200);

sp before is 0x20000200, sp after is 0x200001D8 0x1D8 + (4 * 10) = 0x200

first column address, second is value at that address, third is value in other space where each register was saved using an str.

200001D8 00000003 00000003 
200001DC 00000040 00000040 
200001E0 20000100 20000100 
200001E4 20000128 20000128 
200001E8 00000007 00000007 
200001EC 00000008 00000008 
200001F0 00000009 00000009 
200001F4 0000000A 0000000A 
200001F8 0000000B 0000000B 
200001FC 0800023B 0800023B 

instructions copied to ram and then run

ra=HOP(0x20000005,0x20000100,0x20000200);

200001D8 00000003 00000003 
200001DC 20000078 20000078 
200001E0 20000100 20000100 
200001E4 20000128 20000128 
200001E8 00000007 00000007 
200001EC 00000008 00000008 
200001F0 00000009 00000009 
200001F4 0000000A 0000000A 
200001F8 0000000B 0000000B 
200001FC 080002A1 080002A1 

run again with different addresses for r1 and sp.

ra=HOP(0x20000005,0x20000200,0x20000300);

200002D8 00000003 00000003 
200002DC 20000200 20000200 
200002E0 20000200 20000200 
200002E4 20000228 20000228 
200002E8 00000007 00000007 
200002EC 00000008 00000008 
200002F0 00000009 00000009 
200002F4 0000000A 0000000A 
200002F8 0000000B 0000000B 
200002FC 080002D9 080002D9 

In both cases I get a full push and it seems all the registers were pushed.

I would start by not using a debugger and see what you see (I have no use for them they cause as many problems as they solve). What was the problem that lead you to dig into this instruction? If you dump ram in that area do you see the instructions expected? Do you have an icache enabled? (I think the m4 has a cache yes?) does the sam4l have an Atmel (microchip) prefetch cache that might have caught stale code? Did you re-use this ram space for other code then loaded this code over that and then ran this code?


EDIT

.inst.w 0xE92DBE00
.inst.w 0xE92D4FF8

   8:   e92d be00   stmdb   sp!, {r9, r10, r11, r12, sp, pc}
   c:   e92d 4ff8   stmdb   sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}

that will definitely break it. But how did that instruction get there? Is this compiled code? The key it sounds is to not use the debugger but examine what the compiler is producing and look at the disassembly (from the binary not from the memory on the part...). See how that compares to the memory on the part if needed, etc...

1
votes

So, It was a problem with the debugger. When I used the Eclipse MCU debugger to view the instruction it showed 0xE92D4FF8. When I used another debugger (J-Link) to view the instruction, it showed 0xE92DBE00.

The Eclipse MCU debugger is able to write breakpoints to Flash when it runs out if hard breakpoints on the controller. 0xBE00 corresponds to the BKPT instruction, so it looks like a breakpoint instruction was written over the top of the low 16 bits of the STMDB instruction.

It appears that the Eclipse MCU debugger somehow lost track of that breakpoint, and left the modified instruction in Flash. A full Erase (from J-Link) and reprogram, and everything is now working the way it should.