Uploading with st-flash iso openocd

I’ve been confused for quite a while now w.r.t. openocd and st-flash - at some point there was a tool-stlink package or similar, but I can’t find it. That’s the texane/stlink tool on GitHub, somewhat confusingly named since the executable is actually st-flash and the hardware is called “ST-Link”.

The puzzle I’m running into is that I can’t reliably upload to a Nucleo-L432KC board. The board probably doesn’t matter, only that it has an ST-Link v2.1 on-board (upgraded to the latest rev from STM). This happens under various conditions: having the µC in deep sleep is a known case where JTAG & SWD cease to work, but also enabling PA15 as UART RX on this Nucleo board, which disables JTDI. Which is surprising: I thought that only the two SWD pins were needed.

Anyway, it turns out that the default upload_protocol = stlink can’t re-flash the µC, whereas upload_protocol = mbed works 100% all the time. Likewise, st-flash write doesn’t work, but here’s the clue to what is needed: st-flash --connect-under-reset write works just fine. So the good news is that there are two reliable upload mechanisms. The third (somewhat tedious) option is to keep the tiny reset button pressed, start an upload, then release the button at just the right time, before openocd times out.

  1. Is there a way to force OpenOCD to upload-under-reset? I tried all the srts/trst -c variants, but none of them seemed to do the trick.

  2. How do I get PIO to install packages/tool-stlink? It used to be on my system, but it no longer is and I haven’t figured out what triggers this tool dependency.

P.S. Mbed uploads are quite a bit slower than openocd & st-flash, which is why I’m more interested in these alternatives.

FWIW, this works - using an st-flash installed locally (on MacOS in my case):

upload_protocol = custom
upload_command = st-flash --connect-under-reset write $SOURCE 0x8000000

(but I’d prefer to avoid a separate tool dependency)

Update - The essence stands that reset is needed to get out of some use cases, but “enabling PA15 as UART RX” messing up uploads was entirely my fault (bad bit masking in my code, doh).

Per SWD part 3 – SWO and nRST – Kudelski Security Research the right method is

reset_config srst_nogate connect_assert_srst

Did you try to do that on the commandline or better, in the config file of the board directly? (C:\Users\<user>\.platformio\packages\tool-openocd\scripts\board\x.cfg)

Looking at the package tool-stlink the last update to that was in the end of 2017 (v1.4.0), and I don’t recall any platofrm using that as an upload method these days, so I think it’s deprecated. One can ofc still use it manually / with scripting.

I could get this to work under Windows by downloading the stlink-1.7.0-x86_64-w64-mingw32.zip release from Releases · stlink-org/stlink · GitHub, putting a package.json inside it from the old version and then using the path to that package with

platform = ststm32
board = nucleo_f103rb
framework = arduino
upload_protocol = custom
extra_scripts = upload_stlink.py
platform_packages =

with upload_stlink.py being

from os.path import join
platform = env.PioPlatform()
uploader_tool = join(platform.get_package_dir("tool-stlink") or "",
                    "bin", "st-flash")
    UPLOADERFLAGS=["--connect-under-reset", "write"],

Resulting in

"C:\Users\Max\.platformio\packages\tool-stlink\bin\st-flash" --connect-under-reset write .pio\build\nucleo_f103rb\firmware.bin 0x8000000
st-flash 1.7.0
2021-08-11T09:47:15 INFO common.c: F1xx Medium-density: 20 KiB SRAM, 128 KiB flash in at least 1 KiB pages.
2021-08-11T09:47:16 INFO common.c: Flash written and verified! jolly good!
========================== [SUCCESS] Took 11.97 seconds ==========================

Another possibility would have also been pyOCD, which in its recent version supports

>pyocd flash --help
  -M MODE, --connect MODE
                        Select connect mode from one of (halt, pre-reset, under-reset, attach).

and that package is more recently updated (22nd of January 2021) and used in platforms (example). Sadly theh caveat here is that that is still not updated enough. And after nearly 1h of playing around with it and it nearly working (updated to pyOCD 0.31, installed device packs, used scripting) and it still failing (can’t recognize connected debug probes, but only when called within a PlatformIO context) I gave up on that one…

Wow … you’ve really gone the extra mile!

I replaced that command in target/stm32l4x.cfg line 83 by adding connect_assert_rst and got an error (Error: BUG: can't assert SRST).

The command I see with -v has: -f interface/stlink.cfg and -f target/stm32l4x.cfg (no board/ file).

That looks like a more forward-looking approach to me, than trying to get texane/stlink back into the mix (it’s C code, needs to be built for each platform, and it sometimes fails to identify the latest µC chip variants).

But the first hurdle to overcome, is to get an ST-Link v2.1 to generate a hardware reset through OpenOCD. Since the mbed & st-flash options can do it, I’m certain that the h/w is up to this task.

PS. My most immediate concern is gone, since I can upload again after fixing my own silly mistake. But IMO, upload-under-reset is really an important detail to get right (and document in the PIO docs) - any time you put the µC in standby or shutdown mode, or (accidentally) mess with the two SWD pins, you run into this problem of not being able to connect over JTAG or SWD. Not everyone will know that the solution is to keep the µC under reset while uploading new code (or put the µC into boot loader mode).

PS. Maybe it’d be better to rename this topic - “How to hard-reset via an ST-Link v2.1” or something?

To follow up - there is a way which resets just fine:

~/.platformio/packages/tool-openocd/bin/openocd -d2 -s /Users/jcw/.platformio/packages/tool-openocd/scripts -f board/st_nucleo_l4.cfg -c "program {.pio/build/blink/firmware.elf} verify reset; shutdown;"

The PIO command is different though, it does not use that board file:

openocd -d2 -s /Users/jcw/.platformio/packages/tool-openocd/scripts -f interface/stlink.cfg -c "transport select hla_swd" -f target/stm32l4x.cfg -c "program {.pio/build/blink/firmware.elf} verify reset; shutdown;"

Note the different files use. My hunch is that the order of files & commands matters, and that what I’ve tried manually doesn’t do the right thing, The board/st_nucleo_l4.cfg file includes the above two -f files, I think - but with some subtle differences.

Sooo … my conclusion so far is that the OpenOCD command constructed by PIO is good enough for normal uploads, but that a better choice would be to select the board file already present in the OpenOCD scripts. Then uploads will work regardless of the state of the µC.

Update - I’m not 100% certain of the above. Still trying to understand the different scenarios, but when I put the µC is standby mode, it seems to work, i.e. does a hard reset (whereas the PIO default doesn’t). FWIW, the board/st_nucleo_l4.cfg contents is as follows:

# Should work with all STM32L4 Nucleo Dev Boards.
# http://www.st.com/en/evaluation-tools/stm32-mcu-nucleo.html
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32l4x.cfg]
# use hardware reset
reset_config srst_only srst_nogate

To follow up, the above line (after source target!) appears to work in all cases.

I now use this in [env]:
upload_protocol = custom
upload_command = openocd -s $PROJECT_PACKAGES_DIR/tool-openocd/scripts -f interface/stlink.cfg -c "transport select hla_swd" $UPLOAD_FLAGS -c "reset_config srst_only srst_nogate" -c "program {$SOURCE} 0x08000000 verify reset; shutdown;"

… and this in each µC section, e.g. for L432:
upload_flags = -c, hla_serial 0669FF555052836687022922, -f, target/stm32l4x.cfg

1 Like