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