STM32MP157A-DK1测评 (7) M4核程序小试
<div class='showpostmsg'> 本帖最后由 cruelfox 于 2020-4-30 00:46 编辑<p> 前面玩的都是 STM32MP157A 的 Cortex-A7 核,因为这对我而言是新东西。除了两个A7核,此芯片还配备了一个 Cortex-m4 核。不过 M4 核的地位要“矮”一些,在对片上设备的访问权限上是 A7 核当领导的。STM32MP157A 内部没有 Flash, Boot ROM 仅是给 A7 核运行的,因此 M4 核运行的程序需要由 A7 核提供给它。这样也有更大的灵活性,可以在 Linux 运行时候动态地为 M4 核加载软件。</p>
<p> STM32MP157 有一个 "Engineering Mode",留给调试使用。在 DK1 开发板上,可以将底下的BOOT开关设成 BOOT0=0, BOOT2=1状态,就进入"Engineering Mode" (wiki上介绍了,但数据表中没有说,只描述为 ”Reserved(Noboot)“)。在这个模式下,ROM Bootloader 会将 M4 核启动,可以用 OpenOCD 调试 M4 核程序。我以前调试时,OpenOCD列出来的 cpu2 (对应M4核)状态总是 "examine deferred" 所以无法访问它。<br />
但是我更改 BOOT 状态重开机后,发现 cpu2 仍然是 "examine deferred". 似乎是调试器本身的问题。后来从网络上找到答案——还需要在 OpenOCD 中使用 set ENG_MODE 1 命令。这样就能切换到调试 M4 核了。</p>
<p> 这样就有点像以前STM32那样调试的感觉了,除了没有Flash, 不能 reset init 停在第一条指令之外。两个A7核是保持运行状态的,切换后halt命令只对cpu2有效。(切换前是对cpu0, cpu1一起有效,还不能单独停止cpu0或cpu1, 这个问题我不明白)</p>
<p> </p>
<p> 为了测试 M4 核,我拿个以前的程序改一下:</p>
<pre>
<code class="language-cpp">int main(void)
{
gpio_config();
xTaskCreate( blink1, "Blink One", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
xTaskCreate( blink2, "Blink Two", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
xTaskCreate( blink3, "Blink Three", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
xTaskCreate( blink4, "Blink Four", STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
vTaskStartScheduler();
}</code></pre>
<p>就是搭了个FreeRTOS的框架,用4个任务分别以不同的时间间隔操作GPIO翻转,使不同的LED亮灭变化。GPIO的寄存器操作和 STM32F4 一样,RCC中使能GPIO的操作与 STM32F4 有所不同,我在A7程序编写时已发现。因为现在不涉及和 A7 核争用资源,所以不考虑其它的。</p>
<p> 除了必须的寄存器定义头文件 stm32mp157axx_cm4.h , 从开发软件包中还可以找到 startup_stm32mp15xx.s 启动文件(包含中断向量表,ResetHandler代码),和 stm32mp15x_m4.ld 链接脚本。这样用 GCC 编译程序需要的文件就够了。注意提供的 LD 脚本中对内存分配的描述:</p>
<pre>
<code>MEMORY
{
m_interrupts (RX): ORIGIN = 0x00000000, LENGTH = 0x00000298
m_text (RX): ORIGIN = 0x10000000, LENGTH = 0x00020000
m_data (RW): ORIGIN = 0x10020000, LENGTH = 0x00020000
m_ipc_shm (RW): ORIGIN = 0x10040000, LENGTH = 0x00008000
}</code></pre>
<p> M4核执行的程序是放在几块 SRAM 中的,和 A7 核的 SYSRAM 彼此不干扰。编译得到 HEX 文件,可以用 OpenOCD 载入运行。操作命令如下:</p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>load_image f:/test.hex</strong></span><br />
<span style="background-color:#ecf0f1;">664 bytes written at address 0x00000000</span><br />
<span style="background-color:#ecf0f1;">3076 bytes written at address 0x10000000</span><br />
<span style="background-color:#ecf0f1;">downloaded 3740 bytes in 0.031250s (116.875 KiB/s)</span><br />
<span style="background-color:#ecf0f1;">> <strong>mdw 0 2</strong></span><br />
<span style="background-color:#ecf0f1;">0x00000000: 10040000 10000b71</span><br />
<span style="background-color:#ecf0f1;">> <strong>reg sp 0x10040000</strong></span><br />
<span style="background-color:#ecf0f1;">sp (/32): 0x10040000</span><br />
<span style="background-color:#ecf0f1;">> <strong>reg pc 0x10000b71</strong></span><br />
<span style="background-color:#ecf0f1;">pc (/32): 0x10000B71</span><br />
<span style="background-color:#ecf0f1;">> <strong>resume</strong></span></span><br />
虽然中断向量表写在 0x0 处,但只有 reset 时才能加载,现在只能改 SP 和 PC 寄存器手动初始化。最后用 resume 命令让 M4 核跑起来。</p>
<p><br />
那么只有 Engineer mode 才能调试 M4 核?当然不是这样,只是这个模式下 ROM bootloader 让这个核启动了而已。默认,M4核是被停住的,需要软件让它起来:</p>
<p> 手册中说,BOOT_MCU 位(RCC的MP_GCR寄存器里面)写成1,当MCU复位时,才脱离Stop状态。那么我就来测试一下:将板子调到正常启动模式,通电,启动到 U-Boot 提示时在终端里按个键让 u-boot 等待。然后启动 OpenOCD,并使用 set ENG_MODE 1 连接板子。此时,查看 M4 核状态有所不同:</p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>targets</strong></span><br />
<span style="background-color:#ecf0f1;"> TargetName Type Endian TapName State</span><br />
<span style="background-color:#ecf0f1;">-- ------------------ ---------- ------ ------------------ ------------</span><br />
<span style="background-color:#ecf0f1;"> 0* stm32mp15x.cpu0 cortex_a little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 1 stm32mp15x.cpu1 cortex_a little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 2 stm32mp15x.axi mem_ap little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 3 stm32mp15x.ap1 mem_ap little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 4 stm32mp15x.cpu2 cortex_m little stm32mp15x.tap unknown</span><br />
<span style="background-color:#ecf0f1;"> 5 stm32mp15x.ap2 mem_ap little stm32mp15x.tap unknown</span></span></p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">因为它还处于 CStop 状态。我就将 BOOT_MCU 置1试试(寄存器地址 0x5000010C):</span></span></p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>mdw 0x5000010c</strong></span><br />
<span style="background-color:#ecf0f1;">0x5000010c: 00000000</span><br />
<span style="background-color:#ecf0f1;">> <strong>mww 0x5000010c 1</strong></span><br />
<span style="background-color:#ecf0f1;">> <strong>mdw 0x5000010c</strong></span><br />
<span style="background-color:#ecf0f1;">0x5000010c: 00000001</span></span></p>
<p>再让 MCU 复位。按 reset 开关当然不行,不能复位其它部分。从手册中我找到这个寄存器:</p>
<p>其中 bit2 是 MCURST, 写1就可以复位 MCU 了。于是执行:</p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>mww 0x50000404 2</strong></span><br />
<span style="background-color:#ecf0f1;">> <strong>targets</strong></span><br />
<span style="background-color:#ecf0f1;"> TargetName Type Endian TapName State</span><br />
<span style="background-color:#ecf0f1;">-- ------------------ ---------- ------ ------------------ ------------</span><br />
<span style="background-color:#ecf0f1;"> 0* stm32mp15x.cpu0 cortex_a little stm32mp15x.tap halted</span><br />
<span style="background-color:#ecf0f1;"> 1 stm32mp15x.cpu1 cortex_a little stm32mp15x.tap halted</span><br />
<span style="background-color:#ecf0f1;"> 2 stm32mp15x.axi mem_ap little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 3 stm32mp15x.ap1 mem_ap little stm32mp15x.tap running</span><br />
<span style="background-color:#ecf0f1;"> 4 stm32mp15x.cpu2 cortex_m little stm32mp15x.tap unknown</span><br />
<span style="background-color:#ecf0f1;"> 5 stm32mp15x.ap2 mem_ap little stm32mp15x.tap unknown</span></span></p>
<p>但 OpenOCD 还没有识别这个核的状态?可能是 OpenOCD 的问题,于是我将 OpenOCD 退出,重新连接,查看状态——好了,cpu2 处于 halted.</p>
<p> </p>
<p> 那么我就能将程序装到 SRAM 了,采用同样的 load_image 命令。然后,我将 MCU 复位让它从中断向量表获取入口地址。</p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>targets stm32mp15x.cpu2</strong></span></span></p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>load_image f:/test.hex</strong></span><br />
<span style="background-color:#ecf0f1;">664 bytes written at address 0x00000000</span><br />
<span style="background-color:#ecf0f1;">3076 bytes written at address 0x10000000</span><br />
<span style="background-color:#ecf0f1;">downloaded 3740 bytes in 0.031250s (116.875 KiB/s)</span></span></p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>targets stm32mp15x.cpu0</strong></span></span></p>
<p><span style="font-family:Courier;"><span style="background-color:#ecf0f1;">> <strong>mww 0x50000404 2</strong></span></span></p>
<p> 复位MCU的操作必须由MCU来进行,所以在 OpenOCD 中需要切换 targets. </p>
<p> 从 LED 闪烁状态我可以判定 M4 核程序已经正常运行了。</p>
<p> 这会 A7 核仍然在 halted 状态,那我就恢复它,然后让 U-Boot 接着完成后面的工作:启动 Linux. LED继续闪烁着,然后在 Linux 系统启动过程中,闪烁状态消失了——我想该是 Linux 接管了 GPIO,于是 M4 程序无法操作它们。在 OpenOCD 中却已无法调试 M4 核了,看来 Linux 对 M4 的运行已有干预了,这只有深入下去才知道……</p>
<p> 让 A7 核和 M4 核协同工作是更高级的话题。STM32MP157 硬件上有一些设计用来让MPU和MCU子系统之间通信,软件上也有相应支持的框架。若让 Linux 进入 Suspend to RAM 模式,保留 M4 核在低功耗运行处理简单任务,只在需要的时候才唤醒 A7 核恢复 Linux 执行,将比单纯的 Linux 系统更节省功耗。</p>
<p><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>谢谢楼主分享</p>
页:
[1]