技术博客|面向 STM32MP157 M4 核 + LiteOS-M 移植的开发者
一句话结论:一键下载脚本「看着全部成功、LED 却不闪」,问题几乎不在你的代码,而在脚本的 halt 时机、shutdown 时机、向量表写入地址 这三处。本文把这条最隐蔽的坑一次性讲透。

环境

Windows + OpenOCD + arm-none-eabi-gcc + Makefile,M4 内核程序直接下载运行在内部 SRAM(0x10000000,不烧写 Flash,A7 不参与 M4 程序加载。

摘要

运行一键下载脚本 flash-m4.bat,OpenOCD 输出一切看着正常,但板子 LED 不闪烁、程序没跑起来;用手动 GDB 连接调试却一切正常,可以单步、可以跑 main。本文讲清楚 target was in unknown state when halt was requestedAP write error, reset will not halt 这两条高频告警的真相,以及旧脚本 init → halt → resume → shutdown 命令链里埋着的三个致命问题,并给出最小改动的修复方案与验证标准。

💡 适用场景:STM32MP157 M4 独立运行在内部 SRAM、LiteOS-M 操作系统、GCC + Makefile 裸金属开发,不使用 Keil、不使用 STM32CubeIDE。(上一篇讲 GDB 交互调试的坑,本篇讲一键脚本的坑,两篇互补。)


一、现象总览

运行一键下载脚本 flash-m4.bat,控制台输出看着一切正常,但板子 LED 不闪烁、程序没跑起来。下面用两张日志图把「看着成功」和「完整告警」都摆出来。

在这里插入图片描述

图1:unknown-state 告警日志(抛出问题)

博文用途:脚本输出看着全部正常,但程序实际不跑 main。

肉眼看:镜像下载成功、SP/PC 寄存器回显数值全部和链接脚本预期一致,没有报错。实际结果:LED 不闪烁,M4 根本没有跑到 main 函数。

手动用 GDB 连接 target extended-remote :3334 调试,一模一样的 elf 镜像,同样的 SP、PC,程序正常跑,LED 闪烁。

在这里插入图片描述

图2:AP write error 完整日志(问题分析)

博文用途:完整复现告警现场。

关键看点

  • AP write error, reset will not halt:软件复位无法把 M4 可靠停住(根因见第二节②);
  • stm32mp15x.cpu0 / cpu1: ran after reset and before halt:A7 双核上电抢跑;
  • [stm32mp15x.cm4] halted ... pc: 0x00000008 msp: 0x00000100:M4 停在**初始 PC 槽(向量表基址 + 0x08)**处,还没有真正进入你的 Reset_Handler。本工程链接脚本向量表基址是 0x10000000,正常应为 0x10000008;日志显示 0x00000008 是旧脚本把向量表误写到 RETRAM/别名区 0x0 所致(见第四节问题3)。

二、两条高频警告的人话解释

① Warn: target was in unknown state when halt was requested

人话:OpenOCD 刚连上 M4,完全不知道当前 CPU 是正在跑代码、还是停住、还是刚复位。直接强行发 halt 暂停命令,这个暂停不一定真的生效。

init 完成调试器握手,内核状态是混沌未知的。后面脚本修改 SP、PC、XPSR 寄存器,前提条件必须是 M4 已经被可靠 halt。如果 halt 没有真正生效,写寄存器就是无效操作或行为不可预测——此时你看到的 reg 打印出来的数值,只是 OpenOCD 本地缓存,不等于真正写到 CPU 寄存器里面了

注意:这条警告无法 100% 消除。MP157 是双核,A7 主核上电抢跑,M4 初始状态先天混沌,即便改用 reset halt,极端情况下仍可能偶发。我们的整改目标是大幅抑制、把必现变偶发,而非根治。

② Info : AP write error, reset will not halt

人话:STM32MP157 是双核,A7 主核上电抢跑;OpenOCD 软件复位命令无法把 M4 内核可靠停住。不是硬件坏,是芯片的硬件特性。

MP157 上电永远 A7 双核先启动跑起来,M4 是从核。单纯软件 reset 指令不能保证 M4 被停下,于是 OpenOCD 写调试 AP 寄存器时报 AP write error,并提示 reset will not halt

不要用 -c "halt",要用 -c "reset halt",用 SRST 硬件复位再 halt。

注意 cfg 配置 reset_config srst_only,只复位 M4,不会干扰 A7。


三、为什么 GDB 手动交互调试不会踩这三个坑

同一份 elf,GDB 连 3334 端口单步 / 跑 main 都正常,并不是 GDB 比脚本高级,而是交互调试天然规避了前两节的三个问题:

  1. halt 时机对了:交互调试时,操作者(或 .gdbinit)会先敲 monitor reset halt,把 M4 从混沌未知态拉到明确的复位暂停态——天然绕开问题①的 unknown state
  2. shutdown 时机对了:GDB 连接期间 OpenOCD 进程持续运行、不退出,continue/resume 之后人还在交互,不存在「resume 完立刻 shutdown 把程序打断」——天然绕开问题②。
  3. 向量表/状态处理对了:GDB 加载 elf 通常用 load 并正确设置入口与 Thumb 状态,不会把向量表写到错误地址——天然绕开问题③的表象。

所以「脚本看着成功、GDB 正常」的差异,本质不在你的代码,而在脚本命令链缺了 halt 时机、shutdown 时机、向量表地址 这三处。


四、旧脚本三个致命问题(根因分析)

问题 1:init 之后直接 halt,M4 状态未知

旧命令链:init → targets → halt

init 完成调试器连接,此时 M4 状态未知,直接 halt 就弹出 unknown state 警告。

风险:寄存器写入不可靠。脚本设置 sp/pc 看似打印正常,实际 CPU 并没有真正更新寄存器,resume 之后跑飞 HardFault。

问题 2:resume 之后立刻 shutdown,程序来不及跑

旧命令:-c "resume" -c "shutdown"

resume 通知 M4 开始执行,紧接着马上 shutdown,OpenOCD 进程直接退出。OpenOCD 退出时会做调试适配器 detach 清理动作。M4 刚跳转到 Reset_HandlerSystemInit 还没执行,还没进 main,调试端口被强行断开,程序直接被打断。

问题 3:向量表 mww 写入地址写错

脚本旧代码:

-c "mww 0x00000000 0x10060000"
-c "mww 0x00000004 %ENTRY%"

我们工程链接脚本 stm32mp157_m4.lds,向量表 .isr_vector 放在 SRAM 起始地址 0x10000000。但是脚本却写到了 0x00000000(这是 BOOT 别名 / RETRAM 备份域)。

当前为什么还没彻底跑飞?因为脚本紧接着使用 reg sp / reg pc 直接强行改写 CPU 内核寄存器,绕过向量表读取。
隐患:未来一旦删掉 reg 强制写寄存器两行,依靠硬件读取向量表启动,M4 直接跑飞。

补充小细节:脚本 reg sp 0x10060000 是冗余的。启动汇编 startup_stm32mp15xx.sReset_Handler 第一行就会重新加载 SP 为 _estack = 0x10060000,写了无害,不影响。


五、最小改动修复方案

bat 脚本关键片段:

"%OCD_BIN%" ^
  -s "%OCD_SCRIPTS%" ^
  -f openocd/board_stm32mp157_m4.cfg ^
  -c "init" ^
  -c "targets stm32mp15x.cm4" ^
  -c "reset halt" ^                              ::整改1:硬件 SRST 复位再 halt,大幅抑制 unknown-state 警告(双核特性,偶发难根除)
  -c "load_image build/m4_liteos.elf" ^
  -c "mww 0x10000000 0x10060000" ^                ::整改3:修正向量表写入地址,和链接脚本匹配
  -c "mww 0x10000004 %ENTRY%" ^
  -c "reg sp 0x10060000" ^
  -c "reg pc %ENTRY%" ^
  -c "reg xpsr 0x01000000" ^                      ::补强:显式置 Thumb 状态位,防止进 ARM 态触发 UsageFault
  -c "resume" ^
  -c "sleep 200" ^                               ::整改2:留出 200ms,等待 M4 跑起来再 shutdown
  -c "shutdown"

三处整改 + 一处补强说明

1. -c "halt" 改为 -c "reset halt"

使用硬件 SRST 复位 M4 内核,再 halt,M4 进入明确的复位暂停状态,大幅抑制 unknown-state 警告——但 MP157 双核 A7 抢跑特性下,极端情况仍可能偶发,无法 100% 保证消除(与本系列已发布结论一致),后续写寄存器在已 halt 前提下真正生效。

cfg 中必须配置 reset_config srst_only,只复位 M4,不会干扰 A7 内核。

2. resume 之后新增 -c "sleep 200"

给 M4 留出 200ms 执行时间,完成 Reset_Handler → SystemInit → 库初始化 → main 任务循环。等程序真正跑起来之后,再执行 shutdown 断开调试器。

OpenOCD 退出时的 detach 不会打断已经正在运行的 M4 程序。

3. mww 写内存地址从 0x00000000 改成 0x10000000

和链接脚本向量表基地址完全对齐,代码不依赖 reg pc 强行跳转也可以正常启动,消除隐患。

4.(原脚本缺失,本次补上)reg xpsr 0x01000000

Cortex-M 必须运行在 Thumb 态,xPSR 的 T 位(bit24)必须置 1。脚本用 reg pc %ENTRY% 直接强跳入口时,入口地址低位通常已是 1(Thumb 函数地址为奇数),硬件据 BX 地址低位进 Thumb;但显式设 xPSR = 0x01000000(T 位=1)作为兜底,可避免任何状态下因状态位异常而进入 ARM 态,触发 UsageFault。

关于 reset halt 之后为何还要 reg pc 强跳reset halt 把 M4 停在复位向量处、等待复位释放;脚本为了让程序立刻从 SRAM 入口跑起来、不依赖复位释放时序,直接改 sp/pc 强制跳转到 entry。同时 mww 把向量表 SP/PC 槽也写正确,作为双保险——即使将来去掉 reg 强写、改回依赖硬件读向量表启动,也能正常。


六、修复之后验证标准

修复之后重新运行 flash-m4.bat,控制台输出趋于干净(AP write error 通常消除;unknown-state 大幅减少,但双核特性下极端情况仍可能偶发,无法 100% 消除):

在这里插入图片描述

图3:修复之后正常输出日志(效果对比)

博文用途:对比图。修复后,AP write error 警告消除,unknown-state 警告大幅减少(双核特性下仍可能偶发,无法 100% 消除,与本系列已发布结论一致)。

修复验证标准:

  1. AP write error, reset will not halt 警告消除;target was in unknown state when halt was requested 警告大幅减少、通常不再出现——但 MP157 双核 A7 抢跑特性下极端情况仍可能偶发,无法 100% 保证消除(与本系列已发布结论一致)。
  2. 运行 flash-m4.bat 一键下载结束,LED 正常闪烁,代表 M4 成功跑到 main 任务循环。
  3. 下载脚本执行完毕之后,重新启动 OpenOCD,GDB 连接 3334 端口,执行 monitor halt,查看 PC 寄存器落在 main 或者业务任务循环地址,而不是随机地址——说明 M4 在 OpenOCD 退出之后依旧持续运行。

七、额外配套重要经验(M4 SRAM 调试必看)

M4 内部 SRAM 靠 VDD_CORE 供电,普通 NRST 复位按键不会清空 SRAM 内容,SRAM 会残留旧程序

  1. 想要彻底清空 M4-SRAM:关闭开发板总电源开关(切断 5V 总输入)等待 2~3 秒放电,不要只按 RESET 复位键。
  2. 不要迷信 OpenOCD 的 reset 软件命令,MP157 双核特性,软件复位不能可靠控制 M4。
  3. 一键脚本调试 SRAM 场景,resume 之后一定要 sleep 一段时间再 shutdown;不要 resume 立刻退出进程,否则会出现「控制台看着全部正常,程序就是不跑」的诡异现象。

八、总结坑点清单

现象根因解决办法
OpenOCD 打印 unknown state when halt was requested(偶发,无法 100% 消除)刚 init 直接 halt 致状态未知;双核 A7 抢跑致状态偶发混沌改用 reset halt 大幅抑制,仍可能偶发
脚本下载成功,LED 不闪,GDB 手动调试正常resume 后马上 shutdown,M4 还没跑 main 就被 detach 打断resume 后面加 sleep 延时
mww 写到 0x00000000 向量表地址错误向量表实际在 0x10000000,脚本地址写错位修改 mww 目标地址为 0x10000000
AP write error, reset will not haltMP157 双核,A7 优先启动,软件复位无法停住 M4使用 reset halt + reset_config srst_only

四行的根因与解法在正文第二、四、五节已展开;此表仅作速查。


系列文章索引

  1. LiteOS-M 移植①:环境搭建、编译链接脚本
  2. LiteOS-M 移植⑤:链接脚本与基础运行
  3. LiteOS-M PendSV 阻塞修复:从绕开调度器到 SVC+PendSV 标准启动
  4. LiteOS-M 再辨析:启动第一个任务到底需不需要 SVC?
  5. (上一篇)STM32MP157 M4 GDB 交互调试踩坑
  6. (本篇)STM32MP157 M4 一键脚本 SRAM 调试踩坑
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐