STM32MP157-M4-SRAM调试踩坑-flash脚本不跑main
技术博客|面向 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 requested 与 AP 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 比脚本高级,而是交互调试天然规避了前两节的三个问题:
- halt 时机对了:交互调试时,操作者(或
.gdbinit)会先敲monitor reset halt,把 M4 从混沌未知态拉到明确的复位暂停态——天然绕开问题①的unknown state。 - shutdown 时机对了:GDB 连接期间 OpenOCD 进程持续运行、不退出,
continue/resume之后人还在交互,不存在「resume 完立刻 shutdown 把程序打断」——天然绕开问题②。 - 向量表/状态处理对了: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_Handler,SystemInit 还没执行,还没进 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.s 的 Reset_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% 消除,与本系列已发布结论一致)。
修复验证标准:
AP write error, reset will not halt警告消除;target was in unknown state when halt was requested警告大幅减少、通常不再出现——但 MP157 双核 A7 抢跑特性下极端情况仍可能偶发,无法 100% 保证消除(与本系列已发布结论一致)。- 运行
flash-m4.bat一键下载结束,LED 正常闪烁,代表 M4 成功跑到 main 任务循环。 - 下载脚本执行完毕之后,重新启动 OpenOCD,GDB 连接 3334 端口,执行
monitor halt,查看 PC 寄存器落在 main 或者业务任务循环地址,而不是随机地址——说明 M4 在 OpenOCD 退出之后依旧持续运行。
七、额外配套重要经验(M4 SRAM 调试必看)
M4 内部 SRAM 靠 VDD_CORE 供电,普通 NRST 复位按键不会清空 SRAM 内容,SRAM 会残留旧程序。
- 想要彻底清空 M4-SRAM:关闭开发板总电源开关(切断 5V 总输入)等待 2~3 秒放电,不要只按 RESET 复位键。
- 不要迷信 OpenOCD 的
reset软件命令,MP157 双核特性,软件复位不能可靠控制 M4。 - 一键脚本调试 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 halt | MP157 双核,A7 优先启动,软件复位无法停住 M4 | 使用 reset halt + reset_config srst_only |
四行的根因与解法在正文第二、四、五节已展开;此表仅作速查。
系列文章索引
- LiteOS-M 移植①:环境搭建、编译链接脚本
- LiteOS-M 移植⑤:链接脚本与基础运行
- LiteOS-M PendSV 阻塞修复:从绕开调度器到 SVC+PendSV 标准启动
- LiteOS-M 再辨析:启动第一个任务到底需不需要 SVC?
- (上一篇)STM32MP157 M4 GDB 交互调试踩坑
- (本篇)STM32MP157 M4 一键脚本 SRAM 调试踩坑
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)