stmdb 指令似乎无法正常工作 - Cortex M4 / SAM4L

stmdb instruction appears not to work correctly - Cortex M4 / SAM4L

我也在 ARM 开发者论坛上发布了这个,但经验表明我会在这里得到更好的回应:)

我正在努力寻找这里的问题。似乎 stmdb 指令没有压入所有请求的寄存器,因此当相应的 ldmia.w 指令执行时,它弹出一个 PC 值,该值从 RAM 发送处理器执行代码。

我正在使用 GNU MCU Eclipse 工具进行开发,并使用 SEGGER 进行调试。我的目标是 Microchip 的 SAM4L。还有 运行 FreeRTOS。

arm-none-eabi-gcc(GNU MCU Eclipse ARM 嵌入式 GCC,64 位)7.3.1 20180622(发布)[ARM/embedded-7-branch 修订版 261907]

我可以单步执行有问题的指令,但我看不到它正在执行 ARM 文档所说的它应该执行的操作。

违规指令在函数入口处执行。先前执行的指令是(毫不奇怪)对函数 bl 0x3b38.

的调用

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

在执行该指令之前,SP 的值为 0x20001b88。使用调试器单步执行此指令后,SP 的值为 0x20001b74。我的期望是在压入 10 个寄存器后,SP 的值为 0x200001b60。

检查堆栈内存显示寄存器 R12、R11、R10 和 R9 分别被推送到位置 0x20001b80 到 0x20001b74。位置 0x20001B84 获取指令执行后PC 中的值。这再次与我的预期相反,指令中列出的寄存器将被压入堆栈(尤其是 not R12),并且 LR 将被压入而不是PC.

我检查了存储汇编指令的内存位置,它似乎编码正确,编码为 R3-R11 和 LR (=R14)。目标寄存器是 0b1101 = 堆栈指针。

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

在被调用函数的末尾,“pop”指令似乎如所宣传的那样工作,将 RAM 地址加载到 PC 中。从这里开始事情就不顺利了。
00003bca: ldmia.w sp!, {r3, r4, r5, r6, r7, r8, r9, r10, r11, pc}

我附上了在说明我上面的评论的违规指令之前和之后的调试器屏幕截图。

这个简直要了我的命。任何线索将不胜感激。

谢谢,

我编写了一个测试代码,运行 它在 m4 上,我没有你的芯片。

.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

在 Flash 中测试

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

之前的sp是0x20000200,之后的sp是0x200001D8 0x1D8 + (4 * 10) = 0x200

第一列地址,第二个是该地址的值,第三个是其他 space 中的值,其中每个寄存器都使用 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 

指令复制到 ram 然后 运行

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 

运行 再次为 r1 和 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 

在这两种情况下,我都得到了完全推送,似乎所有寄存器都被推送了。

我将从不使用调试器开始,看看你看到了什么(我对它们没有用,它们引起的问题与它们解决的问题一样多)。导致您深入研究该指令的问题是什么?如果你在那个区域倾倒内存,你会看到预期的说明吗?你有启用icache吗? (我认为 m4 有一个缓存是吗?) sam4l 是否有一个可能捕获陈旧代码的 Atmel(微芯片)预取缓存?您是否将此 ram space 重新用于其他代码,然后在其上加载此代码,然后 运行 此代码?


编辑

.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}

那肯定会打破它。但是该指令是如何到达那里的呢?这是编译后的代码吗?听起来关键是不要使用调试器,而是检查编译器生成的内容并查看反汇编(从二进制文件而不是部件的内存......)。如果需要,看看它与零件上的内存相比如何......

所以,这是调试器的问题。当我使用 Eclipse MCU 调试器查看指令时,它显示 0xE92D4FF8。当我使用另一个调试器(J-Link)查看指令时,它显示 0xE92DBE00.

Eclipse MCU 调试器能够在控制器上的硬断点用完时将断点写入 Flash。 0xBE00对应BKPT指令,所以看起来像是在STMDB指令的低16位上面写了一条断点指令。

Eclipse MCU 调试器似乎以某种方式忘记了该断点,并将修改后的指令留在了闪存中。完全擦除(来自 J-Link)并重新编程,现在一切正常。