Making a Black Magic probe from an STLink/v2 clone

Created 2024-03-13

Table of contents

In this project I've bought cheap STLink clones, found out they don't support debugging RP2040 and then fixed them using FOSS firmware replacement which also made them way better.

I've been using spare Raspberry Pi Pico's as a Pico Probe1, that's Raspberry Pi's first party firmware for using any Pico board as a SWD debugger which can be used from OpenOCD. This feels quite janky, you need to remember which pins to use, you need a USB Micro-B cable and you need some special OpenOCD configuration.

I could buy the special Pico Probe board from Raspberry Pi but that still uses a USB Micro-B cable and their own special cables and more importantly I've also been having some issues with the debugger cutting out so I wanted something more reliable and convenient.

In school I've used an STLink debugger which I've never had any issues with, it was part of the Nucleo boards we used, but I know they're selling standalone debuggers too. When I searched for them I found they were pretty expensive (almost as much as a whole Nucleo board) but there was a lot of cheap clones available. Moreover the clones seem to have the perfect form-factor, the original STLink is a big ugly piece of plastic where the clones are shaped like a USB drive with the header at the end.

It didn't work

When they arrived in mail I've connected one to a Pico, ran OpenOCD and ooops.

> openocd -f /usr/share/openocd/scripts/interface/stlink.cfg -f /usr/share/openocd/scripts/target/rp2040.cfg
Open On-Chip Debugger 0.12.0
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Error: Debug adapter doesn't support 'swd' transport
/usr/share/openocd/scripts/target/rp2040.cfg:7: Error: 
in procedure 'script' 
at file "embedded:startup.tcl", line 28
at file "/usr/share/openocd/scripts/target/rp2040.cfg", line 7

What do you mean does not support SWD? That's its only job to support SWD. A quick search later and I found the answer.

It's true that the original ST-LINK firmware does not support multi-drop SWD debug protocol. RP2040 has two CPU cores, and multi-drop SWD support seems to be required even when you only want to connect to one of them.
-- jpa @ electronics.stackexchange2

The answer also mentioned a piece of very useful information - Black Magic3, an open source debug probe firmware also support this STLink clone hardware, adding support for many more targets than just STM8 and STM32 chips and also improving usability by removing the need for OpenOCD!

Black Magic uses a special purpose utility for flashing the firmware to an STLink, stlink-tool4. Unfortunately we'll need to run it every time we plug in the STLink probe to switch it to the right mode, which is a bit annoying, but it's still simpler than remembering how to use OpenOCD.

It depends on libusb1, the current libusb archlinux package worked for me.

> git clone --recursive https://github.com/blackmagic-debug/stlink-tool
[...]
> cd stlink-tool
> make
cc -std=c11 -Wall -Wextra -Werror -I/usr/include/libusb-1.0 -g -Og -D_DEFAULT_SOURCE -o src/main.o -c src/main.c
cc -std=c11 -Wall -Wextra -Werror -I/usr/include/libusb-1.0 -g -Og -D_DEFAULT_SOURCE -o src/stlink.o -c src/stlink.c
cc -std=c11 -Wall -Wextra -Werror -I/usr/include/libusb-1.0 -g -Og -D_DEFAULT_SOURCE -o src/crypto.o -c src/crypto.c
cc src/main.o src/stlink.o src/crypto.o tiny-AES-c/aes.o -lusb-1.0 -o stlink-tool
> sudo cp stlink-tool /usr/local/bin/

To build the firmware we'll need an bare metal ARM toolchain, fortunately the arm-none-eabi-gcc package on archlinux worked out of the box and I didn't need to do any extra work^^

> git clone --recursive https://github.com/blackmagic-debug/blackmagic
[...]
> cd blackmagic
> meson setup build --cross-file cross-file/stlink.ini
[...]

  User defined options
    Cross files              : cross-file/stlink.ini

Found ninja-1.11.1 at /usr/bin/ninja
> meson compile -C build
[...]
/usr/lib/gcc/arm-none-eabi/13.2.0/../../../../arm-none-eabi/bin/ld: region `rom' overflowed by 2568 bytes
Memory region         Used Size  Region Size  %age Used
             rom:      133640 B       128 KB    101.96%
             ram:       12140 B        20 KB     59.28%
collect2: error: ld returned 1 exit status
[404/493] Compiling C object blackmagic.p/src_target_adiv5_jtag.c.o
ninja: build stopped: subcommand failed.

Oops again. I'll have to remove some target support to make the firmware smaller. Fortunately for me I don't need anything besides the RP2040 target, so I removed a few targets I thought I was least likely to need in the near future. While I was there I also enabled the use of SWIM pins for UART.

--- a/cross-file/stlink.ini
+++ b/cross-file/stlink.ini
@@ -18,7 +18,7 @@
 
 [project options]
 probe = 'stlink'
-targets = 'cortexm,hc32,lpc,nrf,nxp,renesas,rp,sam,stm,ti'
+targets = 'cortexm,nrf,nxp,rp,sam,stm,ti'
 rtt_support = false
-stlink_swim_as_uart = false
+stlink_swim_as_uart = true
 bmd_bootloader = false

And rebuild.

> meson setup build --cross-file cross-file/stlink.ini --wipe
[...]

  User defined options
    Cross files              : cross-file/stlink.ini

Found ninja-1.11.1 at /usr/bin/ninja
> meson compile -C build
[...]
[388/482] Linking target blackmagic_stlink_firmware.elf
Memory region         Used Size  Region Size  %age Used
             rom:      117868 B       128 KB     89.93%
             ram:        3724 B        20 KB     18.18%
[482/482] Linking target blackmagic

And now it builds :)

Flashing

To flash the firmware we need the STLink device to be in DFU (bootloader) mode, this is the default mode it initializes in when connected. Running the stlink-tool utility without arguments runs the flashed code. Try it out by plugging the device in and then run the utility twice.

> stlink-tool
ST-Link v2/v2.1 Bootloader found
Firmware version : V2J34S7
Loader version : 14152
ST-Link ID : 066BFC323148444257391531
Firmware encryption key : EF8725105916078C1BEBE2CE77DD7E21
Current mode : 1
> stlink-tool
ST-Link v2/v2.1 Bootloader found
Firmware version : V2J34S7
Loader version : 14152
ST-Link ID : 000000000000000000000000
Firmware encryption key : 1D05F6CAFEF730AA71439BC208172DA8
Current mode : 256
ST-Link dongle is not in the correct mode. Please unplug and plug the dongle again.

For the firmware that was flashed on mine this switched it to some mode 256 in which it was no longer possible to flash. Reconnecting the device resets it back to DFU mode. So we reconnect the device and then run the flashing command.

> stlink-tool build/blackmagic_stlink_firmware.bin
ST-Link v2/v2.1 Bootloader found
Firmware version : V2J34S7
Loader version : 14152
ST-Link ID : 066BFC323148444257391531
Firmware encryption key : EF8725105916078C1BEBE2CE77DD7E21
Current mode : 1
Type V2
....................................................................................................

After the animated dots the Black Magic firmware immediately runs, but next time we connect the debugger we will need to run stlink-tool without arguments to run the firmware. In dmesg we see the debugger reconnecting as a new device as we run stlink-tool.

usb 2-2: new full-speed USB device number 24 using xhci_hcd
usb 2-2: New USB device found, idVendor=0483, idProduct=3748, bcdDevice= 1.00
usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-2: Product: STM32 STLink
usb 2-2: Manufacturer: STMicroelectronics
usb 2-2: SerialNumber: 3ÿj\x05AB714\x16\x19W
usb 2-2: USB disconnect, device number 24
usb 2-2: new full-speed USB device number 25 using xhci_hcd
usb 2-2: New USB device found, idVendor=1d50, idProduct=6018, bcdDevice= 1.09
usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-2: Product: Black Magic Probe (ST-Link/v2) v1.10.0-634-gfa79ab6b-dirty
usb 2-2: Manufacturer: Black Magic Debug
usb 2-2: SerialNumber: 066BFC323148444257391531
cdc_acm 2-2:1.0: ttyACM0: USB ACM device
cdc_acm 2-2:1.2: ttyACM1: USB ACM device

Those ttyACM0 and ttyACM1 devices correspond to the debugger and UART adapter. On my device the SWCLK and SWDIO pins match their label, UART Tx is on pin labeled RST and UART Rx (the one reading from attached device) on pin labeled SWIM.

Again, after plugging the debugger in we need to run the stlink-tool once to start the firmware. With the Black Magic firmware when we run it a second time it won't show up as an STLink at all and won't do anything, it's ok to run it more than once accidentally even while you're using the device.

> stlink-tool
ST-Link v2/v2.1 Bootloader found
Firmware version : V2J39S7
Loader version : 14152
ST-Link ID : 066BFC323148444257391531
Firmware encryption key : D026981A4C70D368E14F25B31CA48014
Current mode : 1
> stlink-tool
Trying to switch BMP/Application to bootloader
Can not open BMP/Application!
No ST-Link in DFU mode found. Replug ST-Link to flash!

We can use dmesg as shown above to find the TTY device names but if it's the only serial device they'll always be ttyACM0 for the debugger and ttyACM1 for the UART adapter. To connect to the debugger we'll need a compatible GDB build, on archlinux the arm-none-eabi-gdb package worked for me.

> arm-none-eabi-gdb
GNU gdb (GDB) 14.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) target extended-remote /dev/ttyACM0
Remote debugging using /dev/ttyACM0
(gdb) monitor swd_scan
Target voltage: 3.23V
SWD scan failed!
Failed
(gdb)

Aha, we can see the SWD debugger but there is no device attached. Attaching a Rpi Pico using SWD and 5V pin to VBUS we get.

[...]
(gdb) monitor swd_scan
Target voltage: 3.23V
Available Targets:
No. Att Driver
 1      Raspberry RP2040 M0+
 2      Raspberry RP2040 M0+
 3      Raspberry RP2040 Rescue (Attach to reset!)
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x10001f28 in ?? ()
(gdb)

Since we didn't call GDB with any specific file it attached to the Pico with whatever it was already running. So let's use the Pico Logic Anayzer from last time as an example.

[...]
(gdb) file ~/code/sigrok-pico/pico_sdk_sigrok/build/pico_sdk_sigrok.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from ~/code/sigrok-pico/pico_sdk_sigrok/build/pico_sdk_sigrok.elf...
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/max/code/sigrok-pico/pico_sdk_sigrok/build/pico_sdk_sigrok.elf 
^C
Program received signal SIGINT, Interrupt.
0x10003646 in sleep_until ()
(gdb) backtrace
#0  0x10003646 in sleep_until ()
#1  0x100036b0 in sleep_us ()
#2  0x10001a10 in main ()
(gdb) 

There we go, a working debugger :)

STLink debugger plugged in into a laptop. It looks like a shiny blue USB
drive with a blue LED except it has a 10 pin header connector at the end. It's
connected to a Raspberry Pi Pico board with two sets of 4 wire jumpers, each has
one wire unused, one is connected to the Pico SWD header and one is connected at
the opposite side of the board where the UART pins are.