view doc/Unbuffered-FT2232x-JTAG @ 176:fb2f6497ba53 default tip

doc/Linux-DTR-RTS-flaw: point to new location of this article
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 11 Dec 2023 19:37:20 +0000
parents 4f5abad5dd40
children
line wrap: on
line source

How to make a safe JTAG adapter out of a generic unbuffered FT2232x board
=========================================================================

[This guide was originally written in 2019; it has been updated slightly for
 the move of FTDI EEPROM utilities to fc-usbser-tools, plus other minor updates
 for the situation in 2023, but the principal ideas are all from 2019.]

Among the FOSS community of tinkerers who use OpenOCD to operate on the JTAG
interfaces of various hardware targets, one of the most common JTAG adapter
choices (if not the most common) is to use some adapter gadget based on an FTDI
chip, most commonly one of FT2232x variants.  However, a major distinction needs
to be drawn between specialized purpose-made JTAG adapter products which just
happen to use an FT2232x chip internally, versus generic FT2232x breakout boards
which the user wires up for JTAG on his or her own.

In an ideal world, using a purpose-made buffered JTAG adapter (one that has a
buffer inserted between FT2232x I/O pins and the target connection interface)
would be strongly preferable for a whole host of reasons.  However, to this
author's disappointment, there are very few community vendors who make such
adapters, and when I last looked in 2019, I was NOT able to find any high-
quality buffered JTAG adapter which could be readily bought and which comes
with published schematics.

Given the sorry state of availability of buffered JTAG adapters, I gave more
thought to the unbuffered option, and I found what appears to be a way to make
them safe - but my method requires programming the EEPROM on the FT2232x board
with a special custom configuration, and in this article I am going to provide
the full details and instructions.

To begin with, an unbuffered JTAG adapter (one in which the target JTAG signals
are connected directly to FT2232x I/O pins without any buffer in between) can
work only with targets that operate their JTAG interface at 3.3 V, or perhaps
a slightly lower but still fully 3.3V-compatible logic voltage level like the
2.8 V I/O on Calypso GSM baseband processors.  An unbuffered adapter CANNOT
work with, say, a 1.8 V JTAG interface - but as long as your target runs at
3.3 or 2.8 V, then we can continue.

The next big problem with unbuffered FT2232x adapters is that if you don't put
a special configuration in the EEPROM (or if your FT2232x board omits the EEPROM
altogether), the channel which you are going to wire up for JTAG (can only be
Channel A on FT2232C/D, can be either channel on FT2232H) is going to come up
in FTDI's default UART mode on power-up, and it is going to stay in that mode
until and unless you run OpenOCD, which will then switch it into MPSSE mode for
JTAG.  Why is it a problem?  Answer: you need to connect the TDO line from the
target to the FT2232x chip's ADBUS2 pin for JTAG to work via MPSSE, but in the
power-up default UART mode this ADBUS2 pin is the RTS output.  FT2232x RTS
output fighting with the target's TDO output - not good, and it could even fry
one or both of the chips.

Unfortunately FTDI's stupid chip design does not allow the desired MPSSE mode
to be configured in the EEPROM so that it is there right from power-up.  But
there is a workaround: if the EEPROM config is set up to put Channel A (the one
that will be wired for JTAG) into the rarely-used 245 FIFO mode instead of UART,
all 8 ADBUS pins (including ADBUS2 where TDO will be connected) will power up
as inputs with weak internal pull-ups (as long as the ACBUS2 control line is
left unconnected), which is much safer than what these pins do in the default
UART mode.

And if we need to program the EEPROM with a special custom config to change
Channel A from 232 UART to 245 FIFO, we can also assign a different USB VID:PID
at the same time.  FTDI's default FT2232x ID of 0403:6010 works great when both
channels of the FT2232x device are used as UARTs - the Linux kernel recognizes
this USB ID, creates a pair of ttyUSB devices (one for each channel), and
everything Just Works.  But what if Channel A is used for JTAG and is therefore
not a valid UART channel?  If the default USB ID is left unchanged, what happens
is that a pair of ttyUSB devices still gets created, with the first out of the
pair being completely bogus and non-functional.  And when you run OpenOCD, that
bogus Channel A ttyUSB device disappears, while the Channel B ttyUSB device
(which will actually work if Channel B is wired as a UART) remains, creating a
gap in ttyUSB numbers.  If you have a ton of ttyUSB devices on your system and
are struggling to keep track of which is which, this behaviour certainly does
not help.

As it happens, our company Falconia Partners LLC has received a block of 8 PIDs
from FTDI, allocated out of FTDI's VID range - these PIDs have been officially
allocated by FTDI to our company for use in products based on FTDI chips.  And
because we can spare one PID for a worthy cause, one of these PIDs (0403:7151)
is hereby being donated to the community for use on generic FT2232x boards in
the unbuffered JTAG adapter configuration.

Support for this 0403:7151 USB ID has been added to Linux ftdi_sio driver in
2020-09 with this commit:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6cf87e5edd9944e1d3b6efd966ea401effc304ee

This commit is included in mainline kernel versions from 5.10 onward, and in
stable kernel versions 4.4.240, 4.9.240, 4.14.202, 4.19.152, 5.4.72, 5.8.16 and
5.9.1.  If your Linux kernel version (or rather ftdi_sio driver version if it's
a module) includes this commit, the ftdi_sio driver will create a ttyUSB device
for Channel B, allowing that channel to function as a UART if desired, but
Channel A will be left alone by the kernel driver, reserved for userspace
applications like OpenOCD.  If your kernel/driver version (older than late 2020)
does not include the commit in question, both FT2232x channels will be left
alone by the kernel driver, i.e., no ttyUSB devices will be created.  If you are
interested only in JTAG and don't need an extra UART on Channel B, it should not
matter whether your ftdi_sio driver knows about the new custom USB ID or not -
you simply configure your OpenOCD in userspace to find your unbuffered and
ad-hoc-wired JTAG adapter at USB ID 0403:7151.  If you do need the UART on
Channel B but your Linux kernel version is old, you will need to manually apply
the trivial patch from the commit linked above.

Choice of FT2232x breakout board
================================

In earlier years we made heavy use of generic FT2232D breakout boards made by
PLDkit OU in Estonia.  We are not sure if that original company still makes
them or not, but the person behind that company name did eventually sell us
their Gerber files, and we have published them here:

ftp://ftp.freecalypso.org/pub/USB/FTDI/
or
https://www.freecalypso.org/pub/USB/FTDI/

Given that we have a stash of FT2232D chips and given that we still have use
cases for these generic breakout boards, we have a tentative plan to produce
our own Falconia-branded version of the same adapter/breakout board.

These days FT2232H chips and FT2232H breakout boards are much more popular, but
I generally prefer FT2232C/D for classicness and simplicity.  Additionally,
FTDI's AN_184 document lists I/O pin behaviour of various FTDI chips including
FT2232D and FT2232H; according to this document FT2232H I/O pins go through a
brief phase of acting as UART signals (including RTS output on ADBUS2) while
the EEPROM is being read, whereas FT2232D I/O pins are tristated during this
time.  Thus I strongly recommend using an FT2232D breakout board.

Programming the EEPROM
======================

The boards we used to buy from PLDkit (FT2232D breakout) have 93C46 EEPROMs on
them, and the boards were shipped with blank EEPROMs.  The blank EEPROM state
is perfectly good for operating the board as a dual UART, but our JTAG
application calls for custom EEPROM programming.  A number of people in the
FOSS community have produced several different tools for programming FTDI
EEPROMs, and you could even use FTDI's official Winblows tools if you like, but
I am going to describe how to program the EEPROM using the tools which I
developed and which are used in production here at FreeCalypso HQ.

FreeCalypso FTDI EEPROM tools reside in this Hg repository:

https://www.freecalypso.org/hg/fc-usbser-tools/

You will need to compile and install these tools, then continue.

If you have the FT2232D board in its initial blank-EEPROM state plugged into
your system and you don't have any other FT2232x devices with 0403:6010 IDs,
you can program the EEPROM for JTAG as follows (the command is a shell
pipeline):

ftee-gen2232c jtag-unbuf | fteeprom-prog i:0x0403:0x6010

Then unplug and replug the FT2232D board, and it should come back with the new
0403:7151 USB ID.  If you wish to bring it back to its original blank-EEPROM
state, you can do so by erasing the EEPROM:

fteeprom-erase i:0x0403:0x7151

Wire connections
================

The JTAG signal connections to ADBUS0 through ADBUS3 are fixed by FTDI, and if
you go against my advice and use FT2232H rather than FT2232C/D, then ADBUS7 is
also reserved for RTCK.  The I/O pins available for reset and other sideband or
GPIO signals are ADBUS4 through ADBUS7 on FT2232C/D adapters, or ADBUS4 through
ADBUS6 and ACBUS5 through ACBUS7 on FT2232H.  The other pins should be left
untouched to avoid problems with the 245 FIFO mode which is active in the time
window between power-up (USB plug-in) and running OpenOCD.