From db92a5ac24ee78aa1e3f982638b2bde41ea902f3 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 13 May 2026 16:27:29 +0200 Subject: [PATCH] xilinxPlatformCableUSB: driver for Xilinx Platform Cable USB probe --- CMakeLists.txt | 31 ++- doc/cable.yml | 7 + src/cable.hpp | 2 + src/jtag.cpp | 12 ++ src/xilinxPlatformCableUSB.cpp | 378 +++++++++++++++++++++++++++++++++ src/xilinxPlatformCableUSB.hpp | 108 ++++++++++ 6 files changed, 531 insertions(+), 7 deletions(-) create mode 100644 src/xilinxPlatformCableUSB.cpp create mode 100644 src/xilinxPlatformCableUSB.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf20e1..40a4ebd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ option(ENABLE_USB_BLASTERII "enable Altera USB Blaster II support" ${ set(BLASTERII_PATH "" CACHE STRING "usbBlasterII firmware directory") set(ISE_PATH "/opt/Xilinx/14.7" CACHE STRING "ise root directory (default: /opt/Xilinx/14.7)") +set(VIVADO_PATH "" CACHE STRING "Vivado root directory (something like /opt/Xilinx/Vivado/VERSION)") # Libgpiod is only available on Linux OS. if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") @@ -64,6 +65,8 @@ else() set(ENABLE_LIBGPIOD OFF) endif() +option(ENABLE_XILINX_PLATFORM_CABLE_USB "enable Xilinx Platform Cable USB (XPCU) support" ${ENABLE_CABLE_ALL}) + # XVC and RemoteBitbang are not available on Windows OS. if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows") option(ENABLE_REMOTEBITBANG "enable remote bitbang driver" ${ENABLE_CABLE_ALL}) @@ -102,6 +105,11 @@ set(USE_LIBFTDI OFF) set(USE_LIBUSB OFF) set(USE_LIBUSB_LL OFF) +# USB BlasterII and Xilinx Platform Cable USB requires FX2 driver +if (ENABLE_XILINX_PLATFORM_CABLE_USB OR ENABLE_USB_BLASTERII) + set(USE_FX2_LL ON) +endif() + # Only adds libftdi as dependency when a cable # need this library. if (ENABLE_FTDI_BASED_CABLE OR ENABLE_USB_BLASTERI OR ENABLE_XILINX_VIRTUAL_CABLE_SERVER) @@ -112,7 +120,7 @@ endif(ENABLE_FTDI_BASED_CABLE OR ENABLE_USB_BLASTERI OR ENABLE_XILINX_VIRTUAL_CA # Only adds libusb as dependency when a cable need this library if (ENABLE_DFU OR ENABLE_ANLOGIC_CABLE OR ENABLE_CH347 OR ENABLE_DIRTYJTAG - OR ENABLE_ESP_USB OR ENABLE_JLINK OR ENABLE_GOWIN_GWU2X OR ENABLE_USB_BLASTERII OR ENABLE_USB_SCAN) + OR ENABLE_ESP_USB OR ENABLE_JLINK OR ENABLE_GOWIN_GWU2X OR USE_FX2_LL OR ENABLE_USB_SCAN) set(USE_LIBUSB ON) endif() @@ -120,11 +128,6 @@ if (ENABLE_USB_SCAN OR ENABLE_GOWIN_GWU2X) set(USE_LIBUSB_LL ON) endif() -# Only enable fx2_ll when cable using it -if (ENABLE_USB_BLASTERII) - set(USE_FX2_LL ON) -endif() - #################################################################################################### # Cross-compilation support #################################################################################################### @@ -179,6 +182,7 @@ include(GNUInstallDirs) add_definitions(-DDATA_DIR=\"${CMAKE_INSTALL_FULL_DATAROOTDIR}\") add_definitions(-DISE_DIR=\"${ISE_PATH}\") +add_definitions(-DVIVADO_DIR=\"${VIVADO_PATH}\") #################################################################################################### # Dependencies check/search @@ -591,6 +595,12 @@ list (APPEND OPENFPGALOADER_HEADERS src/svf_jtag.hpp) add_definitions(-DENABLE_SVF_JTAG) endif() +# Xilinx Platform Cable USB +if (ENABLE_XILINX_PLATFORM_CABLE_USB) + list(APPEND OPENFPGALOADER_SOURCE src/xilinxPlatformCableUSB.cpp) + list(APPEND OPENFPGALOADER_HEADERS src/xilinxPlatformCableUSB.hpp) +endif() + # Xilinx Virtual Cable if (ENABLE_XILINX_VIRTUAL_CABLE_CLIENT) list (APPEND OPENFPGALOADER_SOURCE src/xvc_client.cpp) @@ -773,6 +783,13 @@ else() message("Remote bitbang client support disabled") endif() +if (ENABLE_XILINX_PLATFORM_CABLE_USB) + add_definitions(-DENABLE_XILINX_PLATFORM_CABLE_USB=1) + message("Xilinx Platform Cable USB (XPCU) support enabled") +else() + message("Xilinx Platform Cable USB (XCPU) support disabled") +endif() + if (ENABLE_XILINX_VIRTUAL_CABLE_CLIENT) add_definitions(-DENABLE_XVC_CLIENT=1) set(CMAKE_EXE_LINKER_FLAGS "-pthread ${CMAKE_EXE_LINKER_FLAGS}") @@ -800,6 +817,7 @@ if (ZLIB_FOUND) add_definitions(-DHAS_ZLIB=1) else() message("zlib library not found: can't flash intel/altera devices") + message("or reads compressed bitstreams") endif() if (LINK_CMAKE_THREADS) @@ -814,7 +832,6 @@ if (USE_LIBFTDI) add_definitions(-DFTDI_VERSION=${FTDI_VAL}) endif() - install(TARGETS openFPGALoader DESTINATION bin) #################################################################################################### diff --git a/doc/cable.yml b/doc/cable.yml index f8c8230..81cdd02 100644 --- a/doc/cable.yml +++ b/doc/cable.yml @@ -302,6 +302,13 @@ usb-blasterIII: URL: https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_usb_blstr_ii_cable.pdf +xilinxPlatformCableUsb: + + - Name: Xilinx Platform Cable USB (XPCU) + Description: Xilinx Platform Cable USB (XPCU) from AMD/Xilinx + URL: https://www.amd.com/en/products/adaptive-socs-and-fpgas/board-accessories/hw-usb-ii-g.html + + xvc-client: - Name: Xilinx Virtual Cable diff --git a/src/cable.hpp b/src/cable.hpp index a9b79c9..ab28d74 100644 --- a/src/cable.hpp +++ b/src/cable.hpp @@ -30,6 +30,7 @@ enum communication_type { MODE_CH347, /*! CH347 JTAG mode */ MODE_GWU2X, /*! Gowin GWU2X JTAG mode */ MODE_ESP, /*! esp32c3, esp32s3 */ + MODE_XPCU, /*! Xilinx Platform Cable USB (XPCU) */ }; /*! @@ -137,6 +138,7 @@ static std::map cable_list = { {"usb-blasterII", CABLE_DEF(MODE_USBBLASTER, 0x09Fb, 0x6810 )}, {"usb-blasterII_1", CABLE_DEF(MODE_USBBLASTER, 0x09Fb, 0x6010 )}, {"usb-blasterIII", FTDI_SER(0x09fb, 0x6022, FTDI_INTF_A, 0x08, 0x3B, 0x58, 0xb0)}, + {"xilinxPlatformCableUsb", CABLE_DEF(MODE_XPCU, 0x03fd, 0x0013)}, {"xvc-client", CABLE_DEF(MODE_XVC_CLIENT, 0x0000, 0x0000 )}, #ifdef ENABLE_LIBGPIOD {"libgpiod", CABLE_DEF(MODE_LIBGPIOD_BITBANG, 0, 0x0000 )}, diff --git a/src/jtag.cpp b/src/jtag.cpp index b5ed9a5..d084bd8 100644 --- a/src/jtag.cpp +++ b/src/jtag.cpp @@ -58,6 +58,9 @@ #ifdef ENABLE_XVC_CLIENT #include "xvc_client.hpp" #endif +#ifdef ENABLE_XILINX_PLATFORM_CABLE_USB +#include "xilinxPlatformCableUSB.hpp" +#endif #define DEBUG 0 @@ -206,6 +209,15 @@ Jtag::Jtag(const cable_t &cable, const jtag_pins_conf_t *pin_conf, case MODE_REMOTEBITBANG: _jtag = new RemoteBitbang_client(ip_adr, port, verbose); break; +#endif + case MODE_XPCU: +#ifdef ENABLE_XILINX_PLATFORM_CABLE_USB + _jtag = new XilinxPlatformCableUSB(0x03fd, 0x0013, clkHZ, + firmware_path, verbose); + break; +#else + printError("Jtag: support for Xilinx Platform Cable USB (XPCU) was not enabled at compile time"); + throw std::exception(); #endif default: std::cerr << "Jtag: unknown cable type" << std::endl; diff --git a/src/xilinxPlatformCableUSB.cpp b/src/xilinxPlatformCableUSB.cpp new file mode 100644 index 0000000..4245e90 --- /dev/null +++ b/src/xilinxPlatformCableUSB.cpp @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022-2026 Gwenhael Goavec-Merou + */ + +#include +#include +#include + +#include +#include +#include + +#include "display.hpp" +#include "fx2_ll.hpp" +#include "xilinxPlatformCableUSB.hpp" + +#define XPCU_BREQUEST 0xB0 +#define XPCU_INITIALIZED_VID 0x03fd +#define XPCU_INITIALIZED_PID 0x0008 +#define XPCU_CMD_DISABLE 0x10 +#define XPCU_CMD_ENABLE 0x18 +#define XPCU_CMD_SET_SPEED 0x28 +#define XPCU_CMD_STATUS 0x38 +#define XPCU_CMD_RETURN_CONSTANT 0x40 +#define XPCU_CMD_GET_VERSION 0x50 +#define XPCU_CMD_GPIO_TRANSFER 0xA6 +#define XPCU_EP_JTAG_OUT 0x02 +#define XPCU_EP_JTAG_IN 0x86 +#define XPCU_STATUS_CONNECTED 0x40 +#define XPCU_SPEED_CLASS_ENABLE 0x10 +#define XPCU_VERSION_CPLD 0x01 +#define XPCU_VERSION_CONST1 0x02 +#define XPCU_VERSION_CONST2 0x03 + +#define TCK_OFFSET 0 +#define TDO_OFFSET 4 +#define TDI_OFFSET 0 +#define TMS_OFFSET 4 + +#define TCK_IDX (1 << TCK_OFFSET) +#define TDO_IDX (1 << TDO_OFFSET) +#define TDI_IDX (1 << TDI_OFFSET) +#define TMS_IDX (1 << TMS_OFFSET) + +XilinxPlatformCableUSB::XilinxPlatformCableUSB(const uint16_t vid, + const uint16_t pid, + uint32_t clkHz, + const std::string &firmware_path, + int8_t verbose): _verbose(verbose), _nb_bit(0), _nb_tdo_bit(0), + _curr_tms(0), _curr_tdi(0), _buffer_size(4096), + _buffer_bit_size((_buffer_size / 2 * 4) - 1) +{ + std::string firmware_file; + /* firmare path must be known: + * 1/ provided by user + * 2/ from Vivado install directory + * 3/ from ISE install directory + */ + if (firmware_path.empty() && strlen(ISE_DIR) == 0 && strlen(VIVADO_DIR) == 0) { + printError("missing FX2 firmware"); + printError("use --probe-firmware with something"); + printError("like /opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/xusb_xp2.hex for ISE"); + printError("or /opt/Xilinx/Vivado/VERSION/data/xicom/xusb_xp2.hex for Vivado"); + printError("Or use -DISE_DIR=/opt/Xilinx/14.7 / -DVIVADO_DIR=/opt/Xilinx/Vivado/VERSION at build time"); + throw std::runtime_error("xilinxPlatformCableUSB: missing firmware"); + } + + /* Extract firmware according to possibilities */ + if (!firmware_path.empty()) + firmware_file = firmware_path; + else if (strlen(VIVADO_DIR) > 0) + firmware_file = VIVADO_DIR "/data/xicom/xusb_xp2.hex"; + else if (strlen(ISE_DIR) > 0) + firmware_file = ISE_DIR "/ISE_DS/ISE/bin/lin64/xusb_xp2.hex"; + + try { + fx2 = std::make_unique(vid, pid, XPCU_INITIALIZED_VID, + XPCU_INITIALIZED_PID, firmware_file); + } catch (std::exception &e) { + printError(e.what()); + throw std::runtime_error("lowlevel init failed"); + } + + fx2->set_interface_alt_setting(0, 1); + + displayCableVersion(); + + /* Write GPIO bit */ + fx2->write_ctrl(XPCU_BREQUEST, 0x030, nullptr, 0, (1 << 3)); + + if (!enableDevice(true)) + throw std::runtime_error("Unable to enable device"); + + uint8_t buf[1]; + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_STATUS, buf, 1)) + throw std::runtime_error("Unable to read status."); + + char mess[64]; + snprintf(mess, sizeof(mess), "status %02x connected: %s", + buf[0], (buf[0] & XPCU_STATUS_CONNECTED) ? "yes" : "no"); + printInfo(mess); + + _in_buf = std::make_unique(_buffer_size); + + setClkFreq(clkHz); +} + +XilinxPlatformCableUSB::~XilinxPlatformCableUSB() +{ + flush(); + enableDevice(false); +} + +int XilinxPlatformCableUSB::setClkFreq(uint32_t clkHz) +{ + /* speed table: index Hz; bit 4 must always be set in the speed class */ + static constexpr uint32_t speeds[] = {12000000, 6000000, 3000000, 1500000, 750000}; + uint8_t speed = 4; /* default: slowest */ + for (uint8_t i = 0; i < sizeof(speeds) / sizeof(speeds[0]); i++) { + if (speeds[i] <= clkHz) { + speed = i; + break; + } + } + if (!fx2->write_ctrl(XPCU_BREQUEST, XPCU_CMD_SET_SPEED, nullptr, 0, + speed | XPCU_SPEED_CLASS_ENABLE)) { + printError("setClkFreq: failed to set speed"); + return -1; + } + _clkHZ = speeds[speed]; + printInfo("Jtag frequency : requested " + std::to_string(clkHz) + + " Hz -> real " + std::to_string(_clkHZ) + " Hz"); + return _clkHZ; +} + +int XilinxPlatformCableUSB::writeTMS(const uint8_t *tms, uint32_t len, + bool flush_buffer, const uint8_t tdi) +{ + int ret; + + if (len == 0) + return flush_buffer ? flush() : 0; + + _curr_tdi = tdi ? 1 : 0; + + for (uint32_t i = 0; i < len; i++) { + _curr_tms = (tms[i >> 3] >> (i & 0x07)) & 0x01; + if (storeBit(_curr_tdi, _curr_tms, 1, 0)) { + if (write(nullptr, 0) < 0) + return -1; + } + } + + if (flush_buffer) { + ret = flush(); + if (ret < 0) + return ret; + } + + return len; +} + +int XilinxPlatformCableUSB::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) +{ + if (len == 0) + return 0; + + if (rx && _nb_bit != 0) { + if (write(nullptr, 0) < 0) + return -1; + } + + uint32_t rx_offset = 0; + + for (uint32_t i = 0; i < len; i++) { + bool last_bit = (i == len - 1 && end); + _curr_tdi = tx ? (0x01 & ((tx[i >> 3]) >> (i & 0x07))) : 0; + + if (last_bit) + _curr_tms = 1; + + if (storeBit(_curr_tdi, _curr_tms, 1, rx ? 1 : 0)) { + uint32_t bits = _nb_tdo_bit; + if (write(rx, rx_offset) < 0) + return -1; + rx_offset += bits; + } + } + + if (_nb_bit != 0 && (end || rx)) { + if (write(rx, rx_offset) < 0) + return -1; + } + + return len; +} + +int XilinxPlatformCableUSB::toggleClk([[maybe_unused]] uint8_t tms, + [[maybe_unused]] uint8_t tdi, uint32_t clk_len) +{ + for (uint32_t i = 0; i < clk_len; i++) { + if (storeBit(_curr_tdi, _curr_tms, 1, 0)) { + if (write(nullptr, 0) < 0) + return -1; + } + } + + /* Flush buffer if not empty */ + if (_nb_bit != 0) { + if (flush() < 0) + return -1; + } + + return clk_len; +} + +int XilinxPlatformCableUSB::flush() +{ + return write(nullptr, 0); +} + +/* TMS 1st nibble, TDI 2nd nibble, TDO 3rd nibble, TCK 4th nibble + * byte n byte n+1 + * [7:4 3:0] [7:4 3:0] + * TMS TDI TDO TCK + */ +bool XilinxPlatformCableUSB::storeBit(uint8_t tdi, uint8_t tms, + uint8_t tck, uint8_t tdo) noexcept +{ + const uint32_t buf_pos = (_nb_bit >> 2) << 1; + const uint8_t bit_pos = _nb_bit & 0x03; + + if (bit_pos == 0) + _in_buf[buf_pos] = _in_buf[buf_pos + 1] = 0; + + if (tms) + _in_buf[buf_pos] |= (TMS_IDX << bit_pos); + if (tdi) + _in_buf[buf_pos] |= (TDI_IDX << bit_pos); + if (tdo) + _in_buf[buf_pos + 1] |= (TDO_IDX << bit_pos); + if (tck) + _in_buf[buf_pos + 1] |= (TCK_IDX << bit_pos); + + _nb_bit++; + if (tdo) + _nb_tdo_bit++; + + return _nb_bit >= _buffer_bit_size; +} + +/* Compute how many bytes EP6 will return for nb_bit TDO bits. + * The device uses a shift-register encoding: a 16-bit register that grows + * to 32-bit after 16 bits, then a new register starts every 32 bits. + */ +uint32_t XilinxPlatformCableUSB::rxBufSize(uint32_t nb_bit) noexcept +{ + const uint32_t full_groups = nb_bit / 32; + const uint32_t rem = nb_bit & 31u; + return full_groups * 4 + (rem == 0 ? 0 : (rem > 16 ? 4 : 2)); +} + +int XilinxPlatformCableUSB::write(uint8_t *rx, uint32_t rx_offset) +{ + if (_nb_bit == 0) + return 0; + + /* N ops: N/4 pairs of 2 bytes each (round up to complete pair) */ + uint32_t xfer_tx = (_nb_bit >> 1) & ~0x01u; + xfer_tx += ((_nb_bit & 0x03) != 0) ? 2 : 0; + + /* count is 0-indexed per protocol spec */ + if (!fx2->write_ctrl(XPCU_BREQUEST, XPCU_CMD_GPIO_TRANSFER, nullptr, 0, + _nb_bit - 1)) { + printError("Fails to write GPIO transfer control message"); + return -1; + } + + if (fx2->write(XPCU_EP_JTAG_OUT, _in_buf.get(), xfer_tx) != (int)xfer_tx) + return -1; + + if (rx) { + if (_nb_tdo_bit != _nb_bit) { + printError("Unable to decode mixed TDO/non-TDO transfer"); + return -1; + } + + uint32_t xfer_rx = rxBufSize(_nb_tdo_bit); + std::vector rx_buf(xfer_rx); + if (fx2->read(XPCU_EP_JTAG_IN, rx_buf.data(), xfer_rx) != (int)xfer_rx) + return -1; + + /* Decode shift-register encoded TDO bits into rx. + * Each group of up to 32 bits occupies a 16 or 32-bit little-endian + * shift register: bit_k is at position (reg_size - group + k). + */ + uint32_t buf_off = 0; + uint32_t remaining = _nb_tdo_bit; + uint32_t bit_idx = 0; + while (remaining > 0) { + const uint32_t group = (remaining > 32) ? 32 : remaining; + const uint32_t reg_size = (group > 16) ? 32 : 16; + const uint32_t shift = reg_size - group; + uint32_t reg = 0; + for (uint32_t b = 0; b < reg_size / 8; b++) + reg |= (uint32_t)rx_buf[buf_off + b] << (b * 8); + const uint32_t base = rx_offset + bit_idx; + if ((base & 7u) == 0 && (group & 7u) == 0) { + uint8_t *out = rx + (base >> 3); + for (uint32_t b = 0; b < group / 8; b++) + out[b] = static_cast((reg >> (shift + b * 8)) & 0xFF); + } else { + for (uint32_t k = 0; k < group; k++) { + const uint32_t out_bit = base + k; + if ((reg >> (shift + k)) & 1) + rx[out_bit >> 3] |= (1 << (out_bit & 7)); + else + rx[out_bit >> 3] &= ~(1 << (out_bit & 7)); + } + } + buf_off += reg_size / 8; + bit_idx += group; + remaining -= group; + } + } + + _nb_bit = 0; + _nb_tdo_bit = 0; + return 0; +} + +bool XilinxPlatformCableUSB::enableDevice(bool enable) +{ + if (!fx2->write_ctrl(XPCU_BREQUEST, + (enable ? XPCU_CMD_ENABLE : XPCU_CMD_DISABLE), nullptr, 0)) { + char mess[64]; + snprintf(mess, sizeof(mess), "Unable to %s device", + (enable ? "enable" : "disable")); + printError(mess); + return false; + } + return true; +} + +void XilinxPlatformCableUSB::displayCableVersion() +{ + uint8_t buf[2]; + + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_RETURN_CONSTANT, buf, 2)) + throw std::runtime_error("Unable to read constant."); + const uint16_t const0 = ((uint16_t)buf[0] << 8) | buf[1]; + + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_GET_VERSION, buf, 2)) + throw std::runtime_error("Unable to read firmware version."); + const uint16_t fx2_firmware = ((uint16_t)buf[0] << 8) | buf[1]; + + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_GET_VERSION, buf, 2, + XPCU_VERSION_CPLD)) + throw std::runtime_error("Unable to read CPLD version."); + const uint16_t cpld_firmware = ((uint16_t)buf[0] << 8) | buf[1]; + + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_GET_VERSION, buf, 2, + XPCU_VERSION_CONST1)) + throw std::runtime_error("Unable to read const 1."); + const uint16_t const1 = ((uint16_t)buf[0] << 8) | buf[1]; + + if (!fx2->read_ctrl(XPCU_BREQUEST, XPCU_CMD_GET_VERSION, buf, 2, + XPCU_VERSION_CONST2)) + throw std::runtime_error("Unable to read const 2."); + const uint16_t const2 = ((uint16_t)buf[0] << 8) | buf[1]; + + printf("FX2 version: %04x\n", fx2_firmware); + printf("CPLD version: %04x\n", cpld_firmware); + printf("Const0 version: %04x\n", const0); + printf("Const1 version: %04x\n", const1); + printf("Const2 version: %04x\n", const2); +} diff --git a/src/xilinxPlatformCableUSB.hpp b/src/xilinxPlatformCableUSB.hpp new file mode 100644 index 0000000..0ad44a5 --- /dev/null +++ b/src/xilinxPlatformCableUSB.hpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022-2026 Gwenhael Goavec-Merou + */ + +#ifndef SRC_XILINXPLATFORMCABLEUSB_HPP_ +#define SRC_XILINXPLATFORMCABLEUSB_HPP_ + +#include +#include +#include + +#include "fx2_ll.hpp" +#include "jtagInterface.hpp" + +#define XPCU_DEFAULT_VID 0x03fd +#define XPCU_DEFAULT_PID 0x0013 + +/*! + * \file xilinxPlatformCableUSB.hpp + * \class XilinxPlatformCableUSB + * \brief concrete class between jtag implementation and Xilinx USB Cable probe + * \author Gwenhael Goavec-Merou + */ +class XilinxPlatformCableUSB : public JtagInterface { + public: + XilinxPlatformCableUSB(const uint16_t vid = XPCU_DEFAULT_VID, + const uint16_t pid = XPCU_DEFAULT_PID, + uint32_t clkHz = 750000, + const std::string &firmware_path = "", + int8_t verbose = 0); + ~XilinxPlatformCableUSB() override; + + int setClkFreq(uint32_t clkHz) override; + + /*! + * \brief drive TMS to move in JTAG state machine + * \param tms serie of TMS state + * \param len number of TMS state + * \param flush_buffer force flushing the buffer + * \param tdi TDI constant value + * \return number of state written + */ + int writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer, + const uint8_t tdi = 1) override; + + /*! + * \brief send TDI bits (mainly in shift DR/IR state) + * \param tx array of TDI values (used to write) + * \param rx array of TDO values (used when read) + * \param len number of bit to send/receive + * \param end in JTAG state machine last bit and tms are set in same time + * \return number of bit written and/or read + */ + int writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override; + + /*! + * \brief toggle clock with static tms and tdi + * \param tms state of tms signal + * \param tdi state of tdi signal + * \param clk_len number of clock cycle + * \return number of clock cycle send + */ + int toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) override; + + /*! + * \brief return internal buffer size (in byte). + * \return _buffer_size + */ + int get_buffer_size() override { return static_cast(_buffer_size); } + + bool isFull() override { return _nb_bit >= _buffer_bit_size; } + + int flush() override; + + /*! + * \brief display fx2 & CPLD firmwares version + */ + void displayCableVersion(); + + /*! + * \brief enable device output + * \param[in] enable: enable or disable device + * \return true when transfer success, false otherwise + */ + bool enableDevice(bool enable); + + private: + /* \brief pack one JTAG bit into the internal buffer + * \return true when buffer is full, false otherwise + */ + bool storeBit(uint8_t tdi, uint8_t tms, uint8_t tck, uint8_t tdo) noexcept; + int write(uint8_t *rx, uint32_t rx_offset = 0); + static uint32_t rxBufSize(uint32_t nb_bit) noexcept; + + int8_t _verbose; + + std::unique_ptr _in_buf; + uint32_t _nb_bit; + uint32_t _nb_tdo_bit; + uint8_t _curr_tms; + uint8_t _curr_tdi; + uint32_t _buffer_size; + uint32_t _buffer_bit_size; + std::unique_ptr fx2; +}; + +#endif // SRC_XILINXPLATFORMCABLEUSB_HPP_