Use Raspberry Pi (2/3) as a JTAG/SWD adapter

Did you know that we can use the Raspberry Pi GPIO to bitbang the JTAG/SWD protocols? We can actually use our Raspberry Pi as a JTAG adapter for programming microcontrollers, FPGAs, or another Raspberry Pi! JTAG is a protocol similar to SPI, but works a little different. It’s commonly used for board bring-up, debugging, and programming in bare-metal environments. It’s incredibly useful because you have direct low-level access to hardware and peripherals, which allows things like reading and modifying CPU registers or reading/writing memory.

SWD is a newer technology developed around Cortex-based processors which boasts a reduced pin-count vs JTAG (2 vs 4) and can actually be used over JTAG pins if the device supports it. While SWD doesn’t support things like boundary-scan, it’s perfectly suited for debugging and bare-metal programming. Let’s get started!


We’ll need a couple of parts to get going:

  • Raspberry Pi (2/3) – I will be using the Pi 3 for my example
  • Rasbpian Jessie with latest firmware (I’m partial to Debian, sorry Red Hat guys)
  • Female to female jumper wires (3 or 4 for SWD, and 5-7 for JTAG, more information on this later)
  • A solid 2.5A 5V power supply (this is important always)

Let’s get our repositories and packages up to date:

Once we’re updated, we’ll need to install some dependencies for OpenOCD:

This should provide all the pieces of the puzzle we need to compile OpenOCD. Next we’ll need to pull the latest source for OpenOCD so we can compile it:

This will clone the OpenOCD repository to a directory called ‘openocd’ in your current working directory. Next we’ll need to enter this directory, and start the process of compiling from source:

During this process, the scripts will download JimTCL and other dependencies for OpenOCD. Once that’s complete, we’ll move on to configuring our options for OpenOCD:

If all goes successfully, we’re finally ready to compile:

We will use ‘make -j4’ for the Pi 2/3 because of the four cores. This will improve the time it takes to compile OpenOCD by distributing the workload across all four cores (it’ll take a little while, so get comfortable.)

If we’ve finished without errors, our next step is to finally install it:

At this point, we have OpenOCD configured properly for our GPIO bitbanging on the Pi, in addition to compiled binaries and scripts that are now installed. Let’s take a side-step for a moment here and take a look at the pins we’ll need:


JTAG PINOUT:

FUNC GPIO PIN #
TCK GPIO11 23
TMS GPIO25 22
TDI GPIO10 19
TDO GPIO9 21
SRST* GPIO18 12
TRST* GPIO7 26
GND GND 20

* NOTE: Pins are optional, I recommend using SRST and ignoring TRST. The reasons why are out of the scope of this tutorial.

SWD PINOUT:

FUNC GPIO PIN
SWCLK GPIO25 22
SWDIO GPIO24 18
SRST GPIO18 12
GND GND 14

You’ll need to identify the pins on your target and connect them to your Raspberry Pi according to the charts above. Because target configurations are independent, you’ll need to find your configuration file for your target device in ‘/usr/local/share/openocd/scripts/target/’

We’ll need to make some quick modifications to the Raspberry Pi 2/3 interface configuration file depending on which protocol we will be using. Here are my examples:

JTAG:

SWD:

You can save either of these files in your home directory (/home/pi/) and name them whatever you want, I chose rpi_swd.cfg and rpi_jtag.cfg (Remember, you’ll need to be in the directory where your configuration files are when you execute OpenOCD, at least to make it easy.)

While OpenOCD has this script already, you may have problems if you need to modify it because of permissions. It’s easier to modify it locally if you need to. I also noticed an error in the RPi2 interface configuration they provide; they state SRST is pin 18/GPIO24, but it’s actually pin 12/GPIO18.

Now that our preparation is done, we can start debugging. Remember to find the target configuration file for your device first. Here’s what I would do for my STM32L476RG:

This launches our OpenOCD/gdb server which we’ll need to connect to using telnet. You can use netcat or the telnet client, I personally prefer the telnet client. We’ll open a new terminal window or SSH connection on the Pi and run this command to connect to OpenOCD:

That’s it! We can debug our devices using our Raspberry Pi and OpenOCD. If you have any questions or need further clarification, leave a comment.

 

11 Comments

  1. tatery

    Hi,
    This tutorial is really great, thank you so much for it.

    I have some issue with reset using RPi as SWD programmer. It seems like reset does not work or at least my MCU (SAM4E8C) does not perform reset.
    This is my configuration file:

    source [find rpi_swd.cfg]
    transport select swd
    set CHIPNAME AT91SAM4E8C
    source [find target/at91sam4XXX.cfg]
    set _FLASHNAME $_CHIPNAME.flash
    flash bank $_FLASHNAME at91sam4 0x00400000 0 1 1 $_TARGETNAME
    init
    targets
    reset halt
    program firmware.bin verify reset 0x410000

    log from execution:
    sudo openocd
    Open On-Chip Debugger 0.10.0-dev-00371-g81631e4 (2016-08-29-12:46)
    Licensed under GNU GPL v2
    For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
    BCM2835 GPIO nums: swclk = 25, swdio = 24
    BCM2835 GPIO config: srst = 18
    srst_only separate srst_gates_jtag srst_push_pull connect_deassert_srst
    adapter speed: 100 kHz
    adapter_nsrst_delay: 100
    cortex_m reset_config sysresetreq
    Info : BCM2835 GPIO JTAG/SWD bitbang driver
    Info : SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)
    Info : clock speed 100 kHz
    Info : SWD DPIDR 0x2ba01477
    Info : AT91SAM4E8C.cpu: hardware has 6 breakpoints, 4 watchpoints
    TargetName Type Endian TapName State
    — —————— ———- —— —————— ————
    0* AT91SAM4E8C.cpu cortex_m little AT91SAM4E8C.cpu running
    AT91SAM4E8C.cpu: target state: halted
    target halted due to debug-request, current mode: Thread
    xPSR: 0x21000000 pc: 0x00442aae psp: 0x20007b28
    AT91SAM4E8C.cpu: target state: halted
    target halted due to debug-request, current mode: Thread
    xPSR: 0x21000000 pc: 0x00442aae psp: 0x20007b28
    ** Programming Started **
    auto erase enabled
    Info : sam4 does not auto-erase while programming (Erasing relevant sectors)
    Info : sam4 First: 0x00000008 Last: 0x00000026
    Info : Erasing sector: 0x00000008
    Info : Erasing sector: 0x00000009
    Info : Erasing sector: 0x0000000a
    Info : Erasing sector: 0x0000000b
    Info : Erasing sector: 0x0000000c
    Info : Erasing sector: 0x0000000d
    Info : Erasing sector: 0x0000000e
    Info : Erasing sector: 0x0000000f
    Info : Erasing sector: 0x00000010
    Info : Erasing sector: 0x00000011
    Info : Erasing sector: 0x00000012
    Info : Erasing sector: 0x00000013
    Info : Erasing sector: 0x00000014
    Info : Erasing sector: 0x00000015
    Info : Erasing sector: 0x00000016
    Info : Erasing sector: 0x00000017
    Info : Erasing sector: 0x00000018
    Info : Erasing sector: 0x00000019
    Info : Erasing sector: 0x0000001a
    Info : Erasing sector: 0x0000001b
    Info : Erasing sector: 0x0000001c
    Info : Erasing sector: 0x0000001d
    Info : Erasing sector: 0x0000001e
    Info : Erasing sector: 0x0000001f
    Info : Erasing sector: 0x00000020
    Info : Erasing sector: 0x00000021
    Info : Erasing sector: 0x00000022
    Info : Erasing sector: 0x00000023
    Info : Erasing sector: 0x00000024
    Info : Erasing sector: 0x00000025
    Info : Erasing sector: 0x00000026
    wrote 253952 bytes from file firmware.bin in 20.702446s (11.979 KiB/s)
    ** Programming Finished **
    ** Verify Started **
    verified 248528 bytes in 3.864183s (62.808 KiB/s)
    ** Verified OK **
    ** Resetting Target **

    When I run openocd second time (after manual MCU reset) I’ve got this:
    sudo openocd
    Open On-Chip Debugger 0.10.0-dev-00371-g81631e4 (2016-08-29-12:46)
    Licensed under GNU GPL v2
    For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
    BCM2835 GPIO nums: swclk = 25, swdio = 24
    BCM2835 GPIO config: srst = 18
    srst_only separate srst_gates_jtag srst_push_pull connect_deassert_srst
    adapter speed: 100 kHz
    adapter_nsrst_delay: 100
    cortex_m reset_config sysresetreq
    adapter_nsrst_delay: 100
    adapter_nsrst_assert_width: 100
    Info : BCM2835 GPIO JTAG/SWD bitbang driver
    Info : SWD only mode enabled (specify tck, tms, tdi and tdo gpios to add JTAG mode)
    Info : clock speed 100 kHz
    Info : SWD DPIDR 0x2ba01477
    Info : AT91SAM4E8C.cpu: hardware has 6 breakpoints, 4 watchpoints
    TargetName Type Endian TapName State
    — —————— ———- —— —————— ————
    0* AT91SAM4E8C.cpu cortex_m little AT91SAM4E8C.cpu running
    Error: timed out while waiting for target halted
    TARGET: AT91SAM4E8C.cpu – Not halted
    in procedure ‘reset’ called at file “openocd.cfg”, line 21
    in procedure ‘ocd_bouncer’
    Info : Halt timed out, wake up GDB.

    Do you have any idea what is wrong with my configuration?

    • ryan

      Judging from the second part of the log, it looks like a possible issue with the SRST line, whether it’s on the target or the Pi I’m not sure. How are you powering the Pi and the target device? I’ll investigate the configurations to ensure consistency on my end.

      EDIT: What is your reset configuration?

  2. tatery

    Hi again,

    When i was able to program MCU only once and all next tries failed I decided to use jtag mode but unfortunately I have more errors than for SDW.

    My openocd.cfg configuration:

    source [find rpi_jtag.cfg]
    transport select jtag
    set CHIPNAME AT91SAM4E8C
    source [find target/at91sam4XXX.cfg]
    set _FLASHNAME $_CHIPNAME.flash
    flash bank $_FLASHNAME at91sam4 0x00400000 0 1 1 $_TARGETNAME
    reset_config srst_only
    init
    targets
    reset halt

    console ouptut:

    sudo openocd
    Open On-Chip Debugger 0.10.0-dev-00371-g81631e4 (2016-08-29-12:46)
    Licensed under GNU GPL v2
    For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
    BCM2835 GPIO config: tck = 11, tms = 25, tdi = 10, tdo = 9
    BCM2835 GPIO config: srst = 18
    srst_only separate srst_gates_jtag srst_push_pull connect_deassert_srst
    adapter speed: 4000 kHz
    adapter_nsrst_delay: 100
    jtag_ntrst_delay: 100
    cortex_m reset_config sysresetreq
    srst_only separate srst_gates_jtag srst_push_pull connect_deassert_srst
    Info : BCM2835 GPIO JTAG/SWD bitbang driver
    Info : JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)
    Info : clock speed 4061 kHz
    Error: JTAG scan chain interrogation failed: all zeroes
    Error: Check JTAG interface, timings, target power, etc.
    Error: Trying to use configured scan chain anyway…
    Error: AT91SAM4E8C.cpu: IR capture error; saw 0x00 not 0x01
    Warn : Bypassing JTAG setup events due to errors
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Could not initialize the debug port
    TargetName Type Endian TapName State
    — —————— ———- —— —————— ————
    0* AT91SAM4E8C.cpu cortex_m little AT91SAM4E8C.cpu unknown
    Error: JTAG scan chain interrogation failed: all zeroes
    Error: Check JTAG interface, timings, target power, etc.
    Error: Trying to use configured scan chain anyway…
    Error: AT91SAM4E8C.cpu: IR capture error; saw 0x00 not 0x01
    Warn : Bypassing JTAG setup events due to errors
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Invalid ACK (0) in DAP response
    Error: Could not initialize the debug port

    • ryan

      Investigating this, but double check your pinout and connection. The scan chain is all 0s so my guess would be either a power issue or pinout issue. There’s some differences in the configurations for the original Pi and Pi2/3 for OpenOCD adapter configurations. I’ll keep you updated.

  3. tatery

    Finally both SWD and JTAG configurations work. They have started working after I added following lines to my openocd.cfg file:

    reset_config srst_only
    adapter_khz to 250

    Thanks once again ryan for great tutorial.

    • ryan

      Interesting, glad you were able to solve the problem. Sounds like either the clock was too high, or your wires are a little too long.

  4. Fernando

    Hi there

    Thanks for this tutorial.

    Knowing what JTAG is but only seeing someone else use commercial off-the-shelf dedicated JTAG programmers before, I wonder, what kind of electric protection there is on the RasPi GPIO pins? in other words what is the potential of misconnecting something and frying the raspi or the target device?

    Just curious / cautious :)…
    😉
    FC
    PS: I have a Raspi 2 model B and a PiZero, if it makes any difference

    • ryan

      As far as I know, there’s no protection on the pins. They should be 3.3v tolerant, but there’s no telling what will happen if something were to be connected incorrectly. However, I’ve done this process dozens of times, even with incorrect connections (for debugging purposes) on numerous devices and have had zero issues. Most non-commercial JTAG programmers are simply FTDI chips like the FT2232H. Always double check your pinouts, but again, I haven’t had any issues on my Pi1, 2 or 3.

  5. George

    Great tutorial Ryan! Thank you so much.

    I was just wondering which would be the next steps to program a FPGA via JTAG after following this tutorial?

    Thank you!
    George

Leave a Reply

Your email address will not be published. Required fields are marked *