本帖最后由 cruelfox 于 2020-4-19 08:46 编辑
虽然我已可以使用板载的ST-Link通过SWD口对 STM32MP157 进行调试,但却在实验中遇到意外情况,总之OpenOCD中调试操作并不像对STM32系列MCU那样顺利。我对 OpenOCD 背后的调试原理不熟悉,又是头一次对付 Cortex-A7 核,所以在这里遇到些障碍。
比如,用 halt 命令停止 CPU 的执行时,可能明显出现等待,然后OpenOCD报个错
> halt
stm32mp15x.cpu0 rev 5, partnum c07, arch f, variant 0, implementor 41
stm32mp15x.cpu0 cluster 0 core 0 multi core
Timeout waiting for halt
比如遇到 step 命令不能单步执行(PC寄存器无变化)的状态。
更要命的是,在以前对 Cortex-M 进行复位初始化我用的 reset init 命令不能正常用了。
> reset init
ap 0 selected, csw 0x10006000
mem2array: Read @ 0x50001000, w=4, cnt=1, failed
mem2array: Read @ 0x50000208, w=4, cnt=1, failed
SRST line asserted
SRST line released
stlink_connect(connect)
SWD DPIDR 0x6ba02477
timed out while waiting for target halted
stm32mp15x.cpu0 rev 5, partnum c07, arch f, variant 0, implementor 41
target halted in Thumb state due to debug-request, current mode: Supervisor
cpsr: 0x800001f3 pc: 0x00010b6a
MMU: enabled, D-Cache: disabled, I-Cache: enabled
ap 0 selected, csw 0x10006000
pc (/32): 0x00010B6A
stm32mp15x.cpu1: ran after reset and before halt ...
Timeout waiting for halt
embedded:startup.tcl:24: Error:
in procedure 'ocd_process_reset'
in procedure 'ocd_process_reset_inner' called at file "embedded:startup.tcl", line 261
in procedure 'arp_reset_default_handler'
in procedure 'arp_reset_plan_srst_dbg_gated' called at file "embedded:startup.tcl", line 387
in procedure 'stm32mp15x.cpu1' called at file "embedded:startup.tcl", line 339
in procedure 'ocd_bouncer'
at file "embedded:startup.tcl", line 24
Deferring arp_examine of stm32mp15x.cpu2
Use arp_examine command to examine it manually!
Deferring arp_examine of stm32mp15x.ap2
Use arp_examine command to examine it manually!
timed out while waiting for target halted
TARGET: stm32mp15x.cpu1 - Not halted
假如 reset init 命令能让CPU复位成执行第一条指令之前的状态,那就能定位到程序入口开始跟踪执行过程。对付 Cortex-m 系列我经常这么干的。现在这一招失效,于是一连上调试器并不知道执行的是什么程序。如果是 Linux 运行状态下可能是某个程序或者内核中,如果是 u-boot 期间那多半是 u-boot 的代码。如果TF卡不插入上电的话,那应该就是 ROM bootloader 中的程序,这些是可以推断的。
但是 halt 超时等现象就不知道是什么梗了。
根据手册中的内存映射表,可以知道4G空间的最低地址 0x0 是 ROM bootloader 所在的地方,然后 0x10000000 开始有几块 SRAM, 而对 A7 核最重要的 SYSRAM 在 0x2FFC0000 处。0x40000000 开始有硬件设备的寄存器,和 STM32 MCU 相似。
然而,在ROM程序阶段介入调试,用 OpenOCD 内存查看命令竟然出错:
> mdb 0 16
data abort at 0x00000000, dfsr = 0x00000008
> mdb 0x10000000 16
data abort at 0x10000000, dfsr = 0x00000005
> mdb 0x2ffc0000 16
data abort at 0x2ffc0000, dfsr = 0x00000008
> mdb 0x30000000 16
0x30000000: 08 00 00 30 68 02 00 30 00 00 00 00 00 00 00 00
> mdw 0x40000000 16
data abort at 0x40000000, dfsr = 0x00000007
试了几处地址,除了 RAM alias 区域开头可以读访问之外,其它几个地方竟然不能读?这就费解了,程序在 ROM 中运行,又如何不能读ROM? 查看 PC 寄存器,执行的地址是 ROM 区没错:
> reg pc
pc (/32): 0x0000A396
那么就看这个地址,是可以读取的
> mdb 0xA396 16
0x0000a396: f8 49 c0 1b 88 42 07 d9 07 f0 c9 fb 26 ea 05 05
> arm disassemble 0xA396 4 thumb
0x0000a396 0x49f8 LDR r1, [pc, #0x3e0] ; 0x0000a778
0x0000a398 0x1bc0 SUBS r0, r0, r7
0x0000a39a 0x4288 CMP r0, r1
0x0000a39c 0xd907 BLS 0x0000a3ae
看来 ROM 的部分地址被屏蔽了,这可能是启用了保护机制,可能是 MMU (内存管理单元) 控制了的原因。由于不能用 reset init 停在复位之后的状态调试,现在经过 ROM 程序的初始化之后,现场是什么样我不知晓。
从 SP 寄存器看来,的确 SYSRAM 还能部分访问的。
> reg sp
sp (/32): 0x2FFC1BE0
> mdw 0x2ffc1000 4
0x2ffc1000: 260148df 0640bac1 077ff997 4e5e4a05
再找个设备的寄存器呢,看看 GPIOA (从手册可以查到地址范围 0x50002000 ~ 0x500023FF)
> mdw 0x50002000 4
0x50002000: f7ffffbf 00002000 0c0000c0 00000000
说明 GPIOA 可以访问。那么我可以动动手脚,先写段 LED 点灯的程序跑下试试。
在官方的 STM32Cube_FW_MP1_V1.2.0 这个软件包里,我像往常对待 STM32 MCU 那样找出了 stm32mp157axx_ca7.h 这个寄存器定义文件,一看内容和 STM32 MCU 很像嘛!
这样就可以通过寄存器操作开始玩它了。先点灯,只需要操作 GPIO. (因为 ROM 程序已经把一些初始化工作做了)
根据电路图,有两个用户 LED 就在 PA13, PA14 上,同时还连着按钮。
那么点灯程序这样写:
#include "stm32mp157axx_ca7.h"
void _start(void)
{
int i;
GPIOA->MODER = 1<<13*2|1<<14*2|~(3<<13*2|3<<14*2);
for(;;)
{
if(GPIOA->ODR & 1<<14)
GPIOA->BRR = 1<<14;
else
GPIOA->BSRR = 1<<14;
for(i=0;i<10000;i++)
{
int n;
if(GPIOA->ODR & 1<<13)
GPIOA->BRR = 1<<13;
else
GPIOA->BSRR = 1<<13;
for(n=0;n<100;n++)
{
__NOP();
///// 总共100条 NOP 指令,这里为了页面空间省略了
__NOP();
}
}
}
}
因为这里我只是写了一个程序片段,没有初始化代码(不需要,没有全局变量),不用到标准库,都不用写 main()
然后先试试编译,用以前开发 STM32 那个 GCC 行不行呢?
E:\stm32mp157\a7>arm-none-eabi-gcc -mcpu=cortex-a7 -Os test.c -c -std=gnu99
这样可以编译的(加 -std=gnu99 是因为使用的头文件需要)。那就看下编译后的代码:
E:\stm32mp157\a7>arm-none-eabi-objdump -d test.o
test.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e59f31e0 ldr r3, [pc, #480] ; 1e8 <_start+0x1e8>
4: e3e0230a mvn r2, #671088640 ; 0x28000000
8: e5832000 str r2, [r3]
c: e59f31d4 ldr r3, [pc, #468] ; 1e8 <_start+0x1e8>
10: e5932014 ldr r2, [r3, #20]
14: e3120901 tst r2, #16384 ; 0x4000
18: e3a02901 mov r2, #16384 ; 0x4000
1c: 15832028 strne r2, [r3, #40] ; 0x28
20: 05832018 streq r2, [r3, #24]
24: e3023710 movw r3, #10000 ; 0x2710
28: e59f21b8 ldr r2, [pc, #440] ; 1e8 <_start+0x1e8>
2c: e5921014 ldr r1, [r2, #20]
30: e3110a02 tst r1, #8192 ; 0x2000
34: e3a01a02 mov r1, #8192 ; 0x2000
38: 15821028 strne r1, [r2, #40] ; 0x28
3c: 05821018 streq r1, [r2, #24]
40: e3a02064 mov r2, #100 ; 0x64
看起来正常,那么把它弄到 STM32MP157 的 SRAM 里面运行一下。因为这段代码没有使用除了GPIOA之外的绝对内存地址,放到任何地址运行都是可以的。将编译结果二进制文件提取出来:
E:\stm32mp157\a7>arm-none-eabi-objcopy -Obinary test.o test.bin
然后,用 OpenOCD 的命令将其写到内存地址 0x2FFC4000 (先确认过这块可访问)
> load_image e:/stm32mp157/a7/test.bin 0x2FFC4000
492 bytes written at address 0x2ffc4000
downloaded 492 bytes in 0.062500s (1.500 KiB/s)
程序写到 SRAM 里面了,然后就运行它:让 CPU 恢复执行,从 0x2FFC4000 处开始。
> resume 0x2FFC4000
stm32mp15x.cpu0 rev 5, partnum c07, arch f, variant 0, implementor 41
Timeout waiting for halt
Polling target stm32mp15x.cpu0 failed, trying to reexamine
stm32mp15x.cpu0: hardware has 6 breakpoints, 4 watchpoints
出了问题,而且 PC 寄存器就停留在 0x2FFC4000
> reg pc
pc (/32): 0x2FFC4000
我查看了下 CPSR, 发现 T 位是置1的,那么就给取消一下试试。因为我编译的代码是 ARM 模式的。
> reg cpsr
cpsr (/32): 0x800001F3
> reg cpsr 0x1d3
cpsr (/32): 0x000001D3
> resume
然后,LED开始闪烁,运行了!
看LED LD6(红色)上面的波形
因为我在代码中每次修改这个引脚 (PA13) 状态,间隔了10000条NOP指令,可以估算下每条 NOP 指令执行时间:25.1us/10000 = 2.51ns, 若以每条 NOP 指令一个时钟周期的速度执行的话,大约是 400MHz 的时钟。这只是揣测,不一定对。
此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处