xilinxPlatformCableUSB: driver for Xilinx Platform Cable USB probe

This commit is contained in:
Gwenhael Goavec-Merou 2026-05-13 16:27:29 +02:00
parent 698c8daaf4
commit db92a5ac24
6 changed files with 531 additions and 7 deletions

View File

@ -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)
####################################################################################################

View File

@ -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

View File

@ -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 <std::string, cable_t> 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 )},

View File

@ -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;

View File

@ -0,0 +1,378 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2022-2026 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <stdexcept>
#include <string>
#include <vector>
#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<FX2_ll>(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<uint8_t[]>(_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<uint8_t> 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<uint8_t>((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);
}

View File

@ -0,0 +1,108 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2022-2026 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#ifndef SRC_XILINXPLATFORMCABLEUSB_HPP_
#define SRC_XILINXPLATFORMCABLEUSB_HPP_
#include <cstdint>
#include <memory>
#include <string>
#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<int>(_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<uint8_t[]> _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_ll> fx2;
};
#endif // SRC_XILINXPLATFORMCABLEUSB_HPP_