From 7432d493911bc23cb1718cb27c3cf8797ac9983e Mon Sep 17 00:00:00 2001 From: Jernej Volk Date: Sun, 18 Jan 2026 21:24:07 +0100 Subject: [PATCH 01/13] spiFlashdb: Added MX25V8035F NOR flash chip --- src/spiFlashdb.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/spiFlashdb.hpp b/src/spiFlashdb.hpp index 49e8207..2e21fc2 100644 --- a/src/spiFlashdb.hpp +++ b/src/spiFlashdb.hpp @@ -585,6 +585,23 @@ static std::map flash_list = { .quad_mask = 0, .global_lock = false, }}, + {0xc22314, { + /* https://datasheet4u.com/pdf-down/M/X/2/MX25V8035F-MACRONIX.pdf */ + .manufacturer = "Macronix", + .model = "MX25V8035F", + .nr_sector = 16, + .sector_erase = true, + .subsector_erase = true, + .has_extended = false, + .tb_otp = true, + .tb_offset = (1 << 3), + .tb_register = CONFR, + .bp_len = 4, + .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 5)}, + .quad_register = STATR, + .quad_mask = (1 << 6), + .global_lock = false, + }}, {0xef4014, { /* https://cdn-shop.adafruit.com/datasheets/W25Q80BV.pdf */ .manufacturer = "Winbond", From 25ad02bb5efd83abbcbcf568a81a5888096f71ec Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Jan 2026 14:15:02 +0100 Subject: [PATCH 02/13] src/part.hpp: Add missing Xilinx UltraScale+ device IDCODEs. Extend fpga_list with additional UltraScale+ parts (Artix/Kintex/Virtex). Source: - https://review.openocd.org/c/openocd/+/7716 - https://sourceforge.net/p/openocd/mailman/openocd-devel/thread/20230525234809.B6A131B2%40openocd.org/ --- src/part.hpp | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/part.hpp b/src/part.hpp index d512a76..196955c 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -114,16 +114,46 @@ static std::map fpga_list = { {0x03842093, {"xilinx", "virtexus", "xcvu095", 6}}, /* Xilinx Ultrascale+ / Artix */ - {0x04AC2093, {"xilinx", "artixusp", "xcau15p", 6}}, - {0x04A64093, {"xilinx", "artixusp", "xcau25p", 6}}, + {0x04AC4033, {"xilinx", "artixusp", "xcau10p", 6}}, + {0x04AC4093, {"xilinx", "artixusp", "xcau10p", 6}}, + {0x04AC2093, {"xilinx", "artixusp", "xcau15p", 6}}, + {0x04A65093, {"xilinx", "artixusp", "xcau20p", 6}}, + {0x04A64093, {"xilinx", "artixusp", "xcau25p", 6}}, /* Xilinx Ultrascale+ / Kintex */ - {0x04a63093, {"xilinx", "kintexusp", "xcku3p", 6}}, - {0x04a62093, {"xilinx", "kintexusp", "xcku5p", 6}}, + {0x04A63093, {"xilinx", "kintexusp", "xcku3p", 6}}, + {0x04A62093, {"xilinx", "kintexusp", "xcku5p", 6}}, + {0x0484A093, {"xilinx", "kintexusp", "xcku9p", 6}}, + {0x04A4E093, {"xilinx", "kintexusp", "xcku11p", 6}}, + {0x04A51093, {"xilinx", "kintexusp", "xcku11p", 6}}, + {0x04A52093, {"xilinx", "kintexusp", "xcku13p", 6}}, + {0x04A56093, {"xilinx", "kintexusp", "xcku15p", 6}}, + {0x04A59093, {"xilinx", "kintexusp", "xcku15p", 6}}, + {0x04ACF093, {"xilinx", "kintexusp", "xcku19p", 6}}, + {0x04AD3093, {"xilinx", "kintexusp", "xcku19p", 6}}, /* Xilinx Ultrascale+ / Virtex */ - {0x04b31093, {"xilinx", "virtexusp", "xcvu9p", 18}}, - {0x14b79093, {"xilinx", "virtexusp", "xcvu37p", 18}}, + {0x04AEA093, {"xilinx", "virtexusp", "xcvu2p", 6}}, + {0x04B39093, {"xilinx", "virtexusp", "xcvu3p", 6}}, + {0x04B3D093, {"xilinx", "virtexusp", "xcvu3p", 6}}, + + {0x04B2B093, {"xilinx", "virtexusp", "xcvu5p", 12}}, + {0x04B2F093, {"xilinx", "virtexusp", "xcvu5p", 12}}, + {0x04B29093, {"xilinx", "virtexusp", "xcvu7p", 12}}, + {0x04B2D093, {"xilinx", "virtexusp", "xcvu7p", 12}}, + + {0x04B31093, {"xilinx", "virtexusp", "xcvu9p", 18}}, + {0x04B35093, {"xilinx", "virtexusp", "xcvu9p", 18}}, + {0x14B79093, {"xilinx", "virtexusp", "xcvu37p", 18}}, + {0x04B49093, {"xilinx", "virtexusp", "xcvu11p", 18}}, + {0x04B4F093, {"xilinx", "virtexusp", "xcvu11p", 18}}, + + {0x04B51093, {"xilinx", "virtexusp", "xcvu13p", 24}}, + {0x04B55093, {"xilinx", "virtexusp", "xcvu13p", 24}}, + + {0x04BA3093, {"xilinx", "virtexusp", "xcvu15p", 24}}, + {0x04BA1093, {"xilinx", "virtexusp", "xcvu19p", 24}}, + {0x04BA5093, {"xilinx", "virtexusp", "xcvu19p", 24}}, /* Xilinx Ultrascale+ / Spartan */ {0x04e80093, {"xilinx", "spartanusp", "xcsu35p", 6}}, From 87bf3c0f74c3a6a97732c58b0908d04dad6df2b1 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Jan 2026 14:37:41 +0100 Subject: [PATCH 03/13] src/part.hpp: Add Xilinx Spartan UltraScale+ devices. Extend fpga_list with Spartan UltraScale+ JTAG IDCODEs. Source: - https://docs.amd.com/r/en-US/ug860-spartan-configuration/Spartan-UltraScale-Devices-and-JTAG-IDCODEs --- src/part.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/part.hpp b/src/part.hpp index 196955c..3c1492a 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -156,7 +156,16 @@ static std::map fpga_list = { {0x04BA5093, {"xilinx", "virtexusp", "xcvu19p", 24}}, /* Xilinx Ultrascale+ / Spartan */ - {0x04e80093, {"xilinx", "spartanusp", "xcsu35p", 6}}, + {0x04E81093, {"xilinx", "spartanusp", "xcsu10p", 6}}, + {0x04E82093, {"xilinx", "spartanusp", "xcsu25p", 6}}, + {0x04E80093, {"xilinx", "spartanusp", "xcsu35p", 6}}, + {0x04EB1093, {"xilinx", "spartanusp", "xcsu45p", 6}}, + {0x04E90093, {"xilinx", "spartanusp", "xcsu55p", 6}}, + {0x04EB2093, {"xilinx", "spartanusp", "xcsu60p", 6}}, + {0x04E99093, {"xilinx", "spartanusp", "xcsu65p", 6}}, + {0x04E98093, {"xilinx", "spartanusp", "xcsu100p", 6}}, + {0x04EA1093, {"xilinx", "spartanusp", "xcsu150p", 6}}, + {0x04EA0093, {"xilinx", "spartanusp", "xcsu200p", 6}}, /* Xilinx Ultrascale+ / ZynqMP */ /* When powering a zynq ultrascale+ MPSoC, PL Tap and ARM dap From 4767ec2ce2cc7a673a8143a68f0030cf33f9adbe Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 20 Jan 2026 14:52:04 +0100 Subject: [PATCH 04/13] src/part.hpp: Add missing Xilinx 7-series IDCODEs. Add missing 7-series entries based on UG470 v1.17 Table 1-1. Source: - AMD UG470 (7 Series FPGAs Configuration User Guide), v1.17 (2023-12-05), Table 1-1 https://docs.amd.com/v/u/en-US/ug470_7Series_Config --- src/part.hpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/part.hpp b/src/part.hpp index 3c1492a..c11d8cb 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -72,11 +72,13 @@ static std::map fpga_list = { {0x037c4093, {"xilinx", "spartan7", "xc7s25", 6}}, {0x0362f093, {"xilinx", "spartan7", "xc7s50", 6}}, {0x037c8093, {"xilinx", "spartan7", "xc7s75", 6}}, + {0x037c7093, {"xilinx", "spartan7", "xc7s100", 6}}, /* Xilinx Virtex6 */ {0x8424a093, {"xilinx", "virtex6", "xc6vlx130t", 10}}, /* Xilinx 7-Series / Artix7 */ + {0x037c3093, {"xilinx", "artix a7 12t", "xc7a12t", 6}}, {0x0362e093, {"xilinx", "artix a7 15t", "xc7a15", 6}}, {0x037c2093, {"xilinx", "artix a7 25t", "xc7a25", 6}}, {0x0362D093, {"xilinx", "artix a7 35t", "xc7a35", 6}}, @@ -89,13 +91,26 @@ static std::map fpga_list = { {0x03647093, {"xilinx", "kintex7", "xc7k70t", 6}}, {0x0364c093, {"xilinx", "kintex7", "xc7k160t", 6}}, {0x03651093, {"xilinx", "kintex7", "xc7k325t", 6}}, + {0x03747093, {"xilinx", "kintex7", "xc7k355t", 6}}, {0x03656093, {"xilinx", "kintex7", "xc7k410t", 6}}, {0x23752093, {"xilinx", "kintex7", "xc7k420t", 6}}, {0x23751093, {"xilinx", "kintex7", "xc7k480t", 6}}, /* Xilinx 7-Series / Virtex7 */ - {0x03667093, {"xilinx", "virtex7", "xc7vx330t", 6}}, - {0x33691093, {"xilinx", "virtex7", "xc7vx690t", 6}}, + {0x03671093, {"xilinx", "virtex7", "xc7v585t", 6}}, + {0x03667093, {"xilinx", "virtex7", "xc7vx330t", 6}}, + {0x03682093, {"xilinx", "virtex7", "xc7vx415t", 6}}, + {0x03687093, {"xilinx", "virtex7", "xc7vx485t", 6}}, + {0x03692093, {"xilinx", "virtex7", "xc7vx550t", 6}}, + {0x33691093, {"xilinx", "virtex7", "xc7vx690t", 6}}, + {0x03696093, {"xilinx", "virtex7", "xc7vx980t", 6}}, + + {0x036d9093, {"xilinx", "virtex7", "xc7vh580t", 22}}, + + {0x036b3093, {"xilinx", "virtex7", "xc7v2000t", 24}}, + {0x036d5093, {"xilinx", "virtex7", "xc7vx1140t", 24}}, + + {0x036db093, {"xilinx", "virtex7", "xc7vh870t", 38}}, /* Xilinx 7-Series / Zynq */ {0x03722093, {"xilinx", "zynq", "xc7z010", 6}}, From ecebc400049f38e6b68bcd34aeaed14bf9061484 Mon Sep 17 00:00:00 2001 From: atmgnd Date: Wed, 21 Jan 2026 21:38:30 +0800 Subject: [PATCH 05/13] Fix build with -DENABLE_FTDI_BASED_CABLE=off; fix potential array out-of-bounds access (#616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix build without FTDI cable Signed-off-by: Qi Zhou * Fix potential array out‑of‑bounds access Signed-off-by: Qi Zhou --------- Signed-off-by: Qi Zhou Co-authored-by: Qi Zhou --- src/ch347jtag.cpp | 15 +++++++++------ src/main.cpp | 8 +++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ch347jtag.cpp b/src/ch347jtag.cpp index ff2f7ab..8f5c14f 100644 --- a/src/ch347jtag.cpp +++ b/src/ch347jtag.cpp @@ -249,14 +249,17 @@ int CH347Jtag::_setClkFreq(uint32_t clkHZ) uint32_t *ptr = _is_largerPack ? speed_clock_larger_pack : speed_clock_standard_pack; int size = (_is_largerPack?sizeof(speed_clock_larger_pack):sizeof(speed_clock_standard_pack)) / sizeof(uint32_t); for (int i = 0; i < size; ++i) { - if (clkHZ > ptr[i] && clkHZ <= ptr[i+1]){ - setClk_index = i + 1; - } + if (clkHZ >= ptr[i] ){ + setClk_index = i; + } else { + break; + } } if (setClk(setClk_index)) { printError("failed to set clock rate"); return 0; } + _clkHZ = ptr[setClk_index]; char mess[256]; snprintf(mess, 256, "JTAG TCK frequency set to %.3f MHz\n\n", (double)ptr[setClk_index] / MHZ(1)); printInfo(mess); @@ -333,7 +336,7 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len) ptr = obuf; } } - return EXIT_SUCCESS; + return len; } int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) @@ -381,7 +384,7 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) } unsigned actual_length; if (bits == 0) - return EXIT_SUCCESS; + return len; cmd = (rx) ? CMD_BITS_WR : CMD_BITS_WO; if (get_obuf_length() < (int)(4 + bits * 2)) { flush(); @@ -427,5 +430,5 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) *rptr &= ~(0x01 << i); } } - return EXIT_SUCCESS; + return len; } diff --git a/src/main.cpp b/src/main.cpp index 4aca9f6..be2a440 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,9 @@ #include "anlogic.hpp" #include "board.hpp" #include "cable.hpp" +#ifdef USE_LIBFTDI #include "colognechip.hpp" +#endif #include "common.hpp" #include "cxxopts.hpp" #include "device.hpp" @@ -25,12 +27,16 @@ #include "dfu.hpp" #endif #include "display.hpp" +#ifdef USE_LIBFTDI #include "efinix.hpp" #include "ftdispi.hpp" +#endif #include "gowin.hpp" -#include "ice40.hpp" #include "lattice.hpp" +#ifdef USE_LIBFTDI +#include "ice40.hpp" #include "latticeSSPI.hpp" +#endif #ifdef ENABLE_USB_SCAN #include "libusb_ll.hpp" #endif From 65ac4b24a347379f336c7745f0649f7db6586e2e Mon Sep 17 00:00:00 2001 From: fisherdog4 <150569332+fisherdog4@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:50:19 -0500 Subject: [PATCH 06/13] Add xc7z007s --- src/part.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/part.hpp b/src/part.hpp index c11d8cb..7d86411 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -113,6 +113,7 @@ static std::map fpga_list = { {0x036db093, {"xilinx", "virtex7", "xc7vh870t", 38}}, /* Xilinx 7-Series / Zynq */ + {0x13723093, {"xilinx", "zynq", "xc7z007s", 6}}, {0x03722093, {"xilinx", "zynq", "xc7z010", 6}}, {0x03727093, {"xilinx", "zynq", "xc7z020", 6}}, {0x1372c093, {"xilinx", "zynq", "xc7z030", 6}}, From fad4a28103d53a30f1946be2ea66664d89c75e6a Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Thu, 22 Jan 2026 10:54:24 +0100 Subject: [PATCH 07/13] CMakeLists.txt: try to uses find_package to detect the libFTDI and keep pkg_config way as backward compat --- CMakeLists.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1044071..10af6ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,11 +114,23 @@ add_definitions(-DISE_DIR=\"${ISE_PATH}\") # Dependencies check/search #################################################################################################### +if (USE_LIBFTDI) + # Try to find the LibFTDI1 with cmake + find_package(LibFTDI1 QUIET) +endif() + if (USE_PKGCONFIG) find_package(PkgConfig REQUIRED) + # Backward compat when the libftdi is not found + # by using cmake if (USE_LIBFTDI) - pkg_check_modules(LIBFTDI REQUIRED libftdi1) + if (NOT LIBFTDI_FOUND) + pkg_check_modules(LIBFTDI REQUIRED libftdi1) + string(REPLACE "." ";" VERSION_LIST ${LIBFTDI_VERSION}) + list(GET VERSION_LIST 0 LIBFTDI_VERSION_MAJOR) + list(GET VERSION_LIST 1 LIBFTDI_VERSION_MINOR) + endif() else() set(LIBFTDI_LIBRARY_DIRS "") set(LIBFTDI_INCLUDE_DIRS "") @@ -604,9 +616,6 @@ endif() if (USE_LIBFTDI) # libftdi < 1.4 as no usb_addr # libftdi >= 1.5 as purge_buffer obsolete - string(REPLACE "." ";" VERSION_LIST ${LIBFTDI_VERSION}) - list(GET VERSION_LIST 0 LIBFTDI_VERSION_MAJOR) - list(GET VERSION_LIST 1 LIBFTDI_VERSION_MINOR) math(EXPR FTDI_VAL "${LIBFTDI_VERSION_MAJOR} * 100 + ${LIBFTDI_VERSION_MINOR}") add_definitions(-DFTDI_VERSION=${FTDI_VAL}) endif() From aaad826635a5b51a707309f45d5fd403b4974bb4 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Sat, 24 Jan 2026 17:23:38 +0100 Subject: [PATCH 08/13] CMakeLists.txt: ihexParser must be built only when USE_FX2_LL is set --- CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10af6ef..ea7f19b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,6 @@ list(APPEND OPENFPGALOADER_SOURCE src/anlogicBitParser.cpp src/bitparser.cpp src/fsparser.cpp - src/ihexParser.cpp src/mcsParser.cpp src/pofParser.cpp src/rawParser.cpp @@ -232,7 +231,6 @@ list(APPEND OPENFPGALOADER_HEADERS src/anlogicBitParser.hpp src/bitparser.hpp src/fsparser.hpp - src/ihexParser.hpp src/mcsParser.hpp src/pofParser.hpp src/rawParser.hpp @@ -447,8 +445,8 @@ list(APPEND OPENFPGALOADER_HEADERS src/libusb_ll.hpp) endif() if (USE_FX2_LL) -list(APPEND OPENFPGALOADER_SOURCE src/fx2_ll.cpp) -list(APPEND OPENFPGALOADER_HEADERS src/fx2_ll.hpp) +list(APPEND OPENFPGALOADER_SOURCE src/fx2_ll.cpp src/ihexParser.cpp) +list(APPEND OPENFPGALOADER_HEADERS src/fx2_ll.hpp src/ihexParser.hpp) endif() add_executable(openFPGALoader From fb1768a175195a46a27fbbc1415e0ac43b824851 Mon Sep 17 00:00:00 2001 From: rkebelj Date: Wed, 28 Jan 2026 22:13:05 +0100 Subject: [PATCH 09/13] xilinx: Fixed infinite loop in bit files smaller than 100 --- src/xilinx.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xilinx.cpp b/src/xilinx.cpp index a1fe30d..d831cd9 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -843,6 +843,9 @@ void Xilinx::program_mem(ConfigBitstreamParser *bitfile) Jtag::tapState_t tx_end; int burst_len = byte_length / 100; + if (burst_len == 0) + burst_len = byte_length; + ProgressBar progress("Load SRAM", byte_length, 50, _quiet); for (int i=0; i < byte_length; i+=burst_len) { From b3a8ea03d50b936fe61eac3a3a23c869bdf18873 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Thu, 29 Jan 2026 14:43:35 +0100 Subject: [PATCH 10/13] xilinx.cpp: nitpick --- src/xilinx.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/xilinx.cpp b/src/xilinx.cpp index d831cd9..43f9fad 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -841,10 +841,7 @@ void Xilinx::program_mem(ConfigBitstreamParser *bitfile) const uint8_t *data = bitfile->getData(); int tx_len; Jtag::tapState_t tx_end; - int burst_len = byte_length / 100; - - if (burst_len == 0) - burst_len = byte_length; + const int burst_len = (byte_length < 100) ? byte_length : byte_length / 100; ProgressBar progress("Load SRAM", byte_length, 50, _quiet); From cc1dc3c86847aad00dc4e650f94328809d30f0a8 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 28 Jan 2026 14:52:41 +0100 Subject: [PATCH 11/13] CMakeLists.txt,main: added options to enable/disable vendors drivers --- CMakeLists.txt | 231 +++++++++++++++++++++++++++++++++++-------------- src/main.cpp | 84 ++++++++++++++++-- 2 files changed, 244 insertions(+), 71 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea7f19b..12c68e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,23 @@ else() set(ENABLE_XILINX_VIRTUAL_CABLE OFF) endif() +#################################################################################################### +# VENDORS Options +#################################################################################################### + +# set all vendor on by default +option(ENABLE_VENDORS_ALL "Enable all vendors support" ON) + +option(ENABLE_ALTERA_SUPPORT "enable Altera FPGAs Support" ${ENABLE_VENDORS_ALL}) +option(ENABLE_ANLOGIC_SUPPORT "enable Anlogic FPGAs Support" ${ENABLE_VENDORS_ALL}) +option(ENABLE_COLOGNECHIP_SUPPORT "enable CologneChip FPGAs Support (requires libftdi)" ${ENABLE_VENDORS_ALL}) +option(ENABLE_EFINIX_SUPPORT "enable Efinix FPGAs Support (requires libftdi)" ${ENABLE_VENDORS_ALL}) +option(ENABLE_GOWIN_SUPPORT "enable Gowin FPGAs Support" ${ENABLE_VENDORS_ALL}) +option(ENABLE_ICE40_SUPPORT "enable Ice40 FPGAs Support (requires libftdi)" ${ENABLE_VENDORS_ALL}) +option(ENABLE_LATTICE_SUPPORT "enable Lattice FPGAs Support" ${ENABLE_VENDORS_ALL}) +option(ENABLE_LATTICESSPI_SUPPORT "enable Lattice FPGAs Support (SSPI / requires libftdi)" ${ENABLE_VENDORS_ALL}) +option(ENABLE_XILINX_SUPPORT "enable Xilinx FPGAs Support" ${ENABLE_VENDORS_ALL}) + #################################################################################################### # Variables #################################################################################################### @@ -146,7 +163,7 @@ if (USE_PKGCONFIG) endif(USE_LIBUSB) if(ENABLE_CMSISDAP) - pkg_check_modules(HIDAPI hidapi-libusb) + pkg_check_modules(HIDAPI hidapi-libusb) # if libusb not found try with hidraw if (NOT HIDAPI_FOUND) pkg_check_modules(HIDAPI hidapi-hidraw) @@ -155,6 +172,7 @@ if (USE_PKGCONFIG) pkg_check_modules(HIDAPI hidapi) endif() endif() + # zlib support (gzip) pkg_check_modules(ZLIB zlib) if (NOT ZLIB_FOUND) @@ -217,44 +235,33 @@ set(OPENFPGALOADER_HEADERS # =========================== # Parsers classes # =========================== -list(APPEND OPENFPGALOADER_SOURCE - src/anlogicBitParser.cpp - src/bitparser.cpp - src/fsparser.cpp - src/mcsParser.cpp - src/pofParser.cpp - src/rawParser.cpp - src/xilinxMapParser.cpp -) +list(APPEND OPENFPGALOADER_SOURCE src/rawParser.cpp) +list(APPEND OPENFPGALOADER_HEADERS src/rawParser.hpp) -list(APPEND OPENFPGALOADER_HEADERS - src/anlogicBitParser.hpp - src/bitparser.hpp - src/fsparser.hpp - src/mcsParser.hpp - src/pofParser.hpp - src/rawParser.hpp - src/xilinxMapParser.hpp -) +if (${ENABLE_LATTICE_SUPPORT} OR ${ENABLE_XILINX_SUPPORT}) + list(APPEND OPENFPGALOADER_SOURCE + src/jedParser.cpp + src/mcsParser.cpp + ) + + list(APPEND OPENFPGALOADER_HEADERS + src/jedParser.hpp + src/mcsParser.hpp + ) +endif() # =========================== # To be sorted # =========================== list(APPEND OPENFPGALOADER_SOURCE - src/anlogic.cpp src/spiFlash.cpp src/spiInterface.cpp src/epcq.cpp src/svf_jtag.cpp src/jtag.cpp - src/gowin.cpp - src/altera.cpp - src/xilinx.cpp ) list(APPEND OPENFPGALOADER_HEADERS - src/altera.hpp - src/anlogic.hpp src/jtag.hpp src/jtagInterface.hpp src/spiFlash.hpp @@ -262,8 +269,6 @@ list(APPEND OPENFPGALOADER_HEADERS src/epcq.hpp src/spiInterface.hpp src/svf_jtag.hpp - src/gowin.hpp - src/xilinx.hpp ) # FTDI Based cables @@ -287,52 +292,148 @@ if (${USE_LIBFTDI}) ) # CologneChip Drivers / Files parsers. - list(APPEND OPENFPGALOADER_SOURCE - src/colognechip.cpp - src/colognechipCfgParser.cpp - ) + if (ENABLE_COLOGNECHIP_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/colognechip.cpp + src/colognechipCfgParser.cpp + ) - list(APPEND OPENFPGALOADER_HEADERS - src/colognechip.hpp - src/colognechipCfgParser.hpp - ) + list(APPEND OPENFPGALOADER_HEADERS + src/colognechip.hpp + src/colognechipCfgParser.hpp + ) + add_definitions(-DENABLE_COLOGNECHIP_SUPPORT=1) + message("CologneChip support enabled") + else() + message("CologneChip support disabled") + endif(ENABLE_COLOGNECHIP_SUPPORT) # Efinix Drivers / Files parsers. - list(APPEND OPENFPGALOADER_SOURCE - src/efinix.cpp - src/efinixHexParser.cpp - ) + if (ENABLE_EFINIX_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/efinix.cpp + src/efinixHexParser.cpp + ) - list(APPEND OPENFPGALOADER_HEADERS - src/efinix.hpp - src/efinixHexParser.hpp - ) + list(APPEND OPENFPGALOADER_HEADERS + src/efinix.hpp + src/efinixHexParser.hpp + ) + add_definitions(-DENABLE_EFINIX_SUPPORT=1) + message("Efinix support enabled") + else() + message("Efinix support disabled") + endif(ENABLE_EFINIX_SUPPORT) - # Lattice Drivers / Files parsers. - list(APPEND OPENFPGALOADER_SOURCE - src/ice40.cpp - src/latticeSSPI.cpp - ) - list(APPEND OPENFPGALOADER_HEADERS - src/ice40.hpp - src/latticeSSPI.hpp - ) + # ICE40 Driver. + if (ENABLE_ICE40_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE src/ice40.cpp) + list(APPEND OPENFPGALOADER_HEADERS src/ice40.hpp) + add_definitions(-DENABLE_ICE40_SUPPORT=1) + message("ICE40 support enabled") + else() + message("ICE40 support disabled") + endif(ENABLE_ICE40_SUPPORT) + + # Lattice SSPI Driver. + if (ENABLE_LATTICESSPI_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE src/latticeSSPI.cpp) + list(APPEND OPENFPGALOADER_HEADERS src/latticeSSPI.hpp) + add_definitions(-DENABLE_LATTICESSPI_SUPPORT=1) + message("Lattice SSPI support enabled") + else() + message("Lattice SSPI support disabled") + endif(ENABLE_LATTICESSPI_SUPPORT) endif() -# Lattice Drivers / Files parsers. -list(APPEND OPENFPGALOADER_SOURCE - src/lattice.cpp - src/feaparser.cpp - src/jedParser.cpp - src/latticeBitParser.cpp -) +# Altera Drivers / Files parsers. +if (ENABLE_ALTERA_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/altera.cpp + src/pofParser.cpp + ) -list(APPEND OPENFPGALOADER_HEADERS - src/lattice.hpp - src/jedParser.hpp - src/feaparser.hpp - src/latticeBitParser.hpp -) + list(APPEND OPENFPGALOADER_HEADERS + src/altera.hpp + src/pofParser.hpp + ) + add_definitions(-DENABLE_ALTERA_SUPPORT=1) + message("Altera support enabled") +else() + message("Altera support disabled") +endif(ENABLE_ALTERA_SUPPORT) + +# Anlogic Drivers / Files parsers. +if (ENABLE_ANLOGIC_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/anlogic.cpp + src/anlogicBitParser.cpp + ) + + list(APPEND OPENFPGALOADER_HEADERS + src/anlogic.hpp + src/anlogicBitParser.hpp + ) + add_definitions(-DENABLE_ANLOGIC_SUPPORT=1) + message("Anlogic support enabled") +else() + message("Anlogic support disabled") +endif(ENABLE_ANLOGIC_SUPPORT) + +# Gowin Drivers / Files parsers. +if (ENABLE_GOWIN_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/gowin.cpp + src/fsparser.cpp + ) + + list(APPEND OPENFPGALOADER_HEADERS + src/gowin.hpp + src/fsparser.hpp + ) + add_definitions(-DENABLE_GOWIN_SUPPORT=1) + message("Gowin support enabled") +else() + message("Gowin support disabled") +endif(ENABLE_GOWIN_SUPPORT) + +# Xilinx Drivers / Files parsers. +if (ENABLE_XILINX_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/bitparser.cpp + src/xilinx.cpp + src/xilinxMapParser.cpp + ) + + list(APPEND OPENFPGALOADER_HEADERS + src/bitparser.hpp + src/xilinx.hpp + src/xilinxMapParser.hpp + ) + add_definitions(-DENABLE_XILINX_SUPPORT=1) + message("Xilinx support enabled") +else() + message("Xilinx support disabled") +endif(ENABLE_XILINX_SUPPORT) + +# Lattice Drivers / Files parsers. +if (ENABLE_LATTICE_SUPPORT) + list(APPEND OPENFPGALOADER_SOURCE + src/lattice.cpp + src/feaparser.cpp + src/latticeBitParser.cpp + ) + + list(APPEND OPENFPGALOADER_HEADERS + src/lattice.hpp + src/feaparser.hpp + src/latticeBitParser.hpp + ) + add_definitions(-DENABLE_LATTICE_SUPPORT=1) + message("Lattice support enabled") +else() + message("Lattice support disabled") +endif(ENABLE_LATTICE_SUPPORT) # Cables select diff --git a/src/main.cpp b/src/main.cpp index be2a440..941d42c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,11 +13,15 @@ #include #include +#ifdef ENABLE_ALTERA_SUPPORT #include "altera.hpp" +#endif +#ifdef ENABLE_ANLOGIC_SUPPORT #include "anlogic.hpp" +#endif #include "board.hpp" #include "cable.hpp" -#ifdef USE_LIBFTDI +#ifdef ENABLE_COLOGNECHIP_SUPPORT #include "colognechip.hpp" #endif #include "common.hpp" @@ -27,14 +31,22 @@ #include "dfu.hpp" #endif #include "display.hpp" -#ifdef USE_LIBFTDI +#ifdef ENABLE_EFINIX_SUPPORT #include "efinix.hpp" +#endif +#ifdef USE_LIBFTDI #include "ftdispi.hpp" #endif +#ifdef ENABLE_GOWIN_SUPPORT #include "gowin.hpp" +#endif +#ifdef ENABLE_LATTICE_SUPPORT #include "lattice.hpp" -#ifdef USE_LIBFTDI +#endif +#ifdef ENABLE_ICE40_SUPPORT #include "ice40.hpp" +#endif +#ifdef ENABLE_LATTICESSPI_SUPPORT #include "latticeSSPI.hpp" #endif #ifdef ENABLE_USB_SCAN @@ -44,7 +56,9 @@ #include "part.hpp" #include "spiFlash.hpp" #include "rawParser.hpp" +#ifdef ENABLE_XILINX_SUPPORT #include "xilinx.hpp" +#endif #include "svf_jtag.hpp" #ifdef ENABLE_XVC #include "xvc_server.hpp" @@ -304,25 +318,45 @@ int main(int argc, char **argv) if (board && board->manufacturer != "none") { Device *target; if (board->manufacturer == "efinix") { +#ifdef ENABLE_EFINIX_SUPPORT target = new Efinix(spi, args.bit_file, args.file_type, board->reset_pin, board->done_pin, board->oe_pin, args.verify, args.verbose); +#else + printError("Support for Efinix FPGAs was not enabled at compile time"); + return EXIT_FAILURE; +#endif } else if (board->manufacturer == "lattice") { if (board->fpga_part == "ice40") { +#ifdef ENABLE_ICE40_SUPPORT target = new Ice40(spi, args.bit_file, args.file_type, args.prg_type, board->reset_pin, board->done_pin, args.verify, args.verbose); +#else + printError("Support for ICE40 FPGAs was not enabled at compile time"); + return EXIT_FAILURE; +#endif } else if (board->fpga_part == "ecp5") { +#ifdef ENABLE_LATTICESSPI_SUPPORT target = new LatticeSSPI(spi, args.bit_file, args.file_type, args.verbose); +#else + printError("Support for Lattice FPGAs (SSPI mode) was not enabled at compile time"); + return EXIT_FAILURE; +#endif } else { printError("Error (SPI mode): " + board->fpga_part + " is an unsupported/unknown Lattice Model"); return EXIT_FAILURE; } } else if (board->manufacturer == "colognechip") { +#ifdef ENABLE_COLOGNECHIP_SUPPORT target = new CologneChip(spi, args.bit_file, args.file_type, args.prg_type, board->reset_pin, board->done_pin, DBUS6, board->oe_pin, args.verify, args.verbose); +#else + printError("Support for Gowin FPGAs was not enabled at compile time"); + return EXIT_FAILURE; +#endif } else { printError("Error (SPI mode): " + board->manufacturer + " is an unsupported/unknown target"); @@ -474,7 +508,7 @@ int main(int argc, char **argv) return EXIT_SUCCESS; #else - throw std::runtime_error("DFU support: disabled at build time"); + printError("DFU support: disabled at build time"); return EXIT_FAILURE; #endif } @@ -612,33 +646,71 @@ int main(int argc, char **argv) Device *fpga; try { if (fab == "xilinx") { +#ifdef ENABLE_XILINX_SUPPORT fpga = new Xilinx(jtag, args.bit_file, args.secondary_bit_file, args.file_type, args.prg_type, args.fpga_part, args.bridge_path, args.target_flash, args.verify, args.verbose, args.skip_load_bridge, args.skip_reset, args.read_dna, args.read_xadc); +#else + printError("Support for Xilinx FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } else if (fab == "altera") { +#ifdef ENABLE_ALTERA_SUPPORT fpga = new Altera(jtag, args.bit_file, args.file_type, args.prg_type, args.fpga_part, args.bridge_path, args.verify, args.verbose, args.flash_sector, args.skip_load_bridge, args.skip_reset); +#else + printError("Support for Altera FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } else if (fab == "anlogic") { +#ifdef ENABLE_ANLOGIC_SUPPORT fpga = new Anlogic(jtag, args.bit_file, args.file_type, args.prg_type, args.verify, args.verbose); -#ifdef USE_LIBFTDI +#else + printError("Support for Anlogic FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } else if (fab == "efinix") { +#ifdef ENABLE_EFINIX_SUPPORT fpga = new Efinix(jtag, args.bit_file, args.file_type, args.prg_type, args.board, args.fpga_part, args.bridge_path, args.verify, args.verbose); +#else + printError("Support for Efinix FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; #endif } else if (fab == "Gowin") { +#ifdef ENABLE_GOWIN_SUPPORT fpga = new Gowin(jtag, args.bit_file, args.file_type, args.mcufw, args.prg_type, args.external_flash, args.verify, args.verbose, args.user_flash); +#else + printError("Support for Gowin FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } else if (fab == "lattice") { +#ifdef ENABLE_LATTICE_SUPPORT fpga = new Lattice(jtag, args.bit_file, args.file_type, args.prg_type, args.flash_sector, args.verify, args.verbose, args.skip_load_bridge, args.skip_reset); -#ifdef USE_LIBFTDI +#else + printError("Support for Lattice FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } else if (fab == "colognechip") { +#ifdef ENABLE_COLOGNECHIP_SUPPORT fpga = new CologneChip(jtag, args.bit_file, args.file_type, args.prg_type, args.board, args.cable, args.verify, args.verbose); +#else + printError("Support for Gowin FPGAs was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; #endif } else { printError("Error: manufacturer " + fab + " not supported"); From e78b5e29956c2f791ebb7bb3bf8bec3c52c6d9b9 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Thu, 29 Jan 2026 16:16:40 +0100 Subject: [PATCH 12/13] CMakeLists.txt,main.cpp: option to enable/disable svf_jtag support --- CMakeLists.txt | 10 ++++++++-- src/main.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12c68e1..c827c23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ option(ENABLE_JLINK "enable JLink cable (requires libUSB)" ${ option(ENABLE_DFU "enable DFU support (requires libUSB)" ${ENABLE_CABLE_ALL}) option(ENABLE_FTDI_BASED_CABLE "enable cables based on FTDI (requires libFTDI" ${ENABLE_CABLE_ALL}) option(ENABLE_GOWIN_GWU2X "enable Gowin GWU2X interface" ${ENABLE_CABLE_ALL}) +option(ENABLE_SVF_JTAG "enable SVF support" ${ENABLE_CABLE_ALL}) option(ENABLE_USB_BLASTERI "enable Altera USB Blaster I support" ${ENABLE_CABLE_ALL}) option(ENABLE_USB_BLASTERII "enable Altera USB Blaster II support" ${ENABLE_CABLE_ALL}) @@ -257,7 +258,6 @@ list(APPEND OPENFPGALOADER_SOURCE src/spiFlash.cpp src/spiInterface.cpp src/epcq.cpp - src/svf_jtag.cpp src/jtag.cpp ) @@ -268,7 +268,6 @@ list(APPEND OPENFPGALOADER_HEADERS src/spiFlashdb.hpp src/epcq.hpp src/spiInterface.hpp - src/svf_jtag.hpp ) # FTDI Based cables @@ -504,6 +503,13 @@ list (APPEND OPENFPGALOADER_SOURCE src/remoteBitbang_client.cpp) list (APPEND OPENFPGALOADER_HEADERS src/remoteBitbang_client.hpp) endif() +# SVF JTAG file type support +if (ENABLE_SVF_JTAG) +list (APPEND OPENFPGALOADER_SOURCE src/svf_jtag.cpp) +list (APPEND OPENFPGALOADER_HEADERS src/svf_jtag.hpp) +add_definitions(-DENABLE_SVF_JTAG) +endif() + # Xilinx Virtual Cable if (ENABLE_XILINX_VIRTUAL_CABLE) list (APPEND OPENFPGALOADER_SOURCE src/xvc_client.cpp src/xvc_server.cpp) diff --git a/src/main.cpp b/src/main.cpp index 941d42c..15b0152 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,7 +59,9 @@ #ifdef ENABLE_XILINX_SUPPORT #include "xilinx.hpp" #endif +#ifdef ENABLE_SVF_JTAG #include "svf_jtag.hpp" +#endif #ifdef ENABLE_XVC #include "xvc_server.hpp" #endif @@ -622,6 +624,7 @@ int main(int argc, char **argv) /* detect svf file and program the device */ if (!args.file_type.compare("svf") || args.bit_file.find(".svf") != string::npos) { +#ifdef ENABLE_SVF_JTAG SVF_jtag *svf = new SVF_jtag(jtag, args.verbose); try { svf->parse(args.bit_file); @@ -629,6 +632,11 @@ int main(int argc, char **argv) return EXIT_FAILURE; } return EXIT_SUCCESS; +#else + printError("Support for SVF Jtag was not enabled at compile time"); + delete(jtag); + return EXIT_FAILURE; +#endif } /* check if selected device is supported From d8e81ffdad7553e545b6e45e4b2fbc8516d27879 Mon Sep 17 00:00:00 2001 From: ced2911 Date: Tue, 3 Feb 2026 14:47:00 +0100 Subject: [PATCH 13/13] Add spiOverJtag for 10CL006YE144C8G + fix for quartus 25 --- spiOverJtag/Makefile | 2 +- spiOverJtag/altclkctrl_inst.v | 11 +++++++++++ spiOverJtag/build.py | 3 +++ spiOverJtag/spiOverJtag_10cl006144.rbf.gz | Bin 0 -> 18034 bytes 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 spiOverJtag/altclkctrl_inst.v create mode 100644 spiOverJtag/spiOverJtag_10cl006144.rbf.gz diff --git a/spiOverJtag/Makefile b/spiOverJtag/Makefile index 344e714..91fd1de 100644 --- a/spiOverJtag/Makefile +++ b/spiOverJtag/Makefile @@ -17,7 +17,7 @@ XILINX_PARTS := xc3s500evq100 \ xcau15p-ffvb676 XILINX_BIT_FILES := $(addsuffix .bit.gz,$(addprefix spiOverJtag_, $(XILINX_PARTS))) -ALTERA_PARTS := 10cl025256 10cl016484 10cl055484 \ +ALTERA_PARTS := 10cl025256 10cl016484 10cl055484 10cl006144 \ ep4ce622 ep4ce1017 ep4ce2217 ep4ce1523 ep4ce11523 ep4cgx15027 5ce215 5ce223 5ce423 5ce523 5ce927 5sgsd5 ALTERA_BIT_FILES := $(addsuffix .rbf.gz, $(addprefix spiOverJtag_, $(ALTERA_PARTS))) diff --git a/spiOverJtag/altclkctrl_inst.v b/spiOverJtag/altclkctrl_inst.v new file mode 100644 index 0000000..bbe4b19 --- /dev/null +++ b/spiOverJtag/altclkctrl_inst.v @@ -0,0 +1,11 @@ +module altclkctrl_inst ( + input wire inclk, + input wire ena, + output wire outclk +); +altclkctrl u ( + .inclk(inclk), + .ena(ena), + .outclk(outclk) +); +endmodule diff --git a/spiOverJtag/build.py b/spiOverJtag/build.py index 7233563..8ae4ec3 100755 --- a/spiOverJtag/build.py +++ b/spiOverJtag/build.py @@ -222,6 +222,7 @@ else: "10cl016484" : "10CL016YU484C8G", "10cl025256" : "10CL025YU256C8G", "10cl055484" : "10CL055YU484C8G", + "10cl006144" : "10CL006YE144C8G", "ep4cgx15027": "EP4CGX150DF27I7", "ep4ce11523" : "EP4CE115F23C7", "ep4ce2217" : "EP4CE22F17C6", @@ -238,6 +239,8 @@ else: "5sgsd5" : "5SGSMD5K2F40I3"}[part] files.append({'name': currDir + 'altera_spiOverJtag.v', 'file_type': 'verilogSource'}) + files.append({'name': currDir + 'altclkctrl_inst.v', + 'file_type': 'verilogSource'}) files.append({'name': currDir + 'altera_spiOverJtag.sdc', 'file_type': 'SDC'}) tool_options = {'device': full_part, 'family':family} diff --git a/spiOverJtag/spiOverJtag_10cl006144.rbf.gz b/spiOverJtag/spiOverJtag_10cl006144.rbf.gz new file mode 100644 index 0000000000000000000000000000000000000000..72b3170404828f6e9d63d3dc61bc8a36d6859ea7 GIT binary patch literal 18034 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 HvN8Yw$GgZQ literal 0 HcmV?d00001