Openocd one-liner for proper multicore RP2040 MCU reset?
sdbbs opened this issue · 2 comments
Hi all,
First, copying my query from https://stackoverflow.com/questions/72745969/openocd-one-liner-for-proper-multicore-mcu-reset :
I am working with a RP2040 multicore MCU running FreeRTOS SMP; and so far I have used the following one-liner command to restart the code running on the MCU (I use openocd in MINGW64 bash):
/c/path/to/openocd/src/openocd.exe -s /c/path/to/openocd/tcl -f interface/picoprobe.cfg -f target/rp2040.cfg -c "init ; reset ; exit"
A lot of the times this command works for me, but occasionally I've experienced, that when I use certain construct (e.g. timer ISR), and I use this command, something goes wrong - either the timer ISR does not start, or some FreeRTOS task does not start.
I spent quite a bit of time thinking this happens either due to improper coding on my side, or a bug in FreeRTOS - however, I almost by accident noticed, that if instead of the above command, I restart the code in a gdb
session - which means that in one terminal I have openocd running:
/c/path/to/openocd/src/openocd.exe -s /c/path/to/openocd/tcl -f interface/picoprobe.cfg -f target/rp2040.cfg
... and in another terminal, I have gdb
running:
gdb-multiarch my_progam.elf -ex 'target extended-remote localhost:3333'
... and I restart via r
un command in gdb
:
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: C:\path\to\my_progam\build\my_progam.elf
... then my code starts running properly, as expected!!
My only guess for this problem is, that when running the one-liner openocd command, access to both cores is probably not "simultaneous" and so there might happen a discrepancy in timing of the startup routine on the MCU; gdb
on the other hand, when starting up the session, halts both cores, and so (I guess), when doing r
un, the code is restarted synchronously across both cores, and things work as intended.
So, my question is: is there an openocd
one-liner command, that would restart the code on the MCU in the same way r
un in gdb
does?
I've also tried to run openocd with -d
, and captured the output while doing r
un in gdb
; I don't really understand it at this time, but I tried to isolate the openocd commands that run as a result of gdb
r
un command:
Debug: 743 10952 command.c:146 script_debug(): command - target names
Debug: 744 10953 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-start
Debug: 745 10953 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-start
Debug: 746 10953 command.c:146 script_debug(): command - transport select
Debug: 747 10953 command.c:146 script_debug(): command - transport select
Debug: 748 10954 command.c:146 script_debug(): command - rp2040.core0 invoke-event examine-start
Debug: 749 10954 command.c:146 script_debug(): command - rp2040.core0 arp_examine allow-defer
Debug: 758 10958 command.c:146 script_debug(): command - rp2040.core0 invoke-event examine-end
Debug: 759 10958 command.c:146 script_debug(): command - transport select
Debug: 760 10958 command.c:146 script_debug(): command - rp2040.core1 invoke-event examine-start
Debug: 761 10958 command.c:146 script_debug(): command - rp2040.core1 arp_examine allow-defer
Debug: 770 10962 command.c:146 script_debug(): command - rp2040.core1 invoke-event examine-end
Debug: 771 10962 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-assert-pre
Debug: 772 10962 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-assert-pre
Debug: 773 10963 command.c:146 script_debug(): command - transport select
Debug: 774 10963 command.c:146 script_debug(): command - rp2040.core0 arp_reset assert 1
Debug: 796 11047 command.c:146 script_debug(): command - transport select
Debug: 797 11048 command.c:146 script_debug(): command - rp2040.core1 arp_reset assert 1
Debug: 818 11141 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-assert-post
Debug: 819 11141 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-assert-post
Debug: 820 11142 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-deassert-pre
Debug: 821 11142 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-deassert-pre
Debug: 822 11142 command.c:146 script_debug(): command - transport select
Debug: 823 11142 command.c:146 script_debug(): command - rp2040.core0 arp_reset deassert 1
Debug: 828 11143 command.c:146 script_debug(): command - transport select
Debug: 829 11144 command.c:146 script_debug(): command - rp2040.core1 arp_reset deassert 1
Debug: 832 11144 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-deassert-post
Debug: 833 11145 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-deassert-post
Debug: 834 11145 command.c:146 script_debug(): command - transport select
Debug: 835 11145 command.c:146 script_debug(): command - rp2040.core0 was_examined
Debug: 836 11145 command.c:146 script_debug(): command - rp2040.core0 arp_waitstate halted 1000
Debug: 954 11189 command.c:146 script_debug(): command - rp2040.core0 curstate
Debug: 955 11190 command.c:146 script_debug(): command - transport select
Debug: 956 11190 command.c:146 script_debug(): command - rp2040.core1 was_examined
Debug: 957 11190 command.c:146 script_debug(): command - rp2040.core1 arp_waitstate halted 1000
Debug: 1070 11232 command.c:146 script_debug(): command - rp2040.core1 curstate
Debug: 1071 11233 command.c:146 script_debug(): command - rp2040.core0 invoke-event reset-end
Debug: 1072 11233 command.c:146 script_debug(): command - rp2040.core1 invoke-event reset-end
Debug: 1084 11246 gdb_server.c:2744 gdb_handle_vcont_packet(): target rp2040.core0 continue
... so I tried something like this:
/c/path/to/openocd/src/openocd.exe -s /c/path/to/openocd/tcl -f interface/picoprobe.cfg -f target/rp2040.cfg -c "init ; reset halt ; sleep 100; target names; target rp2040.core0 continue"
... but that fails with Error: invalid subcommand "rp2040.core0 continue"
.
So, unfortunately, I cannot deduce from this log, what sort of command to use, to ensure RP2040 is properly started, like it happens with gdb run
command - so I really hope someone can help with which commands I can use in an openocd one-liner, to properly restart the code ...
Ok, I think I found it.
Basically, what helped, was to run the openocd command without -c
commands, and connect to it with telnet
, instead of gdb
.
Then, in telnet
, it is easier to quickly explore the effect of commands, and look up help.
Eventually, I realized that rp2040.core0 arp_reset assert 1 ; rp2040.core1 arp_reset assert 1
basically halts each core individually - for me, it gives same effect as reset halt
; then to have the core running again, as with reset run
, you can issue rp2040.core0 arp_reset assert 0 ; rp2040.core1 arp_reset assert 0
- and this gives the same results (that is, sometimes timers/tasks do not run).
Then, since the above debug log mentions each core being targeted separately, I thought: what if, once the cores are halted, we run core 1 first? And indeed, that was the solution:
> targets
TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ ------------
0* rp2040.core0 cortex_m little rp2040.core0.cpu running
1 rp2040.core1 cortex_m little rp2040.core1.cpu running
> rp2040.core0 arp_reset assert 1 ; rp2040.core1 arp_reset assert 1
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
> rp2040.core1 arp_reset assert 0 ; rp2040.core0 arp_reset assert 0
Note that arp_reset assert 0
stops the reset, that is, allows the cpu core to run; we control which one goes first, here core 1, by calling rp2040.core1 arp_reset ...
first.
The same result can be obtained with reset halt
for both cores (instead of halting them individually via arp_reset assert 1
):
> targets
TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ ------------
0* rp2040.core0 cortex_m little rp2040.core0.cpu running
1 rp2040.core1 cortex_m little rp2040.core1.cpu running
> reset halt
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
> targets
TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ ------------
0* rp2040.core0 cortex_m little rp2040.core0.cpu halted
1 rp2040.core1 cortex_m little rp2040.core1.cpu halted
> rp2040.core1 arp_reset assert 0 ; rp2040.core0 arp_reset assert 0
And finally we can put this in a one-liner:
/c/path/to/openocd/src/openocd.exe -s /c/path/to/openocd/tcl -f interface/picoprobe.cfg -f target/rp2040.cfg -c "init ; reset halt ; rp2040.core1 arp_reset assert 0 ; rp2040.core0 arp_reset assert 0 ; exit"
The above command works for me (all timers and FreeRTOS tasks are running when I restart the code using that command) - so if others notice this problem too, maybe some explanation of this phenomenon should end up in the documentation.
Maybe submit this as a suggestion at https://github.com/raspberrypi/pico-feedback ?