Why POM-TAG exists

There was no shortage of JTAG programmers when I started POM-TAG. Buying one would have been the sensible option. It would also have meant treating another part of the FPGA toolchain as a finished black box.

I wanted to understand what actually happens between Vivado Hardware Manager and the FPGA: how an FTDI device is identified by Vivado, how its EEPROM is configured, how the MPSSE generates the JTAG transactions, and how those signals are presented safely to a target that may not use 3.3 V logic. POM-TAG became a small project built around that interface rather than around the FPGA itself.

The hardware therefore goes further than a basic FT4232H breakout. The FT4232H handles USB and generates the JTAG signals, but the target side is level-shifted through an SN74AXC8T245 level translator. VTREF is taken from the FPGA board and used to set the target-side logic level, allowing the same programmer to work with JTAG banks operating at 1.5 V, 1.8 V, 2.5 V or 3.3 V. An STM32 measures that reference voltage and keeps the translator disabled until the target voltage is valid. If the target is disconnected or powered down, the JTAG outputs return to a high-impedance state.

I also wanted the programmer to expose more information than a single power LED. The STM32 controls an RGB LED that reports VTREF validity, JTAG activity, device detection, programming progress and the final result. Vivado-related events are sent to the MCU through one of the spare FT4232H UART channels, while the JTAG timing itself remains entirely inside the FTDI hardware.

The result is not an attempt to replace commercial programming cables. POM-TAG is a programmer built to make the interface understandable, measurable and reusable as part of the wider Pomsera FPGA work. And yes, building a programmer because programmers already exist is slightly backwards. That was also part of the appeal.

Xilinx 14-pin JTAG header

The interactive diagram below explains the standard Xilinx 14-pin JTAG header, showing the function of each pin and the placement of TCK, TMS, TDI, TDO, VTREF, ground and the optional reset signals.

POM-TAG Hardware Architecture

Architecture
Main data path and supervision path
FunctionImplementation
Host connectionUSB-C
USB/JTAG controllerFTDI FT4232H
JTAG engineChannel A in MPSSE mode
Configuration memoryMicrochip AT93C56B, 2-Kbit Microwire EEPROM
SupervisorSTM32G031K8U
Level translatorTI SN74AXC8T245RHLR
Supported target reference1.5 V to 3.6 V
Target signalsTCK, TMS, TDI, TDO and VTREF
Target resetSRST not supported
Target connector14-pin, 2 x 7 Xilinx-style JTAG header
Target powerNot supplied by POM-TAG
StatusCommon-anode RGB LED driven by the STM32

USB-C input and the 3.3 V supply

The USB-C connector provides both the data connection and the 5 V bus supply. POM-TAG is a USB device, so CC1 and CC2 each use a 5.1 kΩ pull-down resistor. The duplicated USB 2.0 D+ pins are tied together, as are the duplicated D- pins, which preserves the data pair regardless of plug orientation.

D+ and D- pass through a low-capacitance ESD protection device before reaching the FT4232H. The FTDI-side USB inputs include the 27 Ω series resistors shown in the schematic. These parts belong close to the USB signal path because their purpose is electrical, not logical: the ESD array limits transient stress at the connector, while the series resistors control the edge presented to the FTDI pins.

The connector shell is not shorted directly to logic ground in the schematic. A 1 nF capacitor and a 1 MΩ resistor connect the shield to ground in parallel. The capacitor provides a high-frequency return path while the resistor prevents the shield from remaining indefinitely floating at DC.

USB-C Connector + ESD

VBUS is protected before it is converted to the board's 3.3 V rail. The input network contains the VBUS transient suppressor, the series protection element and local input capacitance. U5 is the fixed 3.3 V regulator for the programmer electronics. Its output feeds the STM32, EEPROM, FT4232H I/O domains and the A side of the JTAG translator.

3.3V LDO + FUSE + Overvoltage Protection
3.3V LDO + FUSE + Overvoltage Protection

FT4232H power domains

The FT4232H itself uses both 3.3 V and 1.8 V. VREGIN is connected to 3.3 V, and the device's internal regulator produces the 1.8 V VREGOUT rail used by the FTDI core. The 4.7 µF capacitor on VREGOUT is part of that regulator implementation. VPHY, VPLL and the selected VCCIO pins remain on the 3.3 V rail in this design. Keeping these domains explicit in the schematic is useful during bring-up: a board can have a valid 3.3 V supply and still fail if the FTDI core rail is absent.

FT4232H regulator connections. VREGIN is supplied from 3.3 V and VREGOUT provides the 1.8 V core rail.
FT4232H regulator connections. VREGIN is supplied from 3.3 V and VREGOUT provides the 1.8 V core rail.

FT4232H: USB interface and JTAG engine

Channel A of the FT4232H is dedicated to JTAG. The important connection is not simply that four GPIO pins reach the header; the device uses its Multi-Protocol Synchronous Serial Engine, or MPSSE, to generate the clocked JTAG transaction in hardware. Vivado sends command blocks over USB and the MPSSE executes the corresponding TCK, TMS and data sequence locally. The spacing of individual JTAG edges is therefore not determined by USB packet latency.

Function Implementation
ADBUS0TCK - clock generated by the FT4232H
ADBUS1TDI - data from POM-TAG to the target
ADBUS2TDO - data returned by the target
ADBUS3TMS - TAP state control

The 12 MHz crystal is connected directly to OSCI and OSCO. Its two load capacitors return to ground. On the PCB, the crystal and its load network should remain close to the FT4232H, with short traces and a clean return path. The same placement rule applies to the local decoupling capacitors around VPLL, VPHY, VCORE and VCCIO.

FT4232H section, including Channel A JTAG signals, Channel B UART signals, EEPROM interface and 12 MHz reference clock.
FT4232H section, including Channel A JTAG signals, Channel B UART signals, EEPROM interface and 12 MHz reference clock.

EEPROM configuration and the current program_ftdi workflow

The AT93C56B stores the non-volatile FTDI configuration read during USB enumeration. This step is required for Vivado Hardware Manager to recognise the FT4232H as a supported USB-to-JTAG cable. AMD documents the procedure in UG908 and provides the program_ftdi utility with the Vivado installation. For a POM-TAG development unit, the EEPROM can be written with the following command:

program_ftdi.tcl
1program_ftdi -write -ftdi FT4232H -serial POM00001 -vendor "Pomsera" -board "POM TAG" -desc "USB-JTAG programmer"

The programmed contents can then be read back:

program_ftdi.tcl
1program_ftdi -read

After the EEPROM has been written, disconnect and reconnect USB so that the FT4232H enumerates again with the new contents. AMD's utility assigns Channel A to USB-JTAG. On an FT4232H, the remaining channels are left in their default UART mode unless they are reconfigured separately. The Manufacturer field is a special case. AMD states that program_ftdi sets it to Xilinx and that changing this field prevents Vivado from recognising the device as a USB-to-JTAG cable. POM-TAG identification therefore belongs in the Vendor, Board, Description and Serial fields, while the Manufacturer field is left as required by AMD.

Translating JTAG to the target voltage

The FT4232H side of the interface operates at 3.3 V, but the target JTAG bank may not. POM-TAG uses an SN74AXC8T245 so the programmer can keep its own 3.3 V domain while adopting the voltage supplied by the target on VTREF. VCCA is tied to 3.3 V. Both VCCB pins are tied to VREF_LVL, the target-derived reference voltage.

The translator is arranged so that the JTAG directions are fixed in hardware. TCK, TDI and TMS occupy channels 1 to 3 and travel from the A bus to the B bus. TDO occupies channel 5 and travels from the B bus back to the A bus. DIR1 and DIR2 are both tied high. According to the SN74AXC8T245 function table, that combination produces A-to-B transfer on bits 1 to 4 and B-to-A transfer on bits 5 to 8 when OE is low. No firmware is required to reverse the data direction during a scan.

FunctionImplementation
TCKA1 → B1, programmer to target
TDIA2 → B2, programmer to target
TMSA3 → B3, programmer to target
TDOB5 → A5, target to programmer
VCCA3.3 V from POM-TAG
VCCBVREF_LVL from the target
DIR1 / DIR 2Both high: bits 1-4 A → B, bits 5-8 B → A
OEActive low, controlled by the STM32
SN74AXC8T245 wiring. The two direction groups allow outgoing JTAG signals and returning TDO to share one translator.
SN74AXC8T245 wiring. The two direction groups allow outgoing JTAG signals and returning TDO to share one translator.

Texas Instruments specifies both voltage domains of the SN74AXC8T245 for 0.65 V to 3.6 V operation. POM-TAG deliberately specifies a narrower 1.5 V to 3.6 V target range because that is the range validated for the intended FPGA use. A 5 V target reference is not supported and must not be connected to VREF_LVL.

VTREF supervision and safe output enable

VTREF performs two jobs. Electrically, it powers VCCB of the SN74AXC8T245 and therefore defines the logic-high level presented to the target. Independently, the same voltage is measured by the STM32 through a 100 kΩ / 100 kΩ divider connected to PA0. The target is not powered from VTREF; the pin is only a reference and a light load for the translator and ADC divider.

With equal resistor values, the ADC sees half of the target reference voltage: VADC = VTREF / 2. A 1.8 V target therefore produces about 0.90 V at PA0, while a 3.3 V target produces about 1.65 V. The divider's Thevenin resistance is approximately 50 kΩ, so the ADC must be configured with enough sampling time for its internal sample-and-hold capacitor to settle. Averaging in firmware prevent noise around the acceptance threshold from repeatedly enabling and disabling the interface.

FunctionImplementation
1.5V VTREF 0.75V at PA0
1.8V VTREF 0.90V at PA0
2.5V VTREF1.25V at PA0
3.3V VTREF1.65V at PA0
3.6V VTREF1.80V at PA0

The translator's OE input is active low. MCU_OE_CTRL is pulled up to 3.3 V, which leaves the translator disabled while the STM32 is in reset or its GPIO is still unconfigured. Only after firmware has measured a stable VTREF inside the supported range does the MCU drive OE low. If VTREF disappears, the interface is disabled again. This arrangement prevents the FT4232H from actively driving TCK, TMS or TDI into an unpowered target. It also makes the safety decision independent of the Vivado status script. UART messages can change the LED state, but they do not authorise the JTAG outputs. The translator's own VCC isolation and partial power-down behaviour provide an additional device-level safeguard when one voltage domain is absent.

Target connector and the SRST limitation

CN1 is a 2 x 7 Xilinx-style JTAG header. The implemented signal pins are VREF_LVL, TMS_LVL, TCK_LVL, TDO_LVL and TDI_LVL. The odd-numbered positions in the schematic are used as ground returns, while pins 12 and 14 are left unconnected. POM-TAG does not supply a target power rail through this connector; the FPGA board must already be powered and must provide VTREF.

Target connector. POM-TAG carries four-wire JTAG and VTREF; the remaining signal positions are not used.
Target connector. POM-TAG carries four-wire JTAG and VTREF; the remaining signal positions are not used.

The number of ground contacts is intentional. JTAG is a digital protocol, but the cable still carries fast edges. A low nominal TCK frequency does not imply low-frequency signal behaviour. Short return paths beside the active signals reduce loop area and help preserve the TCK waveform at the target connector.

STM32 supervision, UART status and the RGB LED

The STM32G031K8U is a supervisor, not a second JTAG controller. Its direct hardware responsibilities are limited to measuring VTREF, controlling MCU_OE_CTRL and driving the status LED. The FTDI SUSPEND signal is also routed to the MCU, which gives the firmware visibility of the USB-side state if required. SWDIO, SWCLK and NRST are broken out on the local service header so the supervisor firmware can be programmed independently.

A spare FT4232H channel provides the status link between the host and the supervisor. On Channel B, BDBUS0 is the FTDI transmit signal and is connected to the MCU's USART1_RX net; BDBUS1 is the FTDI receive signal and is connected to USART1_TX. A companion Vivado Tcl script can therefore send short status messages to the STM32 without interfering with Channel A, which remains dedicated to MPSSE JTAG.

STM32 supervision. PA0 senses divided VTREF; MCU_OE_CTRL drives the translator enable; PB6/PB7 provide the status UART.
STM32 supervision. PA0 senses divided VTREF, MCU_OE_CTRL drives the translator enable; PB6/PB7 provide the status UART.

The RGB LED is common-anode and tied to 3.3 V. The STM32 sinks current through separate resistors for the red, green and blue dies. The resistor values differ because the three LED dies have different forward voltages and luminous efficiencies. The LED is therefore a hardware output driven by firmware, not an indication produced by the FTDI device itself.

Common-anode RGB LED. The three channels use independent current-limiting resistors.
Common-anode RGB LED. The three channels use independent current-limiting resistors.

PCB implementation

The POM-TAG PCB brings the complete programmer into a single compact board. The USB-C interface and input protection form the host-side connection, while the FT4232H sits at the centre of the design as the main USB-to-JTAG controller. Its configuration EEPROM and 12 MHz crystal are placed around the FTDI device as part of the same functional block. The SN74AXC8T245 separates the FTDI’s 3.3 V logic domain from the target JTAG voltage and connects directly to the 2 × 7 programming header. The STM32G031 is kept outside the JTAG timing path and handles VTREF supervision, translator enable control and the RGB status LED. Power regulation and decoupling complete the board, providing the 3.3 V rail used by the digital logic and the 1.8 V core supply required by the FT4232H. The result is a PCB where USB communication, JTAG generation, voltage translation and supervision remain separate functions, but operate together as a single programmer.

POM-TAG Handsoldered Prototype - Front
POM-TAG Handsoldered Prototype - Front
POM-TAG Handsoldered Prototype - Back
POM-TAG Handsoldered Prototype - Back
Bare PCB - Front
Bare PCB - Front
Bare PCB - Back
Bare PCB - Back

Documentation

Schematic

POM-TAG Revision A - Full Schematic
POM-TAG Revision A - Full Schematic

Printed Circuit Board

Best viewed on a computer

The Gerber viewer is interactive and only displays on a larger screen.

Pomsera

Inspect this board's Gerber layers in 2D and 3D. Loads on demand to keep the page fast.

3D Case

Best viewed on a computer

The STL viewer is interactive and only displays on a larger screen.

Best viewed on a computer

The STL viewer is interactive and only displays on a larger screen.

Digilent JTAG Adapter for Single-Row Headers

Since some of the boards I use expose Digilent’s single-row JTAG footprint rather than the standard Xilinx 2 × 7 header, I made a small adapter for POM-TAG as well. It remaps TCK, TMS, TDI, TDO, VTREF and ground between the two connector formats, so the same programmer can be used without modifying the target board or the POM-TAG hardware.

Best viewed on a computer

The Gerber viewer is interactive and only displays on a larger screen.

Pomsera

Inspect this board's Gerber layers in 2D and 3D. Loads on demand to keep the page fast.

Test With Arty S7-50

Video available in the online article.

POM-TAG programming a Digilent Arty S7-50 over JTAG

Firmware

STM32 supervision

The FT4232H handles the JTAG timing, while the STM32 supervises the target voltage, controls the level translator and drives the status LED. The relevant pin definitions are:

stm32.ino
1#define VTREF_SENSE   PA02#define JTAG_OE       PA834#define FT_RX         PB75#define FT_TX         PB6

Measuring VTREF

The firmware averages 64 ADC samples and compensates for the 2:1 divider:

stm32.ino
1float readVTREF()2{3    uint32_t total = 0;45    for (int i = 0; i < 64; i++)6        total += analogRead(VTREF_SENSE);78    float raw = total / 64.0f;910    float pinVoltage =11        (raw / 4095.0f) * 3.3f;1213    return pinVoltage * 2.0f;14}

This gives the STM32 an estimate of the actual voltage present on the target VTREF pin.

Enabling the JTAG interface

The translator always starts disabled:

stm32.ino
1pinMode(JTAG_OE, OUTPUT);2digitalWrite(JTAG_OE, HIGH);

OE is active low, so HIGH places the JTAG translator outputs in high impedance.

When VTREF becomes valid, it must remain stable for 600 ms before JTAG is enabled:

stm32.ino
1if (!targetValid && vtrefCandidate)2{3    if (millis() - vtrefCandidateSince >= 600)4    {5        targetValid = true;6        digitalWrite(JTAG_OE, LOW);7    }8}

If VTREF becomes invalid, the translator is disabled immediately:

stm32.ino
1if (!measurementValid)2{3    digitalWrite(JTAG_OE, HIGH);4}

The delayed enable prevents a transient voltage from activating the interface, while loss of target power disconnects the JTAG outputs without waiting for the LED state to change.

Vivado status over UART

A spare FT4232H channel connects Vivado to the STM32 at 115200 baud:

stm32.ino
1FTSerial.begin(115200);

The protocol is deliberately small:

snippet.txt
1H  heartbeat2I  idle3C  connecting4B  FPGA detected / JTAG ready5S  scan6P  programming7V  verify8D  success9E  error

The companion Tcl script sends these characters as Vivado moves through the programming sequence. The MCU converts them into LED states.

For example:

stm32.ino
1if (c == 'P')2    requestVivadoState(VIVADO_PROGRAM);34if (c == 'D')5    resultEvent = RESULT_SUCCESS;67if (c == 'E')8    resultEvent = RESULT_ERROR;

The UART is not part of the safety path. Losing the Vivado connection only clears the displayed software state; VTREF supervision continues independently.

LED states

The RGB LED gives a quick indication of both hardware and Vivado state:

StateLED
Invalid VTREFSlow orange pulse
Valid VTREFYellow
ConnectingBlue/cyan pulse
FPGA detectedDouble cyan flash
JTAG readyCyan breathing
ScanFast blue
ProgrammingViolet strobe
VerifyAmber
SuccessGreen
ErrorRed

Download

Software

Installing the Vivado integration

POM-TAG uses two Tcl files. The first is the actual integration script, pomsera_vivado.tcl. The second is a very small Vivado startup file whose only job is to load it automatically.

With the files provided here, the expected Windows layout is:

snippet.txt
1C:\2└── Pomsera\3    └── pomsera_vivado.tcl45%APPDATA%\6└── Xilinx\7    └── Vivado\8        └── Vivado_init.tcl

AMD Vivado automatically looks for Vivado_init.tcl in the user's Vivado configuration directory when it starts.

snippet.txt
1%APPDATA%\Xilinx\Vivado\2025.2\Vivado_init.tcl

That path is already present in the supplied Vivado_init.tcl, so placing the main script in C:\Pomsera\pomsera_vivado.tcl requires no modification.

The sequence at startup is therefore:

snippet.txt
1Vivado starts2      │3      ▼4Vivado_init.tcl5      │6      ▼7C:\Pomsera\pomsera_vivado.tcl8      │9      ├── detect POM-TAG COM ports10      ├── open the UART connection11      ├── watch Vivado journal/log events12      └── monitor the connected FPGA

The main script initializes itself after being loaded, opens the serial interface when available, attaches to the current Vivado session files and starts its background watchers.

If the script is edited while Vivado is already open, it does not need to be restarted. The integration provides:

pomsera_vivado.tcl
1::pomsera::reload

which cleanly stops the existing instance and reloads the same file.

Download

BOM

Key non-passive components:

QtyPartWhere to buy
1FT4232HL-REELFT4232HL-REELUSB Bridge, USB to UART USB 2.0 UART Interface 64-LQFP (10x10)DigiKeyBuy MouserBuy LCSCBuy
1AT93C56B-MAHM-EAT93C56B-MAHM-EEEPROM Memory IC 2Kbit 3-Wire Serial 2 MHz 8-UDFN (2x3)DigiKeyBuy MouserBuy LCSCBuy
1STM32G031K8U6STM32G031K8U6ARM® Cortex®-M0+ STM32G0 Microcontroller IC 32-Bit 64MHz 64KB (64K x 8) FLASH 32-UFQFPN (5x5)DigiKeyBuy MouserBuy LCSCBuy
1TPD4E02B04DQATPD4E02B04DQA8.8V (Typ) Clamp 5A (8/20µs) Ipp Tvs Diode Surface Mount 10-USON (2.5x1)DigiKeyBuy MouserBuy LCSCBuy
1TPS7A2133PQWDRBRQ1TPS7A2133PQWDRBRQ1Linear Voltage Regulator IC Positive Fixed 1 Output 500mA 8-SON (3x3)DigiKeyBuy MouserBuy LCSCBuy
1SN74AXC8T245RHLRSN74AXC8T245RHLRTranslation Transceiver 1 Element 8 Bit per Element Tri-State, Non-Inverted Output 24-VQFN (5.5x3.5)DigiKeyBuy MouserBuy LCSCBuy
1APF3236SEEZGKQBKCAPF3236SEEZGKQBKCMulti-Colour LEDs 3.2x3.6MM RGB SM RGB SMDDigiKeyBuy MouserBuy LCSCBuy
1ABM3B-12.000MHZ-B2-TABM3B-12.000MHZ-B2-T12 MHz ±20ppm Crystal 18pF 70 Ohms 4-SMD, No LeadDigiKeyBuy MouserBuy LCSCBuy
108783214200878321420Connector Header Surface Mount 14 position 0.079" (2.00mm)DigiKeyBuy MouserBuy LCSCBuy
1TSD05DYFRTSD05DYFR9V (Typ) Clamp 60A (8/20µs) Ipp Tvs Diode Surface Mount SOD-323DigiKeyBuy MouserBuy LCSCBuy
10458001.DR0458001.DRSurface Mount Fuses 48VAC 1A .18ohms 458 NANO2DigiKeyBuy MouserBuy LCSCBuy
1DX07S024XJ1R1100DX07S024XJ1R1100USB Connectors HYBRID SMT/THRU-HOLE STD MNT - USB TYPEDigiKeyBuy MouserBuy LCSCBuy
1BM05B-SURS-TFBM05B-SURS-TFCONN HEADER SMD 5POS 0.8MMDigiKeyBuy MouserBuy LCSCBuy
1313 lines