RP2040 and SWDv2

I finally got around to soldering my new RP2040 breakout board to try the MCU a bit before using it in any complex projects. The board is quite simple and consists only of a linear regulator, a W25Q16JVSSIM 16 megabit Winbond flash memory, a 12MHz crystal , some usual resistors and capacitors, and of course a RP2040. It was my first time soldering a QFN chip, but the soldering paste and a hot air station made it pretty easy even though I had to wick off some extra solder that bridged some pins.

The USB bootloader worked well enough — after I had noticed that I had mixed up the D+ and D- wires of the USB cable. On the contrary I couldn’t get my SWD debugger to connect to the chip properly. My SWD debugger is a SAMD21 based simple tool that knows a bit about the SWD protocol to check for errors, and I have written a driver for OpenOCD to actually use it. To get OpenOCD to work with RP2040 I had to upgrade it and do some merging, as apparently RP2040 nowadays has support in the mainline OpenOCD.

So when firing up OpenOCD the debugger could connect somehow with some parameters but could not find a MEM-AP even with much trying, and with some parameters the RP2040 didn’t respond at all. I tried multiple approaches to find the problem, one of them being that the suspicion that I might have a bug in my SWD debugger that appeared only now because OpenOCD was using dormant-to-SWD instead of JTAG-to-SWD sequence when connecting to the device. In the end, I did not find a bug but this did lead me to the right direction.

RP2040 uses SWDv2 as it has a support for multi-drop, i.e. you can communicate with multiple devices with one SWD port. In the case of RP2040 this mainly means that you can connect to both cores 0 and 1, though there is also a RESCUE target to place both cores in a known state. Herein lies also the reason to use a dormant-to-SWD sequence as the specification (ADIv5.2) states that a SWDv2 device must start in a dormant state.

Here are examples of how the communication starts, roughly speaking.

SWDv1:

  • JTAG-to-SWD
  • SWD line reset
  • Read DPIDR (from here on we need confirm ack in the responses)
  • Clear sticky errors

SWDv2:

  • Dormant-to-SWD
  • SWD line reset
  • Write to TARGETSEL to select a target (core 0 / core 1 in this case)
  • Read DPIDR
  • Clear sticky errors

Usually, when writing a value, you first specify the location of the write: one of the registers in the DP (debug port) or in the AP (access port), and then you get a three-bit response that must be OK, and only then you write a 32-bit value and one parity bit.

Now the question is: with multi-drop, no devices have been selected before the TARGETSEL is written to so which part of the system will respond with an ack? The specification has an answer: you won’t get a response.

As my debugger diligently checked for the acks before writing any data, in the case of the TARGETSEL the code never actually got to write any data, and no target got ever selected. Thus after a small modification to ignore the response when writing to the TARGETSEL, everything seemed to start working perfectly.

Finally, I get to start actually doing stuff with the RP2040.

Leave a Comment