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:
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade -y
Once we’re updated, we’ll need to install some dependencies for OpenOCD:
sudo apt-get install git autoconf libtool make pkg-config libusb-1.0-0 libusb-1.0-0-dev telnet
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:
git clone git://git.code.sf.net/p/openocd/code openocd
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:
cd openocd/
./bootstrap
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:
./configure --enable-maintainer-mode --enable-bcm2835gpio --enable-sysfsgpio
If all goes successfully, we’re finally ready to compile:
make -j4
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:
sudo make install
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:
#
# Config for using Raspberry Pi's expansion header
#
# This is best used with a fast enough buffer but also
# is suitable for direct connection if the target voltage
# matches RPi's 3.3V and the cable is short enough.
#
# Do not forget the GND connection, pin 6 of the expansion header.
#
interface bcm2835gpio
bcm2835gpio_peripheral_base 0x3F000000
# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET
# These depend on system clock, calibrated for stock 700MHz
# bcm2835gpio_speed SPEED_COEFF SPEED_OFFSET
bcm2835gpio_speed_coeffs 146203 36
# Each of the JTAG lines need a gpio number set: tck tms tdi tdo
# Header pin numbers: 23 22 19 21
bcm2835gpio_jtag_nums 11 25 10 9
# or if you have both connected,
# reset_config trst_and_srst srst_push_pull
# Each of the SWD lines need a gpio number set: swclk swdio
# Header pin numbers: 22 18
# bcm2835gpio_swd_nums 25 24
# If you define trst or srst, use appropriate reset_config
# Header pin numbers: TRST - 26, SRST - 12
# bcm2835gpio_trst_num 7
# reset_config trst_only
bcm2835gpio_srst_num 18
reset_config srst_only srst_push_pull
# or if you have both connected,
# reset_config trst_and_srst srst_push_pull
SWD:
#
# Config for using Raspberry Pi's expansion header
#
# This is best used with a fast enough buffer but also
# is suitable for direct connection if the target voltage
# matches RPi's 3.3V and the cable is short enough.
#
# Do not forget the GND connection, pin 6 of the expansion header.
#
interface bcm2835gpio
bcm2835gpio_peripheral_base 0x3F000000
# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET
# These depend on system clock, calibrated for stock 700MHz
# bcm2835gpio_speed SPEED_COEFF SPEED_OFFSET
bcm2835gpio_speed_coeffs 146203 36
# Each of the JTAG lines need a gpio number set: tck tms tdi tdo
# Header pin numbers: 23 22 19 21
# bcm2835gpio_jtag_nums 11 25 10 9
# or if you have both connected,
# reset_config trst_and_srst srst_push_pull
# Each of the SWD lines need a gpio number set: swclk swdio
# Header pin numbers: 22 18
bcm2835gpio_swd_nums 25 24
# If you define trst or srst, use appropriate reset_config
# Header pin numbers: TRST - 26, SRST - 12
# bcm2835gpio_trst_num 7
# reset_config trst_only
bcm2835gpio_srst_num 18
reset_config srst_only srst_push_pull
# or if you have both connected,
# reset_config trst_and_srst srst_push_pull
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:
sudo openocd -f rpi_jtag.cfg -f target/stm32l4x.cfg
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:
telnet localhost 4444
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.