Merge branch 'trabucayre:master' into master

This commit is contained in:
dettus 2026-05-30 05:38:44 +02:00 committed by GitHub
commit 8de2ae4562
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 840 additions and 253 deletions

View File

@ -118,6 +118,7 @@ jobs:
run: | run: |
#brew update #brew update
brew install --only-dependencies openFPGALoader brew install --only-dependencies openFPGALoader
brew install hidapi
- name: '🚧 Build tarball' - name: '🚧 Build tarball'
run: | run: |
@ -214,4 +215,4 @@ jobs:
with: with:
token: ${{ github.token }} token: ${{ github.token }}
tag: 'nightly' tag: 'nightly'
files: artifact/**/* files: artifact/**/*

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(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,28 +182,23 @@ 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
#################################################################################################### ####################################################################################################
if (USE_LIBFTDI)
# Try to find the LibFTDI1 with cmake
find_package(LibFTDI1 QUIET)
endif()
if (USE_PKGCONFIG) if (USE_PKGCONFIG)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
# Backward compat when the libftdi is not found # Backward compat when the libftdi is not found
# by using cmake # by using cmake
if (USE_LIBFTDI) if (USE_LIBFTDI)
if (NOT LIBFTDI_FOUND) pkg_check_modules(LIBFTDI REQUIRED libftdi1)
pkg_check_modules(LIBFTDI REQUIRED libftdi1) string(REPLACE "." ";" VERSION_LIST ${LIBFTDI_VERSION})
string(REPLACE "." ";" VERSION_LIST ${LIBFTDI_VERSION}) list(GET VERSION_LIST 0 LIBFTDI_VERSION_MAJOR)
list(GET VERSION_LIST 0 LIBFTDI_VERSION_MAJOR) list(GET VERSION_LIST 1 LIBFTDI_VERSION_MINOR)
list(GET VERSION_LIST 1 LIBFTDI_VERSION_MINOR) string(REGEX REPLACE "[^0-9].*" "" LIBFTDI_VERSION_MINOR "${LIBFTDI_VERSION_MINOR}")
endif()
else() else()
set(LIBFTDI_LIBRARY_DIRS "") set(LIBFTDI_LIBRARY_DIRS "")
set(LIBFTDI_INCLUDE_DIRS "") set(LIBFTDI_INCLUDE_DIRS "")
@ -590,6 +588,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)
@ -772,6 +776,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}")
@ -799,6 +810,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)
@ -813,7 +825,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)
#################################################################################################### ####################################################################################################

View File

@ -107,7 +107,8 @@ openFPGALoader -- a program to flash FPGA
non volatile memory (default: 0) non volatile memory (default: 0)
--pins arg pin config TDI:TDO:TCK:TMS or --pins arg pin config TDI:TDO:TCK:TMS or
MOSI:MISO:SCK:CS[:HOLDN:WPN] MOSI:MISO:SCK:CS[:HOLDN:WPN]
--probe-firmware arg firmware for JTAG probe (usbBlasterII) --probe-firmware arg firmware for JTAG probe
(usbBlasterII/xilinxPlatformCableUsb)
--protect-flash arg protect SPI flash area --protect-flash arg protect SPI flash area
--quiet Produce quiet output (no progress bar) --quiet Produce quiet output (no progress bar)
-r, --reset reset FPGA after operations -r, --reset reset FPGA after operations

View File

@ -34,6 +34,13 @@
Memory: OK Memory: OK
Flash: NT Flash: NT
- ID: axc3000
Description: Arrow AXC3000
URL: https://www.arrow.com/de/products/axc3000/trenz-electronic-gmbh.html
FPGA: Altera Agilex 3 A3CY100BM16AE7S
Memory: OK
Flash: NT
- ID: axe5000 - ID: axe5000
Description: A5EC008BM16AE6S FPGA Starter Kit Description: A5EC008BM16AE6S FPGA Starter Kit
URL: https://www.arrow.com/en/products/axe5000/trenz-electronic-gmbh.html URL: https://www.arrow.com/en/products/axe5000/trenz-electronic-gmbh.html
@ -380,8 +387,8 @@
Memory: OK Memory: OK
Flash: OK Flash: OK
Constraints: Constraints:
- ECPIX-5-45F - ECPIX-5-45F
- ECPIX-5-85F - ECPIX-5-85F
- ID: ecpix5_r03 - ID: ecpix5_r03
Description: LambdaConcept ECPIX-5 (FT4232) Description: LambdaConcept ECPIX-5 (FT4232)
@ -390,8 +397,8 @@
Memory: OK Memory: OK
Flash: OK Flash: OK
Constraints: Constraints:
- ECPIX-5-45F - ECPIX-5-45F
- ECPIX-5-85F - ECPIX-5-85F
- ID: fireant - ID: fireant
Description: Fireant Trion T8 Description: Fireant Trion T8
@ -451,8 +458,8 @@
Memory: NA Memory: NA
Flash: OK Flash: OK
Constraints: Constraints:
- iCEBreaker-bitsy-v0 - iCEBreaker-bitsy-v0
- iCEBreaker-bitsy-v1 - iCEBreaker-bitsy-v1
- ID: ice40_generic - ID: ice40_generic
Description: icestick Description: icestick
@ -751,6 +758,13 @@
Memory: OK Memory: OK
Flash: NT Flash: NT
- ID: sp701
Description: Xilinx SP701
URL: https://www.amd.com/en/products/adaptive-socs-and-fpgas/evaluation-boards/sp701.html
FPGA: Spartan 7 xc7s100-2-fgga676
Memory: OK
Flash: OK
- ID: spartanEdgeAccelBoard - ID: spartanEdgeAccelBoard
Description: SeeedStudio Spartan Edge Accelerator Board Description: SeeedStudio Spartan Edge Accelerator Board
URL: http://wiki.seeedstudio.com/Spartan-Edge-Accelerator-Board URL: http://wiki.seeedstudio.com/Spartan-Edge-Accelerator-Board
@ -855,6 +869,13 @@
Memory: OK Memory: OK
Flash: IF Flash: IF
- ID: brs-100-gw1nr9
Description: BrisbaneSilicon BRS-100-GW1NR9
URL: https://brisbanesilicon.com.au/brs-100-gw1nr9/
FPGA: littleBee GW1NR-9C
Memory: OK
Flash: IF/EF
- ID: trion_t20_bga256_jtag - ID: trion_t20_bga256_jtag
Description: Efinix Trion T20 BGA256 Dev Kit Description: Efinix Trion T20 BGA256 Dev Kit
URL: https://www.efinixinc.com/products-devkits-triont20.html URL: https://www.efinixinc.com/products-devkits-triont20.html
@ -904,10 +925,10 @@
Memory: OK Memory: OK
Flash: OK Flash: OK
Constraints: Constraints:
- ULX3S-12F - ULX3S-12F
- ULX3S-25F - ULX3S-25F
- ULX3S-45F - ULX3S-45F
- ULX3S-85F - ULX3S-85F
- ID: ulx3s_dfu - ID: ulx3s_dfu
Description: Radiona ULX3S DFU mode Description: Radiona ULX3S DFU mode

View File

@ -33,6 +33,13 @@ bus_blaster_b:
URL: http://dangerousprototypes.com/docs/Bus_Blaster URL: http://dangerousprototypes.com/docs/Bus_Blaster
ch347:
- Name: CH347
Description: CH347 is a USB HS bus converter with UART, I2C, SPI and JTAG interfaces
URL: https://github.com/wuxx/USB-HS-Bridge
ch347_jtag: ch347_jtag:
- Name: ch347 JTAG adapter - Name: ch347 JTAG adapter
@ -54,33 +61,6 @@ cmsisdap:
URL: https://os.mbed.com/docs/mbed-os/v6.11/debug-test/daplink.html URL: https://os.mbed.com/docs/mbed-os/v6.11/debug-test/daplink.html
gatemate_pgm:
- Name: gatemate pgm
Description: Cologne Chip GateMate FPGA Programmer. FT232H-based JTAG/SPI programmer cable
URL: https://colognechip.com/programmable-logic/gatemate/
gatemate_evb_jtag:
- Name: gatemate evb JTAG
Description: Cologne Chip GateMate JTAG programmer
URL: https://colognechip.com/programmable-logic/gatemate/
gatemate_evb_spi:
- Name: gatemate evb spi
Description: Cologne Chip GateMate SPI programmer
URL: https://colognechip.com/programmable-logic/gatemate/
gwu2x:
- Name: gwu2x
Description: Gowin GWUX2X
URL: https://www.gowinsemi.com/en/product/detail/55/
dfu: dfu:
- Name: DFU interface - Name: DFU interface
@ -94,7 +74,7 @@ digilent:
Description: FT2232 JTAG / UART cable Description: FT2232 JTAG / UART cable
diglent_b: digilent_b:
- Name: digilent cable - Name: digilent cable
Description: digilent FT2232 JTAG / UART cable (interface B) Description: digilent FT2232 JTAG / UART cable (interface B)
@ -108,12 +88,14 @@ digilent_hs2:
digilent_hs3: digilent_hs3:
- Name: digilent hs3 - Name: digilent hs3
Description: JTAG programmer cable from digilent Description: JTAG programmer cable from digilent
URL: https://digilent.com/shop/jtag-hs3-programming-cable/ URL: https://digilent.com/shop/jtag-hs3-programming-cable/
dirtyJtag: dirtyJtag:
- Name: dirty Jtag - Name: dirty Jtag
Description: JTAG probe firmware for STM32F1 Description: JTAG probe firmware for STM32F1
URL: https://github.com/jeanthom/DirtyJTAG URL: https://github.com/jeanthom/DirtyJTAG
@ -121,16 +103,17 @@ dirtyJtag:
New version `dirtyjtag2 <https://github.com/jeanthom/DirtyJTAG/tree/dirtyjtag2>`__ is also supported New version `dirtyjtag2 <https://github.com/jeanthom/DirtyJTAG/tree/dirtyjtag2>`__ is also supported
efinix_spi_ft4232: ecpix5-debug:
- Name: efinix SPI (ft4232) - Name: ecpix5-debug
Description: efinix SPI interface (FTDI4232 interface A) Description: LambdaConcept ECPIX5 (45k/85k) UART/JTAG interface
URL: https://shop.lambdaconcept.com/home/46-ecpix-5.html
efinix_jtag_ft4232: efinix_jtag_ft2232:
- Name: efinix JTAG (ft4232) - Name: efinix JTAG (ft2232)
Description: efinix JTAG interface (FTDI4232 interface B) Description: efinix JTAG interface (FTDI2232 interface B)
efinix_spi_ft2232: efinix_spi_ft2232:
@ -139,10 +122,16 @@ efinix_spi_ft2232:
Description: efinix SPI interface (FTDI2232 interface A) Description: efinix SPI interface (FTDI2232 interface A)
efinix_jtag_ft2232: efinix_jtag_ft4232:
- Name: efinix JTAG (ft2232) - Name: efinix JTAG (ft4232)
Description: efinix JTAG interface (FTDI2232 interface B) Description: efinix JTAG interface (FTDI4232 interface B)
efinix_spi_ft4232:
- Name: efinix SPI (ft4232)
Description: efinix SPI interface (FTDI4232 interface A)
ft2232: ft2232:
@ -196,23 +185,54 @@ ft4232:
Description: quad interface programmer cable. MPSSE capable. Description: quad interface programmer cable. MPSSE capable.
URL: https://ftdichip.com/products/ft4232h-56q/ URL: https://ftdichip.com/products/ft4232h-56q/
ft4232hp: ft4232hp:
- Name: FT4232HP (interface A) - Name: FT4232HP (interface A)
Description: quad interface programmer cable. MPSSE capable. High Speed USB Bridge with Type-C/PD3.0 Controller Description: quad interface programmer cable. MPSSE capable. High Speed USB Bridge with Type-C/PD3.0 Controller
URL: https://ftdichip.com/wp-content/uploads/2023/02/DS_FT4233HP.pdf URL: https://ftdichip.com/wp-content/uploads/2023/02/DS_FT4233HP.pdf
ft4232hp_b: ft4232hp_b:
- Name: FT4232HP (interface B) - Name: FT4232HP (interface B)
Description: quad interface programmer cable. MPSSE capable. High Speed USB Bridge with Type-C/PD3.0 Controller Description: quad interface programmer cable. MPSSE capable. High Speed USB Bridge with Type-C/PD3.0 Controller
URL: https://ftdichip.com/wp-content/uploads/2023/02/DS_FT4233HP.pdf URL: https://ftdichip.com/wp-content/uploads/2023/02/DS_FT4233HP.pdf
ecpix5-debug:
- Name: ecpix5-debug gatemate_pgm:
Description: LambdaConcept ECPIX5 (45k/85k) UART/JTAG interface
URL: https://shop.lambdaconcept.com/home/46-ecpix-5.html - Name: gatemate pgm
Description: Cologne Chip GateMate FPGA Programmer. FT232H-based JTAG/SPI programmer cable
URL: https://colognechip.com/programmable-logic/gatemate/
gatemate_evb_jtag:
- Name: gatemate evb JTAG
Description: Cologne Chip GateMate JTAG programmer
URL: https://colognechip.com/programmable-logic/gatemate/
gatemate_evb_spi:
- Name: gatemate evb spi
Description: Cologne Chip GateMate SPI programmer
URL: https://colognechip.com/programmable-logic/gatemate/
gwu2x:
- Name: gwu2x
Description: Gowin GWUX2X
URL: https://www.gowinsemi.com/en/product/detail/55/
jetson-nano-gpio:
- Name: Bitbang GPIO
Description: Bitbang GPIO pins on Jetson Nano Linux host. Use /dev/mem to have a faster clock.
URL: https://github.com/jwatte/jetson-gpio-example
jlink: jlink:
@ -235,6 +255,13 @@ jtag-smt2-nc:
URL: https://digilent.com/shop/jtag-smt2-nc-surface-mount-programming-module URL: https://digilent.com/shop/jtag-smt2-nc-surface-mount-programming-module
libgpiod:
- Name: Bitbang GPIO
Description: Bitbang GPIO pins on Linux host.
URL: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
lpc-link2: lpc-link2:
- Name: lpc-link2 - Name: lpc-link2
@ -246,7 +273,8 @@ numato:
- Name: numato - Name: numato
Description: Embedded cable for Numato Systems Mimas-A7 board Description: Embedded cable for Numato Systems Mimas-A7 board
orbtrace: orbtrace:
- Name: orbtrace interface - Name: orbtrace interface
@ -261,13 +289,6 @@ papilio:
URL: https://papilio.cc/ URL: https://papilio.cc/
steppenprobe:
- Name: steppenprobe
Description: Open Source Hardware JTAG/SWD/UART/SWO interface board based on FTDI FT2232H
URL: https://github.com/diegoherranz/steppenprobe
remote-bitgang: remote-bitgang:
- Name: OpenOCD remote bitbang - Name: OpenOCD remote bitbang
@ -275,6 +296,13 @@ remote-bitgang:
URL: https://github.com/openocd-org/openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt URL: https://github.com/openocd-org/openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt
steppenprobe:
- Name: steppenprobe
Description: Open Source Hardware JTAG/SWD/UART/SWO interface board based on FTDI FT2232H
URL: https://github.com/diegoherranz/steppenprobe
tigard: tigard:
- Name: tigard - Name: tigard
@ -302,6 +330,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
@ -314,22 +349,3 @@ xvc-server:
- Name: Xilinx Virtual Cable (server side) - Name: Xilinx Virtual Cable (server side)
Description: Xilinx Virtual Cable (XVC) is a TCP/IP-based protocol that acts like a JTAG cable. Description: Xilinx Virtual Cable (XVC) is a TCP/IP-based protocol that acts like a JTAG cable.
URL: https://github.com/Xilinx/XilinxVirtualCable URL: https://github.com/Xilinx/XilinxVirtualCable
libgpiod:
- Name: Bitbang GPIO
Description: Bitbang GPIO pins on Linux host.
URL: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
jetson-nano-gpio:
- Name: Bitbang GPIO
Description: Bitbang GPIO pins on Jetson Nano Linux host. Use /dev/mem to have a faster clock.
URL: https://github.com/jwatte/jetson-gpio-example
ch347:
- Name: CH347
Description: CH347 is a USB HS bus converter with UART, I2C, SPI and JTAG interfaces
URL: https://github.com/wuxx/USB-HS-Bridge

View File

@ -13,6 +13,7 @@ GOWIN GW1N
* Sipeed Tang Nano 4K * Sipeed Tang Nano 4K
* Honeycomb * Honeycomb
* RUNBER * RUNBER
* BrisbaneSilicon BRS-100-GW1NR9
``.fs`` file is the default format generated by *Gowin IDE*, so nothing special must be done to generates this file. ``.fs`` file is the default format generated by *Gowin IDE*, so nothing special must be done to generates this file.
@ -37,12 +38,13 @@ where ``BOARD_NAME`` is:
* ``tangnano20k`` * ``tangnano20k``
* ``tangprimer20k`` * ``tangprimer20k``
* ``runber`` * ``runber``
* ``brs-100-gw1nr9``
Flash Flash
----- -----
.. ATTENTION:: .. ATTENTION::
Only with Trenz TEC0117 and runber. Only with Trenz TEC0117, runber and brs-100-gw1nr9.
with ``-f``, file load: with ``-f``, file load:
@ -54,6 +56,7 @@ where ``BOARD_NAME`` is:
* ``tec0117`` * ``tec0117``
* ``runber`` * ``runber``
* ``brs-100-gw1nr9``
It's possible to flash external SPI Flash (connected to MSPI) in bscan mode by using ``--external-flash`` instead of It's possible to flash external SPI Flash (connected to MSPI) in bscan mode by using ``--external-flash`` instead of
``-f``. ``-f``.

View File

@ -9,6 +9,7 @@ url="https://github.com/trabucayre/openFPGALoader"
license=('Apache-2.0') license=('Apache-2.0')
depends=( depends=(
"${MINGW_PACKAGE_PREFIX}-libftdi" "${MINGW_PACKAGE_PREFIX}-libftdi"
"${MINGW_PACKAGE_PREFIX}-hidapi"
"${MINGW_PACKAGE_PREFIX}-zlib" "${MINGW_PACKAGE_PREFIX}-zlib"
) )
makedepends=( makedepends=(

View File

@ -1,82 +0,0 @@
set parts [lindex $argv 0]
set project_name "spiOverJtag"
set build_path tmp_${parts}
file delete -force $build_path
# Project creation
set grade [dict create \
xc7a15tfgg484 -1 \
xc7a25tcpg238 -1 \
xc7a25tcsg325 -1 \
xc7a35tcpg236 -1 \
xc7a35tcsg324 -1 \
xc7a35tftg256 -1 \
xc7a50tcpg236 -2 \
xc7a50tcsg325 -2 \
xc7a75tfgg484 -2 \
xc7a100tfgg484 -2 \
xc7a200tsbg484 -1 \
xc7k70tfbg676 -1 \
xc7k160tffg676 -1 \
xc7k325tffg676 -1 \
xc7k325tffg900 -2 \
xc7s25csga225 -1 \
xc7s50csga324 -1 \
]
set pkg_name [dict create \
xc7a25tcpg238 xc7a_cpg238 \
xc7a25tcsg325 xc7a_csg325 \
xc7a35tcpg236 xc7a_cpg236 \
xc7a35tcsg324 xc7a_csg324 \
xc7a35tftg256 xc7a_ftg256 \
xc7a50tcpg236 xc7a_cpg236 \
xc7a50tcsg325 xc7a_csg325 \
xc7a15tfgg484 xc7a_fgg484 \
xc7a75tfgg484 xc7a_fgg484 \
xc7a100tfgg484 xc7a_fgg484 \
xc7a200tsbg484 xc7a_sbg484 \
xc7a200tfbg484 xc7a_fbg484 \
xc7k70tfbg676 xc7k_fbg676 \
xc7k160tffg676 xc7k_ffg676 \
xc7k325tffg676 xc7k_ffg676 \
xc7k325tffg900 xc7k_ffg900 \
xc7s25csga225 xc7s_csga225 \
xc7s50csga324 xc7s_csga324 \
]
set curr_grade [dict get $grade $parts]
set curr_pins [dict get $pkg_name $parts]
create_project $project_name $build_path -part ${parts}${curr_grade}
add_files -norecurse xilinx_spiOverJtag.vhd
add_files -norecurse -fileset constrs_1 constr_${curr_pins}.xdc
set_property VERILOG_DEFINE {TOOL_VIVADO} [current_fileset]
# set the current synth run
current_run -synthesis [get_runs synth_1]
reset_run synth_1
set obj [get_runs impl_1]
set_property AUTO_INCREMENTAL_CHECKPOINT 1 [get_runs impl_1]
set_property "needs_refresh" "1" $obj
# set the current impl run
current_run -implementation [get_runs impl_1]
puts "INFO: Project created: $project_name"
launch_runs synth_1 -jobs 4
wait_on_run synth_1
## do implementation
launch_runs impl_1 -jobs 4
wait_on_run impl_1
## make bit file
launch_runs impl_1 -jobs 4 -to_step write_bitstream
wait_on_run impl_1
exit

View File

@ -236,6 +236,7 @@ static std::map <std::string, target_board_t> board_list = {
JTAG_BOARD("spartanEdgeAccelBoard", "", "", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("spartanEdgeAccelBoard", "", "", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("spec45", "xc6slx45tfgg484", "", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("spec45", "xc6slx45tfgg484", "", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("spec150", "xc6slx150tfgg484", "", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("spec150", "xc6slx150tfgg484", "", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("sp701", "xc7s100fgga676", "digilent_ft4232", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("stlv7325", "xc7k325tffg676", "ft4232", SPI_FLASH, 0, 0, CABLE_MHZ(3)), JTAG_BOARD("stlv7325", "xc7k325tffg676", "ft4232", SPI_FLASH, 0, 0, CABLE_MHZ(3)),
JTAG_BOARD("tangconsole", "", "ft2232", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("tangconsole", "", "ft2232", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("tangnano", "", "ch552_jtag", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("tangnano", "", "ch552_jtag", SPI_FLASH, 0, 0, CABLE_DEFAULT),
@ -249,6 +250,7 @@ static std::map <std::string, target_board_t> board_list = {
JTAG_BOARD("te0712_8", "xc7a200tfbg484", "", SPI_FLASH, 0, 0, CABLE_MHZ(15)), JTAG_BOARD("te0712_8", "xc7a200tfbg484", "", SPI_FLASH, 0, 0, CABLE_MHZ(15)),
JTAG_BOARD("tec0117", "", "ft2232", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("tec0117", "", "ft2232", SPI_FLASH, 0, 0, CABLE_DEFAULT),
JTAG_BOARD("tec0330", "xc7vx330tffg1157", "", SPI_FLASH, 0, 0, CABLE_MHZ(15)), JTAG_BOARD("tec0330", "xc7vx330tffg1157", "", SPI_FLASH, 0, 0, CABLE_MHZ(15)),
JTAG_BOARD("brs-100-gw1nr9", "gw1nr-lv9qn88pc7/i6", "ft2232", SPI_FLASH, 0, 0, CABLE_DEFAULT),
SPI_BOARD("titanium_ti60_f225","efinix", "titanium", "efinix_spi_ft4232", SPI_BOARD("titanium_ti60_f225","efinix", "titanium", "efinix_spi_ft4232",
DBUS4, DBUS5, DBUS7, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT), DBUS4, DBUS5, DBUS7, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT),
JTAG_BOARD("titanium_ti60_f225_jtag", "ti60f225","efinix_jtag_ft4232", SPI_FLASH, 0, 0, CABLE_DEFAULT), JTAG_BOARD("titanium_ti60_f225_jtag", "ti60f225","efinix_jtag_ft4232", SPI_FLASH, 0, 0, CABLE_DEFAULT),

View File

@ -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) */
}; };
/*! /*!
@ -99,6 +100,7 @@ static std::map <std::string, cable_t> cable_list = {
{"dfu", CABLE_DEF(MODE_DFU, 0, 0 )}, {"dfu", CABLE_DEF(MODE_DFU, 0, 0 )},
{"digilent", FTDI_SER(0x0403, 0x6010, FTDI_INTF_A, 0xe8, 0xeb, 0x00, 0x60)}, {"digilent", FTDI_SER(0x0403, 0x6010, FTDI_INTF_A, 0xe8, 0xeb, 0x00, 0x60)},
{"digilent_b", FTDI_SER(0x0403, 0x6010, FTDI_INTF_B, 0xe8, 0xeb, 0x00, 0x60)}, {"digilent_b", FTDI_SER(0x0403, 0x6010, FTDI_INTF_B, 0xe8, 0xeb, 0x00, 0x60)},
{"digilent_ft4232", FTDI_SER(0x0403, 0x6011, FTDI_INTF_A, 0x08, 0x2B, 0x08, 0x0B)},
{"digilent_hs2", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0xe8, 0xeb, 0x00, 0x60)}, {"digilent_hs2", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0xe8, 0xeb, 0x00, 0x60)},
{"digilent_hs3", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0x88, 0x8B, 0x20, 0x30)}, {"digilent_hs3", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0x88, 0x8B, 0x20, 0x30)},
{"digilent_ad", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0x08, 0x0B, 0x80, 0x80)}, {"digilent_ad", FTDI_SER(0x0403, 0x6014, FTDI_INTF_A, 0x08, 0x0B, 0x80, 0x80)},
@ -136,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 )},

View File

@ -218,10 +218,13 @@ void Efinix::program(unsigned int offset, bool unprotect_flash)
throw std::runtime_error("device mismatch: " + device + " != " + target); throw std::runtime_error("device mismatch: " + device + " != " + target);
} }
} }
std::string mode = bit->getHeaderVal("mode"); std::map<std::string, std::string> hdr = bit->getHeader();
if (mode.find("passive") != std::string::npos) { if(hdr.find("mode") != hdr.end()) {
delete bit; std::string mode = bit->getHeaderVal("mode");
throw std::runtime_error("passive mode not supported for flash"); if (mode.find("passive") != std::string::npos) {
delete bit;
throw std::runtime_error("passive mode not supported for flash");
}
} }
} catch (std::runtime_error& e) { } catch (std::runtime_error& e) {
throw; throw;

View File

@ -245,13 +245,17 @@ static uint16_t esp_usb_target_chip_id = 0; /* not applicable for FPGA, they hav
/* end copy from openocd */ /* end copy from openocd */
esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid = ESPUSBJTAG_VID, int pid = ESPUSBJTAG_PID): esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose,
int vid = ESPUSBJTAG_VID, int pid = ESPUSBJTAG_PID,
uint8_t bus_addr = 0, uint8_t dev_addr = 0,
const std::string &serial = ""):
_verbose(verbose > 1), _verbose(verbose > 1),
dev_handle(NULL), usb_ctx(NULL), _tdi(0), _tms(0), dev_handle(NULL), usb_ctx(NULL), _tdi(0), _tms(0),
/* Default for emard firmware. */ /* Default for emard firmware. */
_esp_usb_jtag_caps(0x2000), _write_ep(0x02), _esp_usb_jtag_caps(0x2000), _write_ep(0x02),
_vid(ESPUSBJTAG_VID), _pid(ESPUSBJTAG_PID) _vid(ESPUSBJTAG_VID), _pid(ESPUSBJTAG_PID)
{ {
libusb_device **devs = NULL;
int ret; int ret;
char mess[256]; char mess[256];
@ -260,18 +264,61 @@ esp_usb_jtag::esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid = ESPUSBJTAG_
throw std::exception(); throw std::exception();
} }
dev_handle = libusb_open_device_with_vid_pid(usb_ctx, ssize_t cnt = libusb_get_device_list(usb_ctx, &devs);
_vid, _pid); if (cnt < 0) {
if (!dev_handle) { std::cerr << "esp_usb_jtag: libusb_get_device_list failed: "
_esp_usb_jtag_caps = 0x030A; << libusb_error_name(static_cast<int>(cnt)) << std::endl;
_write_ep = 0x03; libusb_exit(usb_ctx);
_pid = 0x1002; throw std::exception();
dev_handle = libusb_open_device_with_vid_pid(usb_ctx,
_vid, _pid);
} }
for (ssize_t i = 0; i < cnt; i++) {
libusb_device *dev = devs[i];
struct libusb_device_descriptor desc;
if (libusb_get_device_descriptor(dev, &desc) < 0)
continue;
if (desc.idVendor != vid || desc.idProduct != pid)
continue;
/* bus/device filter (only when both are user-supplied) */
if (bus_addr != 0 && dev_addr != 0 &&
(libusb_get_bus_number(dev) != bus_addr ||
libusb_get_device_address(dev) != dev_addr))
continue;
/* serial filter */
if (!serial.empty()) {
libusb_device_handle *probe = NULL;
if (libusb_open(dev, &probe) < 0)
continue;
unsigned char raw[256] = {0};
int n = 0;
if (desc.iSerialNumber)
n = libusb_get_string_descriptor_ascii(probe,
desc.iSerialNumber, raw, sizeof(raw));
libusb_close(probe);
if (n <= 0)
continue;
const std::string found(reinterpret_cast<char *>(raw), n);
if (found.find(serial) == std::string::npos)
continue;
}
if (libusb_open(dev, &dev_handle) == 0)
break;
dev_handle = NULL;
}
libusb_free_device_list(devs, 1);
if (!dev_handle) { if (!dev_handle) {
snprintf(mess, 256, "fails to open esp_usb_jtag device"); std::cerr << "fails to open esp_usb_jtag device vid:pid 0x"
printError(mess); << std::hex << vid << ":0x" << pid;
if (bus_addr || dev_addr)
std::cerr << " bus:dev " << std::dec << static_cast<int>(bus_addr)
<< ":" << static_cast<int>(dev_addr);
if (!serial.empty())
std::cerr << " serial '" << serial << "'";
std::cerr << std::endl;
libusb_exit(usb_ctx); libusb_exit(usb_ctx);
throw std::exception(); throw std::exception();
} }

View File

@ -12,6 +12,8 @@
#include <libusb.h> #include <libusb.h>
#include <string>
#include "jtagInterface.hpp" #include "jtagInterface.hpp"
/*! /*!
@ -23,7 +25,9 @@
class esp_usb_jtag : public JtagInterface { class esp_usb_jtag : public JtagInterface {
public: public:
esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid, int pid); esp_usb_jtag(uint32_t clkHZ, int8_t verbose, int vid, int pid,
uint8_t bus_addr, uint8_t dev_addr,
const std::string &serial);
virtual ~esp_usb_jtag(); virtual ~esp_usb_jtag();
int setClkFreq(uint32_t clkHZ) override; int setClkFreq(uint32_t clkHZ) override;

View File

@ -14,7 +14,7 @@
#include "display.hpp" #include "display.hpp"
#include "fx2_ll.hpp" #include "fx2_ll.hpp"
#include "ihexParser.hpp" #include "ihexParser.hpp"
#include "progressBar.hpp"
#define FX2_FIRM_LOAD 0xA0 #define FX2_FIRM_LOAD 0xA0
#define FX2_GCR_CPUCS 0xe600 #define FX2_GCR_CPUCS 0xe600
@ -42,27 +42,36 @@ FX2_ll::FX2_ll(uint16_t uninit_vid, uint16_t uninit_pid,
throw std::runtime_error("claim interface failed"); throw std::runtime_error("claim interface failed");
} }
/* load firmware */ /* load firmware */
load_firmware(firmware_path); if (!load_firmware(firmware_path)) {
libusb_close(dev_handle);
libusb_exit(usb_ctx);
throw std::runtime_error("fx2 firmware load failed");
}
close(); close();
reenum = true; reenum = true;
} }
} }
/* try to open an already init device /* try to open an already init device
* since fx2 may be not immediately ready * since fx2 may be not immediatly ready
* retry with a delay * retry with a delay
*/ */
ProgressBar progress("Waiting reload", 100, 50, false);
int timeout = 100; int timeout = 100;
do { do {
dev_handle = libusb_open_device_with_vid_pid(usb_ctx, dev_handle = libusb_open_device_with_vid_pid(usb_ctx,
vid, pid); vid, pid);
timeout--; timeout--;
progress.display(100-timeout);
if (!dev_handle) if (!dev_handle)
sleep(1); sleep(1);
} while (!dev_handle && timeout > 0 && reenum); } while (!dev_handle && timeout > 0 && reenum);
if (!dev_handle) if (!dev_handle) {
progress.fail();
throw std::runtime_error("FX2: fail to open device"); throw std::runtime_error("FX2: fail to open device");
}
progress.done();
ret = libusb_claim_interface(dev_handle, 0); ret = libusb_claim_interface(dev_handle, 0);
if (ret) { if (ret) {
@ -72,6 +81,18 @@ FX2_ll::FX2_ll(uint16_t uninit_vid, uint16_t uninit_pid,
} }
} }
bool FX2_ll::set_interface_alt_setting(const int if_num, const int alt_setting)
{
const int ret = libusb_set_interface_alt_setting(dev_handle, if_num,
alt_setting);
if (ret && ret != LIBUSB_ERROR_NOT_FOUND) {
printWarn("unable to select alternate interface 1: "
+ std::string(libusb_error_name(ret)));
return false;
}
return true;
}
/* destructor: close current device and /* destructor: close current device and
* destroy context * destroy context
*/ */
@ -134,11 +155,11 @@ int FX2_ll::read(uint8_t endpoint, uint8_t *buff, uint16_t len)
/* write len data using control /* write len data using control
*/ */
int FX2_ll::write_ctrl(uint8_t bRequest, uint16_t wValue, int FX2_ll::write_ctrl(uint8_t bRequest, uint16_t wValue,
uint8_t *buff, uint16_t len) uint8_t *buff, uint16_t len, uint16_t wIndex)
{ {
int ret = libusb_control_transfer(dev_handle, int ret = libusb_control_transfer(dev_handle,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
bRequest, wValue, 0x0000, buff, len, 100); bRequest, wValue, wIndex, buff, len, 100);
if (ret < 0) { if (ret < 0) {
printError("Unable to send control request: " printError("Unable to send control request: "
+ std::string(libusb_error_name(ret))); + std::string(libusb_error_name(ret)));
@ -150,11 +171,11 @@ int FX2_ll::write_ctrl(uint8_t bRequest, uint16_t wValue,
/* read len data using control /* read len data using control
*/ */
int FX2_ll::read_ctrl(uint8_t bRequest, uint16_t wValue, int FX2_ll::read_ctrl(uint8_t bRequest, uint16_t wValue,
uint8_t *buff, uint16_t len) uint8_t *buff, uint16_t len, uint16_t wIndex)
{ {
int ret = libusb_control_transfer(dev_handle, int ret = libusb_control_transfer(dev_handle,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
bRequest, wValue, 0x0000, buff, len, 100); bRequest, wValue, wIndex, buff, len, 100);
if (ret < 0) { if (ret < 0) {
printError("Unable to read control request: " printError("Unable to read control request: "
+ std::string(libusb_error_name(ret))); + std::string(libusb_error_name(ret)));
@ -177,6 +198,7 @@ bool FX2_ll::load_firmware(std::string firmware_path)
return false; return false;
/* load */ /* load */
std::vector<IhexParser::data_line_t> data = ihex.getDataArray(); std::vector<IhexParser::data_line_t> data = ihex.getDataArray();
ProgressBar progress("Loading firmware", data.size(), 50, false);
for (size_t i = 0; i < data.size(); i++) { for (size_t i = 0; i < data.size(); i++) {
IhexParser::data_line_t data_line = data[i]; IhexParser::data_line_t data_line = data[i];
@ -186,14 +208,16 @@ bool FX2_ll::load_firmware(std::string firmware_path)
while (toSend > 0) { while (toSend > 0) {
uint16_t xfer_len = (toSend > 64) ? 64: toSend; uint16_t xfer_len = (toSend > 64) ? 64: toSend;
if (!write_ctrl(FX2_FIRM_LOAD, addr, tx_buff, xfer_len)) { if (!write_ctrl(FX2_FIRM_LOAD, addr, tx_buff, xfer_len)) {
printError("load firmware failed\n"); progress.fail();
return false; return false;
} }
toSend -= xfer_len; toSend -= xfer_len;
tx_buff += xfer_len; tx_buff += xfer_len;
addr += xfer_len; addr += xfer_len;
} }
progress.display(i);
} }
progress.done();
/* unset reset */ /* unset reset */
if (!reset(0)) if (!reset(0))

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
/* /*
* Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com> * Copyright (c) 2021-2026 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/ */
#ifndef SRC_FX2_LL_HPP_ #ifndef SRC_FX2_LL_HPP_
@ -31,6 +31,14 @@ class FX2_ll {
uint16_t vid, uint16_t pid, const std::string &firmware_path); uint16_t vid, uint16_t pid, const std::string &firmware_path);
~FX2_ll(); ~FX2_ll();
/*!
* \brief libusb_set_interface_alt_setting wrapper
* \param[in] if_num: Interface number
* \param[in] alt_setting: alternate setting
* \return true on success
*/
bool set_interface_alt_setting(const int if_num, const int alt_setting);
/*! /*!
* \brief bulk write * \brief bulk write
* \param[in] endpoint: endpoint to use * \param[in] endpoint: endpoint to use
@ -53,20 +61,22 @@ class FX2_ll {
* \param[in] wValue * \param[in] wValue
* \param[in] buff: buffer to write * \param[in] buff: buffer to write
* \param[in] len: buffer length * \param[in] len: buffer length
* \param[in] wIndex: the index field for the setup packet
* \return false when transfer fails, true otherwise * \return false when transfer fails, true otherwise
*/ */
int write_ctrl(uint8_t bRequest, uint16_t wValue, int write_ctrl(uint8_t bRequest, uint16_t wValue,
uint8_t *buff, uint16_t len); uint8_t *buff, uint16_t len, uint16_t wIndex=0x0000);
/*! /*!
* \brief control write * \brief control write
* \param[in] bRequest * \param[in] bRequest
* \param[in] wValue * \param[in] wValue
* \param[in] buff: buffer to write * \param[in] buff: buffer to write
* \param[in] len: buffer length * \param[in] len: buffer length
* \param[in] wIndex: the index field for the setup packet
* \return false when transfer fails, true otherwise * \return false when transfer fails, true otherwise
*/ */
int read_ctrl(uint8_t bRequest, uint16_t wValue, int read_ctrl(uint8_t bRequest, uint16_t wValue,
uint8_t *buff, uint16_t len); uint8_t *buff, uint16_t len, uint16_t wIndex=0x0000);
private: private:
/*! /*!

View File

@ -436,7 +436,7 @@ void Gowin::checkCRC()
* is used, try to compare with this value * is used, try to compare with this value
*/ */
try { try {
std::string hdr = _fs->getHeaderVal("checkSum"); std::string hdr = _fs->getHeaderVal("CheckSum");
if (!hdr.empty()) { if (!hdr.empty()) {
if (ucode == strtol(hdr.c_str(), NULL, 16)) if (ucode == strtol(hdr.c_str(), NULL, 16))
goto success; goto success;

View File

@ -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
@ -162,7 +165,8 @@ Jtag::Jtag(const cable_t &cable, const jtag_pins_conf_t *pin_conf,
break; break;
case MODE_ESP: case MODE_ESP:
#ifdef ENABLE_ESP_USB #ifdef ENABLE_ESP_USB
_jtag = new esp_usb_jtag(clkHZ, verbose, 0x303a, 0x1001); _jtag = new esp_usb_jtag(clkHZ, verbose, 0x303a, 0x1001,
cable.bus_addr, cable.device_addr, serial);
#else #else
std::cerr << "Jtag: support for esp32s3 cable was not enabled at compile time" << std::endl; std::cerr << "Jtag: support for esp32s3 cable was not enabled at compile time" << std::endl;
throw std::exception(); throw std::exception();
@ -206,6 +210,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;

View File

@ -79,7 +79,7 @@ struct arguments {
std::string secondary_bit_file; std::string secondary_bit_file;
std::string device; std::string device;
std::string cable; std::string cable;
std::string ftdi_serial; std::string usb_serial_num;
int ftdi_channel; int ftdi_channel;
int status_pin; int status_pin;
uint32_t freq; uint32_t freq;
@ -264,13 +264,6 @@ int main(int argc, char **argv)
cable.config.interface = mapping[args.ftdi_channel]; cable.config.interface = mapping[args.ftdi_channel];
} }
if (!args.ftdi_serial.empty()) {
if (cable.type != MODE_FTDI_SERIAL && cable.type != MODE_FTDI_BITBANG){
printError("Error: FTDI serial param is for FTDI cables.");
return EXIT_FAILURE;
}
}
if (args.status_pin != -1) { if (args.status_pin != -1) {
if (cable.type != MODE_FTDI_SERIAL){ if (cable.type != MODE_FTDI_SERIAL){
printError("Error: FTDI status pin is for FTDI MPSSE cables."); printError("Error: FTDI status pin is for FTDI MPSSE cables.");
@ -279,6 +272,14 @@ int main(int argc, char **argv)
} }
#endif #endif
if (!args.usb_serial_num.empty()) {
if (cable.type != MODE_FTDI_SERIAL && cable.type != MODE_FTDI_BITBANG &&
cable.type != MODE_ESP){
printError("Error: usb-serial-num param is for FTDI and esp32s3 cables.");
return EXIT_FAILURE;
}
}
if (args.vid != 0) { if (args.vid != 0) {
printInfo("Cable VID overridden"); printInfo("Cable VID overridden");
cable.vid = args.vid; cable.vid = args.vid;
@ -389,7 +390,7 @@ int main(int argc, char **argv)
Jtag *jtag; Jtag *jtag;
try { try {
jtag = new Jtag(cable, &pins_config, args.device, args.ftdi_serial, jtag = new Jtag(cable, &pins_config, args.device, args.usb_serial_num,
args.freq, args.verbose, args.ip_adr, args.port, args.freq, args.verbose, args.ip_adr, args.port,
args.invert_read_edge, args.probe_firmware, args.invert_read_edge, args.probe_firmware,
args.user_misc_devs); args.user_misc_devs);
@ -667,7 +668,7 @@ int run_xvc_server(const struct arguments &args, const cable_t &cable,
try { try {
XVC_server *xvc = NULL; XVC_server *xvc = NULL;
xvc = new XVC_server(args.port, cable, pins_config, args.device, xvc = new XVC_server(args.port, cable, pins_config, args.device,
args.ftdi_serial, args.freq, args.verbose, args.ip_adr, args.usb_serial_num, args.freq, args.verbose, args.ip_adr,
args.invert_read_edge, args.probe_firmware); args.invert_read_edge, args.probe_firmware);
/* create connection */ /* create connection */
xvc->open_connection(); xvc->open_connection();
@ -889,6 +890,7 @@ int parse_opt(int argc, char **argv, struct arguments *args,
jtag_pins_conf_t *pins_config) jtag_pins_conf_t *pins_config)
{ {
std::string freqo; std::string freqo;
std::string ftdi_serial;
std::vector<std::string> pins, bus_dev_num; std::vector<std::string> pins, bus_dev_num;
bool verbose = false, quiet = false; bool verbose = false, quiet = false;
int8_t verbose_level = -2; int8_t verbose_level = -2;
@ -927,8 +929,10 @@ int parse_opt(int argc, char **argv, struct arguments *args,
("busdev-num", ("busdev-num",
"select a probe by it bus and device number (bus_num:device_addr)", "select a probe by it bus and device number (bus_num:device_addr)",
cxxopts::value<std::vector<std::string>>(bus_dev_num)) cxxopts::value<std::vector<std::string>>(bus_dev_num))
("ftdi-serial", "FTDI chip serial number", ("usb-serial-num", "USB iSerial (FTDI chip serial number or ESP32 iSerialNumber substring)",
cxxopts::value<std::string>(args->ftdi_serial)) cxxopts::value<std::string>(args->usb_serial_num))
("ftdi-serial", "FTDI chip serial number (Deprecated)",
cxxopts::value<std::string>(ftdi_serial))
("ftdi-channel", ("ftdi-channel",
"FTDI chip channel number (channels 0-3 map to A-D)", "FTDI chip channel number (channels 0-3 map to A-D)",
cxxopts::value<int>(args->ftdi_channel)) cxxopts::value<int>(args->ftdi_channel))
@ -982,7 +986,7 @@ int parse_opt(int argc, char **argv, struct arguments *args,
cxxopts::value<unsigned int>(args->offset)) cxxopts::value<unsigned int>(args->offset))
("pins", "pin config TDI:TDO:TCK:TMS or MOSI:MISO:SCK:CS[:HOLDN:WPN]", ("pins", "pin config TDI:TDO:TCK:TMS or MOSI:MISO:SCK:CS[:HOLDN:WPN]",
cxxopts::value<std::vector<std::string>>(pins)) cxxopts::value<std::vector<std::string>>(pins))
("probe-firmware", "firmware for JTAG probe (usbBlasterII)", ("probe-firmware", "firmware for JTAG probe (usbBlasterII/xilinxPlatformCableUsb)",
cxxopts::value<std::string>(args->probe_firmware)) cxxopts::value<std::string>(args->probe_firmware))
("protect-flash", "protect SPI flash area", ("protect-flash", "protect SPI flash area",
cxxopts::value<uint32_t>(args->protect_flash)) cxxopts::value<uint32_t>(args->protect_flash))
@ -1027,7 +1031,8 @@ int parse_opt(int argc, char **argv, struct arguments *args,
cxxopts::value<std::string>(args->read_register)) cxxopts::value<std::string>(args->read_register))
("user-flash", "User flash file (Gowin LittleBee FPGA only)", ("user-flash", "User flash file (Gowin LittleBee FPGA only)",
cxxopts::value<std::string>(args->user_flash)) cxxopts::value<std::string>(args->user_flash))
("V,Version", "Print program version"); ("V,version", "Print program version")
("Version", "Print program version (Deprecated)");
options.parse_positional({"bitstream"}); options.parse_positional({"bitstream"});
auto result = options.parse(argc, argv); auto result = options.parse(argc, argv);
@ -1055,7 +1060,7 @@ int parse_opt(int argc, char **argv, struct arguments *args,
args->verbose = verbose_level; args->verbose = verbose_level;
} }
if (result.count("Version")) { if (result.count("Version") || result.count("version")) {
std::cout << "openFPGALoader " << VERSION << std::endl; std::cout << "openFPGALoader " << VERSION << std::endl;
return 1; return 1;
} }
@ -1133,6 +1138,14 @@ int parse_opt(int argc, char **argv, struct arguments *args,
} }
} }
if (result.count("ftdi-serial")) {
if (result.count("usb-serial-num")) {
printError("Error: ftdi-serial and usb-serial-num can't be used at the same time.");
return -1;
}
args->usb_serial_num = ftdi_serial;
}
if (result.count("busdev-num")) { if (result.count("busdev-num")) {
if (bus_dev_num.size() != 2) { if (bus_dev_num.size() != 2) {
printError("Error: busdev-num must be xx:yy"); printError("Error: busdev-num must be xx:yy");

View File

@ -307,6 +307,7 @@ static std::map <uint32_t, fpga_model> fpga_list = {
{0x02e050dd, {"altera", "arria 10", "SX660", 10}}, {0x02e050dd, {"altera", "arria 10", "SX660", 10}},
/* Altera Agilex 3 */ /* Altera Agilex 3 */
{0x4361b0dd, {"altera", "agilex 3", "A3CY100", 10}},
{0x436db0dd, {"altera", "agilex 3", "A3CZ135", 10}}, {0x436db0dd, {"altera", "agilex 3", "A3CZ135", 10}},
/* Altera Agilex 5 */ /* Altera Agilex 5 */

View File

@ -76,25 +76,27 @@ RemoteBitbang_client::~RemoteBitbang_client()
} }
int RemoteBitbang_client::writeTMS(const uint8_t *tms, uint32_t len, int RemoteBitbang_client::writeTMS(const uint8_t *tms, uint32_t len,
bool flush_buffer, __attribute__((unused)) const uint8_t tdi) bool flush_buffer, const uint8_t tdi)
{ {
// empty buffer // empty buffer
// if asked flush // if asked flush
if (len == 0) if (len == 0)
return ((flush_buffer) ? flush() : 0); return ((flush_buffer) ? flush() : 0);
_last_tdi = (tdi) ? TDI_BIT : 0;
uint8_t base_v = '0' + _last_tdi; uint8_t base_v = '0' + _last_tdi;
for (uint32_t pos = 0; pos < len; pos++) { for (uint32_t pos = 0; pos < len; pos++) {
// buffer full -> write // buffer full -> write
if (_num_bytes == _buffer_size) if ((_num_bytes + 2) >= _buffer_size)
ll_write(NULL); flush();
_last_tms = (tms[pos >> 3] & (1 << (pos & 0x07))) ? TMS_BIT : 0; _last_tms = (tms[pos >> 3] & (1 << (pos & 0x07))) ? TMS_BIT : 0;
_xfer_buf[_num_bytes++] = base_v + _last_tms; _xfer_buf[_num_bytes++] = base_v + _last_tms;
_xfer_buf[_num_bytes++] = base_v + _last_tms + TCK_BIT; _xfer_buf[_num_bytes++] = base_v + _last_tms + TCK_BIT;
} }
// flush where it's asked or if the buffer is full // flush where it's asked or if the buffer is full
if (flush_buffer || _num_bytes == _buffer_size * 8) if (flush_buffer || _num_bytes == _buffer_size)
return flush(); return flush();
return len; return len;
} }
@ -107,9 +109,11 @@ int RemoteBitbang_client::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len,
uint8_t base_v = '0' + _last_tms; uint8_t base_v = '0' + _last_tms;
for (uint32_t pos = 0; pos < len; pos++) { for (uint32_t pos = 0; pos < len; pos++) {
if (_num_bytes == _buffer_size) const uint32_t pos_byte = (pos >> 3);
ll_write(NULL); // NULL because _num_bytes is always 0 when read const uint32_t pos_bit = (1 << (pos & 0x07));
_last_tdi = (tx[pos >> 3] & (1 << (pos & 0x07))) ? TDI_BIT : 0; if ((_num_bytes + 2) >= _buffer_size)
flush();
_last_tdi = (tx[pos_byte] & pos_bit) ? TDI_BIT : 0;
if (end && pos == len - 1) { if (end && pos == len - 1) {
_last_tms = TMS_BIT; _last_tms = TMS_BIT;
base_v = '0' + _last_tms; base_v = '0' + _last_tms;
@ -120,9 +124,9 @@ int RemoteBitbang_client::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len,
uint8_t tdo; uint8_t tdo;
ll_write(&tdo); ll_write(&tdo);
if (tdo == '1') if (tdo == '1')
rx[pos >> 3] |= (1 << (pos & 0x07)); rx[pos_byte] |= pos_bit;
else else
rx[pos >> 3] &= ~(1 << (pos & 0x07)); rx[pos_byte] &= ~pos_bit;
} }
} }
@ -130,30 +134,23 @@ int RemoteBitbang_client::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len,
} }
// toggle clk with constant TDI and TMS. // toggle clk with constant TDI and TMS.
int RemoteBitbang_client::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) int RemoteBitbang_client::toggleClk([[maybe_unused]] uint8_t tms,
[[maybe_unused]] uint8_t tdi, uint32_t clk_len)
{ {
// nothing to do // nothing to do
if (clk_len == 0) if (clk_len == 0)
return 0; return 0;
if (_num_bytes != 0)
flush();
_last_tms = tms; const uint8_t val = (_last_tms | _last_tdi);
_last_tdi = tdi;
uint8_t val = (_last_tms | _last_tdi);
// flush buffer before starting
if (_num_bytes != 0)
flush();
for (uint32_t len = 0; len < clk_len; len++) { for (uint32_t len = 0; len < clk_len; len++) {
if (len == _num_bytes) if ((_num_bytes + 2) >= _buffer_size)
ll_write(NULL); flush();
_xfer_buf[_num_bytes++] = '0' + val; _xfer_buf[_num_bytes++] = '0' + val;
_xfer_buf[_num_bytes++] = '0' + (val | TCK_BIT); _xfer_buf[_num_bytes++] = '0' + (val | TCK_BIT);
} }
ll_write(NULL); flush();
return clk_len; return clk_len;
} }

View File

@ -49,8 +49,8 @@ UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
#else #else
throw std::runtime_error("usb-blasterI: Not build"); throw std::runtime_error("usb-blasterI: Not build");
#endif #endif
#ifdef ENABLE_USB_BLASTERII
else if (cable.pid == 0x6810 || cable.pid == 0x6010) else if (cable.pid == 0x6810 || cable.pid == 0x6010)
#ifdef ENABLE_USB_BLASTERII
ll_driver = new UsbBlasterII(firmware_path); ll_driver = new UsbBlasterII(firmware_path);
#else #else
throw std::runtime_error("usb-blasterII: Not build"); throw std::runtime_error("usb-blasterII: Not build");
@ -459,7 +459,7 @@ UsbBlasterII::UsbBlasterII(const std::string &firmware_path)
else else
fpath = firmware_path; fpath = firmware_path;
fx2 = new FX2_ll(0x09fb, 0x6810, 0x09fb, 0x6010, fpath); fx2 = new FX2_ll(0x09fb, 0x6810, 0x09fb, 0x6010, fpath);
if (!fx2->read_ctrl(0x94, 0, buf, 5)) { if (!fx2->read_ctrl(0x94, 0, buf, 5, 0x0000)) {
throw std::runtime_error("Unable to read firmware version."); throw std::runtime_error("Unable to read firmware version.");
} }
printInfo("USB-Blaster II firmware version: " + std::string((char *)buf)); printInfo("USB-Blaster II firmware version: " + std::string((char *)buf));

View File

@ -659,7 +659,6 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash)
/* Check for BPI flash boards */ /* Check for BPI flash boards */
if (_is_bpi_board) { if (_is_bpi_board) {
program_bpi(bit, offset); program_bpi(bit, offset);
reset();
} else { } else {
if (_flash_chips & PRIMARY_FLASH) { if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH); select_flash_chip(PRIMARY_FLASH);
@ -669,8 +668,6 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash)
select_flash_chip(SECONDARY_FLASH); select_flash_chip(SECONDARY_FLASH);
program_spi(secondary_bit, _secondary_file_extension, offset, unprotect_flash); program_spi(secondary_bit, _secondary_file_extension, offset, unprotect_flash);
} }
reset();
} }
} else { } else {
if (_fpga_family == SPARTAN3_FAMILY) if (_fpga_family == SPARTAN3_FAMILY)
@ -810,6 +807,9 @@ void Xilinx::program_bpi(ConfigBitstreamParser *bit, unsigned int offset)
throw std::runtime_error("BPI flash programming failed"); throw std::runtime_error("BPI flash programming failed");
} }
/* Reset the board if skip_reset is not set */
post_flash_access();
printInfo("BPI flash programming complete"); printInfo("BPI flash programming complete");
} }
@ -1277,6 +1277,18 @@ bool Xilinx::dumpFlash(uint32_t base_addr, uint32_t len)
bool Xilinx::detect_flash() bool Xilinx::detect_flash()
{ {
if (_is_bpi_board) {
if (!_bpi_flash) {
if (!load_bpi_bridge())
return false;
}
if (!_bpi_flash->detect()) {
printError("BPI flash detection failed");
return false;
}
return post_flash_access();
}
if (_flash_chips & PRIMARY_FLASH) { if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH); select_flash_chip(PRIMARY_FLASH);
if (!FlashInterface::detect_flash()) if (!FlashInterface::detect_flash())

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_