I am using GNU gdb (GNU Tools for STM32 13.3.rel1.20240926-1715) 14.2.90.20240526-git connected to pyocd 0.39.0 with an STM32H573MIYxQ MCU. Normally I can set breakpoints in GDB and they work.
Sometimes I can't start GDB right at target power-up, so I reset the MCU (via physical reset pin) after starting GDB, in order to hit a breakpoint early in the application startup. This also works.
However some of my debugging setups don't have this physical pin, so I'd like to use a soft reset instead. Currently I use pyocd's target.reset() method. It definitely resets the target, but seems to cause breakpoints not to halt. GDB at first thinks the program has halted, but soon runs into trouble:
(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 gives a bit more characterization of the problem, by printing the following line soon after I type next in gdb:
ERROR:pyocd.coresight.cortex_m:cannot step: core not halted
What I take from this is that GDB was notified that the breakpoint was reached, but the MCU blew right past it (and continues running the application as if nothing had happened).
Does anyone have thoughts on what might cause the MCU to behave in this way?
Note: I've also tried target.reset_and_halt(); target.resume() and the result is the same. Also tried compiling the entire project with -O0 and -Og and the result is the same either way.
Update
I noticed that sometimes breakpoints still work after target.reset(). As a test, I set a delay after startup and before the breakpoint, and found a striking correlation between this delay and breakpoint operation:
10ms - fail
50ms - fail, fail
70ms - fail
85ms - fail, fail
90ms - success, fail
95ms - success, success
100ms - success, success
I can't think of anything in my application or pyocd scripts that would kick in after 90ms or so. I disabled all scripting actions except target.reset() and there is still this 90ms threshold. There are no interrupts in my application, nothing that would happen during this programmed wait. I tried the delay before or after peripheral initialization, but the effect is the same and the threshold is still around 90ms.
Update
Gemini suggested a partial workaround that seems to enable me to reset the target and hit early breakpoints:
(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)
The theory here is that the "GDB session" (maybe Gemini means the GDBserver module in pyocd?) needs to do something to "re-attach" but Gemini didn't give me details.
However it doesn't answer why target.reset() isn't working, and requires the reset command to be given from gdb rather than pyocd.