Skip to content

Xilinx Platform Cable USB on Windows

This note covers the xilinxPlatformCableUsb_alt backend on portable Windows builds.

Expected USB states

Before firmware load, some SP605/Xilinx embedded Platform Cable devices appear as:

03fd:000d

After xusb_emb.hex is loaded, the same device can re-enumerate as:

03fd:0008

Both states must be accessible to libusb. On Windows, use Zadig and bind the Xilinx device/interface to WinUSB or libusbK. If only the boot state is bound, firmware upload can succeed but JTAG transfers can fail after reload.

The public xilinx-xusb mirror documents udev entries for Xilinx PIDs such as 0007, 0008, 0009, 000d, 000f, 0013, and 0015.

Runtime firmware lookup

The portable package should contain:

install/share/openFPGALoader/xusb_emb.hex

Runtime lookup order is:

1. --probe-firmware <path>
2. OPENFPGALOADER_XUSB_FIRMWARE
3. share/openFPGALoader/xusb_emb.hex beside the portable install
4. legacy Vivado/ISE paths

Endpoint and alternate-setting debug

Some Windows/libusbK installations expose the post-firmware interface but reject libusb_set_interface_alt_setting(0, 1) with LIBUSB_ERROR_IO. If control transfers work but JTAG bulk transfers fail with:

FX2 write error: LIBUSB_ERROR_NOT_FOUND
JTAG init failed with: TDO is stuck at 0

then the bulk endpoint pair is not available in the currently selected alternate setting.

This fork scans the USB descriptors and tries to select the alternate setting that contains one bulk OUT endpoint and one bulk IN endpoint. It still defaults to the classic Xilinx endpoints:

OUT 0x02
IN  0x86

For manual testing, these environment variables are available:

$env:OPENFPGALOADER_XPCU_OUT_EP = "0x02"
$env:OPENFPGALOADER_XPCU_IN_EP  = "0x86"

To skip alternate-setting selection entirely:

$env:OPENFPGALOADER_XPCU_SKIP_ALT_SETTING = "1"

Clear them again with:

Remove-Item Env:\OPENFPGALOADER_XPCU_OUT_EP -ErrorAction SilentlyContinue
Remove-Item Env:\OPENFPGALOADER_XPCU_IN_EP -ErrorAction SilentlyContinue
Remove-Item Env:\OPENFPGALOADER_XPCU_SKIP_ALT_SETTING -ErrorAction SilentlyContinue

Normal test command:

.\openFPGALoader.exe -c xilinxPlatformCableUsb_alt --freq 700000 --detect

Control-transfer timeout after reload

Some SP605 / Xilinx Platform Cable USB combinations enumerate correctly after FX2 firmware load, and descriptor endpoint discovery can succeed, but the first vendor control read may still time out:

XPCU bulk endpoints: OUT=0x02 IN=0x86
Unable to read control request: LIBUSB_ERROR_TIMEOUT
JTAG init failed with: Unable to read constant.

The Windows backend now retries early XPCU control transfers and uses a longer FX2 control timeout by default.

Useful debug overrides:

$env:OPENFPGALOADER_FX2_CTRL_TIMEOUT_MS = "2000"
$env:OPENFPGALOADER_XPCU_CTRL_RETRIES = "60"
$env:OPENFPGALOADER_XPCU_CTRL_RETRY_DELAY_MS = "100"
.\openFPGALoader.exe -c xilinxPlatformCableUsb_alt --freq 700000 --detect

Use normal .\openFPGALoader.exe in the command above; the escaped marker is only to avoid accidental formatting in some markdown renderers.

Accelerated-transfer timeout

If version, status, and speed commands succeed but the first JTAG operation fails on the GPIO transfer command with LIBUSB_ERROR_TIMEOUT, the cable or its Windows USB driver is rejecting the accelerated 0xA6 transfer trigger. An IDCODE printed after that failure is invalid scan data and must not be added to the FPGA database.

The backend now falls back automatically to the older control-transfer JTAG protocol. To avoid waiting for the initial accelerated-transfer timeout, force that mode before running the command:

$env:OPENFPGALOADER_XPCU_CONTROL_BITBANG = "1"
.\openFPGALoader.exe -c xilinxPlatformCableUsb_alt --freq 700000 --detect

This mode performs two USB control writes per JTAG clock and a status read for each captured TDO bit, so detection and programming are considerably slower. Clear the override to retry accelerated transfers:

Remove-Item Env:\OPENFPGALOADER_XPCU_CONTROL_BITBANG -ErrorAction SilentlyContinue