target.reset() 会导致断点不再暂停

编程语言 2026-07-09

我正在使用 GNU gdb (GNU Tools for STM32 13.3.rel1.20240926-1715) 14.2.90.20240526-git 连接到 pyocd 0.39.0,并搭载一颗STM32H573MIYxQ MCU。通常我可以在GDB中设置断点,它们能工作。

有时我不能在目标上电时直接启动GDB,因此在启动GDB之后,我通过物理复位引脚复位MCU,以便在应用程序启动的早期就命中一个断点。这也能工作。

然而我的一些调试设置没有这个物理引脚,因此我想改用软复位。当前我使用 pyocdtarget.reset() 方法。它确实会重置目标,但似乎会导致断点不暂停。GDB起初会认为程序已暂停,但很快就会遇到问题:

(gdb) break Sys.c:79
Breakpoint 2 at 0x8003894: file Sys.c, line 79.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) cont
Continuing.

[target.reset() invoked here by a separate path]

Breakpoint 2, Sys_Init () at Sys.c:79
79        if (!res)
(gdb) next
PC register is not available
(gdb)

pyocd 通过在我在 gdb 中输入 next 之后不久打印以下这行,提供了对问题的更多描述:

ERROR:pyocd.coresight.cortex_m:cannot step: core not halted

我的理解是,GDB收到已经到达断点的通知,但MCU直接越过了它(并继续像什么都没发生一样运行应用程序)。

有没有人对导致MCU以这种方式行为的原因有任何看法?

注:我也尝试过 target.reset_and_halt(); target.resume(),结果相同。还用 -O0-Og 编译整个工程,结果也是一样。

更新

我发现有时在 target.reset() 之后断点仍然可以工作。作为测试,我在启动后到断点之间设置了一个延迟,发现这个延迟与断点操作之间存在显著相关性:

10ms  - fail
50ms  - fail, fail
70ms  - fail
85ms  - fail, fail
90ms  - success, fail
95ms  - success, success
100ms - success, success

我想不出我的应用或pyocd脚本中有任何东西会在大约90ms后触发。我禁用除了 target.reset() 之外的所有脚本动作,仍然存在这个90ms的阈值。我的应用中没有中断,在这个编程等待期间也不会发生任何事情。我尝试在外围初始化之前或之后加入延迟,效果相同,阈值仍然在大约90ms。

更新

Gemini提出了一种部分可行的变通办法,似乎可以让我重置目标并在早期断点处命中:

(gdb) break Sys.c:79
Breakpoint 1 at 0x8003894: file Sys.c, line 79.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) monitor reset halt
Resetting target with halt
Successfully halted device on reset
(gdb) cont
Continuing.

Breakpoint 1, Sys_Init () at Sys.c:79
79        if (!res)
(gdb) next
80          INFO("SysInit() successful.");
(gdb)

理论上这里的想法是,“GDB会话”(也许Gemini指的是 pyocd 中的GDBServer模块?)需要做些什么来“重新附着”,但Gemini没有给出细节。

然而这并没有回答为什么 target.reset() 无法工作,也需要从 gdb 而不是 pyocd 发出重置命令。

解决方案

失败仅在断点事件之后发生,也就是GDB尝试单步时,pyOCD报告核心没有暂停。这强烈表明在软件触发的复位之后,调试服务器与目标核心状态之间的同步丢失,而不是断点配置或模块时钟使能的问题。

这一点得到进一步支持,因为来自GDB的 monitor reset halt 一直能稳定工作,而从外部调用 target.reset() 则不行,说明问题与调试会话状态重新附着和暂停的控制权相关,而不是初始化阶段的断点配置。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章