wrtag, JTAG from a D-Link DSL502T router


An ethernet JTAG pod made from an old D-Link ADSL router, used to program Xilinx FPGAs in lieu of a parallel port.

This is not a very polished bunch of hacks, and this page isn't intended to be a full step-by-step guide to making your own (since it's possible to do this by repurposing a hundred different pieces of hardware), but I hope it's of interest. Questions & comments are welcome! :)


My first FPGA board was a Digilent Spartan 2E board, which came with a parallel port JTAG programming cable. Webpack ISE drives this (it's a clone of the Xilinx Parallel III cable IIRC), and many happy days were had sending bitstreams down from my old PC.

Times changed, and now it so happens that I don't actually own anything with a parallel port on it anymore. I use Webpack from a VMWare Fusion VM on a Macintosh, and how do I program my Xilinx boards?

Answer: I spend something like $50 on a USB JTAG cable, of course!

That's the easy way, it seems. But I also have a Spartan 3E Starter Kit, which has on-board USB JTAG. I had a mare of a time trying to get this to work as passthrough-USB with Fusion, and gave up. (It's Cypress EZ-USB-based, and perhaps their clever 'renumeration' thing confuses VMWare?). I didn't want to buy another USB JTAG device that would be ropey when used with a VM.

Besides, that's no fun.

Xilinx's JTAG programmer tool, IMPACT, supports local parallel and USB JTAG cables but will also talk to a remote JTAG cable using Xilinx's 'cableserver'. If you're on Windows, run:

This will listen on TCP/2000 and a remote instance of IMPACT will be able to use the local JTAG cable.

However, Zoltan Csizmadia has reverse-engineered the protocol used and has written the excellent cblsrv (part of the Xilprg Sourceforge project) which emulates this binary, but on Linux.

So, my idea: Rather than have another PC lying about just for its parallel port, build a dedicated embedded Linux system using cblsrv to perform the same duties — making a network-accessible JTAG programming device.


I scrounged an unwanted/"broken" D-Link DSL502-T router. This is an excellent 'tinkering' embedded Linux machine. With 16MB RAM, 4MB flash and a 150MHz MIPS32 CPU, it runs Linux nicely. I used the excellent OpenWRT to build a filesystem and kernel for it. It's a TI AR7 device, which has a few rough edges.

Finding GPIOs

I knew the DSL502T has a few GPIOs to drive the front panel LEDs and planned to use some of these to drive the JTAG signals. There are 32 GPIOs on the chip though — could there be more on-board? I poked about with a resistor pulled to Vcc or Gnd whilst reading the input states, and found 12 spares. This is a pretty damn useful number; I stopped there, but there may be more. Click the image to the right for a map.

Here's a simple program to dump the input bit values: readAR7gpios.c. Compile it with the OpenWRT SDK crosscompiler.

Hardware mods

Six GPIOs are used to 'bit bang' the JTAG signals. These are connected to a 74HC04 CMOS Hex Inverter, which is used as a cheap buffer for the signals. It also adds a degree of protection to the signals brought out to the outside world; a dropped screwdriver or other random short may blow the 74HC04 (which is easily replaced) but hopefully not any GPIO lines. Obviously this means that the sense of each line is inverted, which software must deal with.

In addition to the four standard JTAG pins, I've wired an open collector /RESET output and a misc input; whilst not so useful for programming Xilinx FPGAs these may be useful for future general-JTAG uses.



It's a quick hack on veroboard rather than a neat PCB, but here's the schematic. Three outputs (TMS, TCK, TDI) run to the 74HC04 for buffering. The /RESET output drives a pull-down NPN transistor directly. Note that the 74HC04 is driven from an external Vcc taken from the target circuit; this allows the output voltage to be controlled by the target. Supplying 5V, for example, will let TMS, TCK, etc. have a full 0-5V swing; however inputs to the wrtag will result in the 74HC04 outputs driving the GPIOs at greater than 3.3V. To protect against this, two zener diodes clamp the Misc and TDO inputs to 3.3V. Also, they have a 50ohm resistor in series in case the input GPIOs are configured as outputs before cblsrv can reconfigure them as inputs. Grotty, but it works; it's not hugely useful and I end up driving the buffer Vcc from the box's 3.3V out anyway — everything I need to JTAG is currently 3.3V and any lower will require buffering a little more exotic than a 74HC04. Consider using one of TI's dual-supply level conversion buffers instead.

OpenWRT changes

OpenWRT delivers a great (and small) Linux distribution for this board. Some tweaking and config was required for my AR7-based router:
  • The serial console didn't work, and that's very useful for debug. See https://dev.openwrt.org/ticket/7095 — the latest trunk should be fine.
  • There are two ethernet ports on this version of the AR7. My kernel initialised them in the wrong order, making eth0 the non-functional PHY-less one, which upset OpenWRT a little. There's a hacky patch below to just disable probing one, so eth0 works.
  • I needed to configure the network, of course, so added this to /etc/config/network:
    config 'interface' 'lan'
    	option 'ifname' 'eth0'
    	option 'proto' 'dhcp'
    I removed the 'bridge' and 'nat' options as this is no longer a router. ;)
  • Installed dropbear for maintenance and debugging. I didn't end up using gdbserver but that can be useful to debug things in-situ.

Driving the GPIOs with cblsrv

Here's my Patch cblsrv-0.1-ar7-bitbang.patch, against cblsrv-0.1-src.tar.bz2.

Changes to cblsrv-0.1:

  • Makefile has CONFIG_BITBANG and CONFIG_PARPORT to select which 'backends' to build in.
  • Makefile respects CROSS_COMPILE path for use with OpenWRT toolchain.
  • The code to bit-bang the parallel port has been split into a separate class, which drives pins via a new GpioCtrl class. An implementation of this (in GpioCtrl_ar7.cpp) drives the AR7 GPIOs on the pins listed above.
Note that GpioCtrl_ar7.cpp is specific to my wiring — but the pins are #defined so are easy to change. Note also that their sense is inverted because I'm using an inverter to buffer them!

GPIOs and atomicity

The AR7 GPIOs require a read-modify-write to change a pin state, as there're no SETPIN/CLEARPIN-type registers. There are also two users of the pins: the kernel with its blinkyLEDs (status, network) infrastructure and cblsrv in userland. It sounds like locks are required, but none are, really. This is because:
  • The kernel's read-[AND,OR]-write in gpio_set_value() looks atomic w.r.t. userland in that the kernel will never return having read but not written the pin state. Any state we're about to write does not get corrupted by being preempted and the kernel flashing an LED.
  • We are the only userspace process using the GPIOs.
  • We are single-CPU.
  • Userland can corrupt kernel pin state thus:
      UserA = pin state; UserA |= 1; [pre-empted] KernelB = pin state; KernelB |= 4; pin state = Kernel B; [return to user] pin state = UserA;
    But, the kernel pin state is just flashing LEDs, which is 'lossy'. It doesn't matter if the Ethernet LED blinks 1ms too little, as long as the user application isn't affected.
If another GPIO function is added (such as driving an LCD) this will have to either be driven from the cblsrv binary or careful locking will have to occur between the two GPIO-using tasks.

Building and installing

Using the OpenWRT SDK-built compilers, I crosscompile cblsrv on x86 Linux and just scp the binary to the router.
make CROSS_COMPILE=/path/to/bin/mipsel-openwrt-linux-
Then, I added a dumbass simple startup script to launch cblsrv when the device boots:


#!/bin/sh /etc/rc.common
# (C) 2008 openwrt.org


start() {
	/root/cblsrv >/tmp/cblsrv.log 2>/tmp/cblsrv2.log &
This also needs a link in /etc/rc.d/S99cblsrv to /etc/init.d/cblsrv.

How well does it work?

Great! It's also faster than I expected for bit-bangy code; even though I've made no attempt to optimise the bitbang cblsrv code it downloads bitstreams really quick — about 130KBytes/sec. It's reliable, too, and a nice piece of bench equipment to have around.

Future work

  • LCD: There are enough spare GPIOs to drive an HD44780-style character LCD. This would be useful to display the MAC address and IP address obtained from DHCP. It's more glitter than necessary though, as I just give out a static IP address for the wrtag's MAC address.
  • OpenOCD: A cable driver backend for OpenOCD that uses the cblsrv protocol over TCP should allow the wrtag to be useful as a general-purpose JTAG debugger/ICE for ARM, etc.


1st May 2010, © Matt Evans (matt at axio dot ms)