2023-11-06 01:46 AM - edited 2023-11-06 06:00 AM
Hello,
I got a radio MCU which connects to cloud and can receive binaries from database. And I got a STM32 that controls some stuff, including other MCUs. In production this means a lot of headers and programming time for all of them, no way to update remotely, and even if the code is thoroughly tested - glitches are still possible.
So I want to connect things in a chain, so that if something apart from the radio crashes - radio MCU could re-flash the whole system. This of course could be done with UART when using bootloaders, but this again means you have to pre-program bootloaders on MCUs, and there's still a very slim chance it might fail.
So I need an example of how one MCU can flash another via JTAG or SWD (preferably), and then verify the result. From there I could get it to work on Arduino / STM / AVR / radio ARM. What I really want is a proof of concept simple test example to show it works with the peripheral (bit banging is always possible, but surely there is a smarter way to do it using existing MCU resources), before I go ahead and order prototype boards.
In general this should not be black magic, we've all got some Nucleo boards, which are a great example that programming STM32 with STM32 is possible. On my NucleoF722ZE, the STM32F103CBT6 does the programming. It should receive packets via USB, then program the main MCU via either JTAG or SWD. It has a pair of jumpers - CN4, when closed - the jumper connects T_JTCK with SWCLK, and T_JTMS with SWDIO, and programs the big MCU via SWD interface.
When I look at how these signals are wired, I see this:
PB14 (SPI2_MISO) - T_JTMS - SWDIO
PB13 (SPI2_SCK) - T_JTCK - SWCLK
PB12 (SPI2_NSS) via resistor - T_JTMS - SWDIO
It leads me to think that SWD is done somehow with SPI2 in half-duplex master mode. I've seen people mentioning SPI for JTAG more than once, but I haven't found a good example.
If STM has the code for Nucleo STM32F103CBT6 programmer freely available - that would be everything I need to proceed with my project. But I can't find it.
What I have found so far:
JTAG bit-banging example: https://github.com/openwch/usb-jtag-spi/tree/main/src/OpenOCD/USB20JTAG
Free DAP, also bit-banging: https://github.com/ataradov/free-dap/blob/master/dap.c
Arduino SWD programmer, bit-banging: https://github.com/dimitar-kunchev/arduino-swd-programmer-samd/tree/master
LibSWD, opensource project to make SWD more available: https://github.com/cederom/LibSWD/tree/master
There are Black Magic probe source codes: https://github.com/blackmagic-debug/blackmagic
Wiki page on ST-link with some links to firmware reverse-engineering: https://stm32world.com/wiki/ST-Link
There's a lot of references to OpenOCD, but it's a ton of information, and at this point I don't even see if it's useful for me:
https://openocd.org/doc/html/index.html
There is a topic on Hackaday, which is about the hardware and firmware of STlink: https://hackaday.io/project/179054-custom-st-link-v20-v21-v30/details
At this point I'm just shocked that programmer firmware is something that people need to hack, instead of ST just providing a source code so people can use it with their ICs.
SWD tutorial page: https://robo.fish/wiki/index.php?title=SWD
ST-link open source project: https://github.com/stlink-org/stlink
There is also an adaptation of libSWD for ESP32 which in fact uses SPI, and this might be the most relevant resource: https://github.com/PaulFreund/libSWD-esp32
So it's not like there is no information on the topic. Almost all the code I found seems to bit-bang the data. I find it hard to believe that both JTAG and SWD on all STM32-based programmers are wired to SPI peripheral by pure coincidence :D Especially after that last repo for ESP32, which reads it via SPI.
Or maybe I'm just blind and don't see something right in front of me, and someone will be able to point it out :D
It looks like the best approach is to adopt Paul Freund's project to some Arduino board and check out if it works fine. In the meantime, if anyone knows of original STM32 STlink source, or SWD example, or Arduino/STM32/AVR source that uses SPI to do SWD, to upload the flash to STM32 and check if it uploaded correctly - please drop a comment.
I hope if this does not get resolved, at least the stuff I collected here might be of use.
UPD. 1
Another project, using SWD over SPI with Raspberry PI: https://github.com/lupyuen/pi-swd-spi
And it has a cool spreadsheet with commands and bits: https://docs.google.com/spreadsheets/d/12oXe1MTTEZVIbdmFXsOgOXVFHCQnYVvIw6fRpIQZybg/edit#gid=0
UPD. 2
A very interesting blog post explaining how to do SWD over SPI: https://www.pcbway.com/blog/technology/OpenOCD_on_Raspberry_Pi__Better_with_SWD_on_SPI.html
An Arduino example for monitoring SWD line (might be useful to test or verify something): https://levelup.gitconnected.com/listen-up-how-to-monitor-uart-i2c-swd-and-spi-with-arduino-9d8ef25da308
2023-11-06 04:17 AM
JTAG / SWD control is non-trivial and documented by ARM. You'd need to push in code specific to the STM32 to write FLASH, via the normal registers and interaction.
Less complex would be to initiate the ROM loader and use UART (AN3155) or other supported peripherals and protocols (AN2606) to communicate with code provided to do that.
2023-11-06 05:05 AM
Hi. Yes I know it would be less complex, but you're missing the point. Every ST-LINK does it, and there's a bunch of projects where people have done it. So non-triviality doesn't matter - it's done. In most cases it's done with bit-banging which is usable and portable, but not the best way to do it IMHO. So I did a bit of research and found out it has also been implemented over SPI by few awesome people.
I can try to adapt the code from PaulFreund or lupyuen, and I will do so if I need to. But it will take days to rewrite and test, and I don't want to reinvent the wheel if it's already been done by smart people, or ST have since published their sources, and I just didn't think to look for sources in the most obvious place.
So perhaps my explanation of what I wanted to achieve was a bit blurry - I am looking for an easier to use example, not alternatives or reasons to not do it :D While looking for materials, I've seen some 15+ people trying to do the same thing to simplify their production, not to mention those who try to clone ST-link for whatever reasons, so I believe this is a useful exercise.
2023-11-06 06:56 PM
Take a note that setting an RDP level 2 disables the SWD/JTAG debug interfaces completely and permanently.
2023-11-14 11:55 AM
Thanks for your input, this is of course good to remember. Yet this is a slightly different topic. I've really started this hoping that someone might add more information to the links I collected - solutions that are easier to port or test out, vids with explanations, links to repos.
2024-11-09 07:45 PM - edited 2024-11-09 07:49 PM
I've thought about how an SPI port can do SWD and here is my concept that I have not seen implemented. There is a small bit of electrical trickery to deal with the bidirectional nature of SWDIO. Here is the connection:
SPI SCLK ties to SWCLK. This is always unidirectional from host to target.
SPI MISO ties to SWDIO. This allows the host to read the bits sent from the target to the host.
SPI MOSI ties to SWDIO through a resistor, say 1K. This sends the bits from host to target.
The key insight here is that when the target is NOT driving SWDIO, the MOSI signal does through the 1 K resistor. When the target does drive SWDIO, the pin driver is strong enough to overcome the 1 K resistor and the MISO line picks up that signal.
An SWD transaction is 46 bits. For the bits sent by the host, set them to the desired bits you want the target to get. For the bits in the turn around spots, send a '1' and that provides a pullup on the SWDIO line. For the bits the target is supposed to drive, do the same, send a '1'. The target will replace the '1' with the bits it is driving.
Since SWD transaction is 46 bits, send a 48 bit SPI block and put two '0' bits at the end (or the beginning, doesn't matter). SWD transactions start with a '1' bit so the '0' bits will be ignored.
I think the proper CLK settings will be CPHA = 1, CPOL = 0 to get the right edges to clock on, but this is untested.
SWD requires a "reset" sequence of all '1's for 50 bits, so send 56 bits of '1' to achieve that.
II believe you can setup the 6 byte SPI outgoing bits, send them, and then decode the 48 bits you got back to align the response, check the ack bits, and do any msb/lsb swapping.
The MOSI resistor does limit clock speeds somewhat. Assuming a 20 pF wire loading, that is 20 ns delay on the MOSI to SWDIO signal. This suggests you should operate probably no faster than about 20 MHz, maybe 10 MHz, but this should still be a lot easier and likely faster than bit banging.
This idea is entirely untested, unimplemented, just a concept I had, so let me know if anybody gets it to work.