xilinxPlatformCableUSB: driver for Xilinx Platform Cable USB probe
This commit is contained in:
parent
698c8daaf4
commit
db92a5ac24
|
|
@ -56,6 +56,7 @@ option(ENABLE_USB_BLASTERII "enable Altera USB Blaster II support" ${
|
||||||
|
|
||||||
set(BLASTERII_PATH "" CACHE STRING "usbBlasterII firmware directory")
|
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(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.
|
# Libgpiod is only available on Linux OS.
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
|
|
@ -64,6 +65,8 @@ else()
|
||||||
set(ENABLE_LIBGPIOD OFF)
|
set(ENABLE_LIBGPIOD OFF)
|
||||||
endif()
|
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.
|
# XVC and RemoteBitbang are not available on Windows OS.
|
||||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||||
option(ENABLE_REMOTEBITBANG "enable remote bitbang driver" ${ENABLE_CABLE_ALL})
|
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 OFF)
|
||||||
set(USE_LIBUSB_LL 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
|
# Only adds libftdi as dependency when a cable
|
||||||
# need this library.
|
# need this library.
|
||||||
if (ENABLE_FTDI_BASED_CABLE OR ENABLE_USB_BLASTERI OR ENABLE_XILINX_VIRTUAL_CABLE_SERVER)
|
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
|
# Only adds libusb as dependency when a cable need this library
|
||||||
if (ENABLE_DFU OR ENABLE_ANLOGIC_CABLE OR ENABLE_CH347 OR ENABLE_DIRTYJTAG
|
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)
|
set(USE_LIBUSB ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -120,11 +128,6 @@ if (ENABLE_USB_SCAN OR ENABLE_GOWIN_GWU2X)
|
||||||
set(USE_LIBUSB_LL ON)
|
set(USE_LIBUSB_LL ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Only enable fx2_ll when cable using it
|
|
||||||
if (ENABLE_USB_BLASTERII)
|
|
||||||
set(USE_FX2_LL ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Cross-compilation support
|
# Cross-compilation support
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
@ -179,6 +182,7 @@ include(GNUInstallDirs)
|
||||||
add_definitions(-DDATA_DIR=\"${CMAKE_INSTALL_FULL_DATAROOTDIR}\")
|
add_definitions(-DDATA_DIR=\"${CMAKE_INSTALL_FULL_DATAROOTDIR}\")
|
||||||
|
|
||||||
add_definitions(-DISE_DIR=\"${ISE_PATH}\")
|
add_definitions(-DISE_DIR=\"${ISE_PATH}\")
|
||||||
|
add_definitions(-DVIVADO_DIR=\"${VIVADO_PATH}\")
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Dependencies check/search
|
# Dependencies check/search
|
||||||
|
|
@ -591,6 +595,12 @@ list (APPEND OPENFPGALOADER_HEADERS src/svf_jtag.hpp)
|
||||||
add_definitions(-DENABLE_SVF_JTAG)
|
add_definitions(-DENABLE_SVF_JTAG)
|
||||||
endif()
|
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
|
# Xilinx Virtual Cable
|
||||||
if (ENABLE_XILINX_VIRTUAL_CABLE_CLIENT)
|
if (ENABLE_XILINX_VIRTUAL_CABLE_CLIENT)
|
||||||
list (APPEND OPENFPGALOADER_SOURCE src/xvc_client.cpp)
|
list (APPEND OPENFPGALOADER_SOURCE src/xvc_client.cpp)
|
||||||
|
|
@ -773,6 +783,13 @@ else()
|
||||||
message("Remote bitbang client support disabled")
|
message("Remote bitbang client support disabled")
|
||||||
endif()
|
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)
|
if (ENABLE_XILINX_VIRTUAL_CABLE_CLIENT)
|
||||||
add_definitions(-DENABLE_XVC_CLIENT=1)
|
add_definitions(-DENABLE_XVC_CLIENT=1)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-pthread ${CMAKE_EXE_LINKER_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "-pthread ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
@ -800,6 +817,7 @@ if (ZLIB_FOUND)
|
||||||
add_definitions(-DHAS_ZLIB=1)
|
add_definitions(-DHAS_ZLIB=1)
|
||||||
else()
|
else()
|
||||||
message("zlib library not found: can't flash intel/altera devices")
|
message("zlib library not found: can't flash intel/altera devices")
|
||||||
|
message("or reads compressed bitstreams")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (LINK_CMAKE_THREADS)
|
if (LINK_CMAKE_THREADS)
|
||||||
|
|
@ -814,7 +832,6 @@ if (USE_LIBFTDI)
|
||||||
add_definitions(-DFTDI_VERSION=${FTDI_VAL})
|
add_definitions(-DFTDI_VERSION=${FTDI_VAL})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
install(TARGETS openFPGALoader DESTINATION bin)
|
install(TARGETS openFPGALoader DESTINATION bin)
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
|
||||||
|
|
@ -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
|
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:
|
xvc-client:
|
||||||
|
|
||||||
- Name: Xilinx Virtual Cable
|
- Name: Xilinx Virtual Cable
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ enum communication_type {
|
||||||
MODE_CH347, /*! CH347 JTAG mode */
|
MODE_CH347, /*! CH347 JTAG mode */
|
||||||
MODE_GWU2X, /*! Gowin GWU2X JTAG mode */
|
MODE_GWU2X, /*! Gowin GWU2X JTAG mode */
|
||||||
MODE_ESP, /*! esp32c3, esp32s3 */
|
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", CABLE_DEF(MODE_USBBLASTER, 0x09Fb, 0x6810 )},
|
||||||
{"usb-blasterII_1", CABLE_DEF(MODE_USBBLASTER, 0x09Fb, 0x6010 )},
|
{"usb-blasterII_1", CABLE_DEF(MODE_USBBLASTER, 0x09Fb, 0x6010 )},
|
||||||
{"usb-blasterIII", FTDI_SER(0x09fb, 0x6022, FTDI_INTF_A, 0x08, 0x3B, 0x58, 0xb0)},
|
{"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 )},
|
{"xvc-client", CABLE_DEF(MODE_XVC_CLIENT, 0x0000, 0x0000 )},
|
||||||
#ifdef ENABLE_LIBGPIOD
|
#ifdef ENABLE_LIBGPIOD
|
||||||
{"libgpiod", CABLE_DEF(MODE_LIBGPIOD_BITBANG, 0, 0x0000 )},
|
{"libgpiod", CABLE_DEF(MODE_LIBGPIOD_BITBANG, 0, 0x0000 )},
|
||||||
|
|
|
||||||
12
src/jtag.cpp
12
src/jtag.cpp
|
|
@ -58,6 +58,9 @@
|
||||||
#ifdef ENABLE_XVC_CLIENT
|
#ifdef ENABLE_XVC_CLIENT
|
||||||
#include "xvc_client.hpp"
|
#include "xvc_client.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_XILINX_PLATFORM_CABLE_USB
|
||||||
|
#include "xilinxPlatformCableUSB.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
|
|
@ -206,6 +209,15 @@ Jtag::Jtag(const cable_t &cable, const jtag_pins_conf_t *pin_conf,
|
||||||
case MODE_REMOTEBITBANG:
|
case MODE_REMOTEBITBANG:
|
||||||
_jtag = new RemoteBitbang_client(ip_adr, port, verbose);
|
_jtag = new RemoteBitbang_client(ip_adr, port, verbose);
|
||||||
break;
|
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
|
#endif
|
||||||
default:
|
default:
|
||||||
std::cerr << "Jtag: unknown cable type" << std::endl;
|
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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_
|
||||||
Loading…
Reference in New Issue