raspberrypi/openocd

Openocd one-liner for proper multicore RP2040 MCU reset?

sdbbs opened this issue · 2 comments

sdbbs commented

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 run 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 run, 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 run in gdb does?


I've also tried to run openocd with -d, and captured the output while doing run 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 run 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 ...

sdbbs commented

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.

P33M commented

Maybe submit this as a suggestion at https://github.com/raspberrypi/pico-feedback ?