From 648a4a833a734777c2af8304b603d770c820d947 Mon Sep 17 00:00:00 2001 From: Andrew E Wilson Date: Mon, 13 Oct 2025 23:14:26 -0600 Subject: [PATCH] basic PDI prog for Spartan Ultrascale+ --- doc/FPGAs.yml | 7 ++++ src/part.hpp | 3 ++ src/xilinx.cpp | 103 ++++++++++++++++++++++++++++++++----------------- src/xilinx.hpp | 1 + 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/doc/FPGAs.yml b/doc/FPGAs.yml index 6193fc7..11140a5 100644 --- a/doc/FPGAs.yml +++ b/doc/FPGAs.yml @@ -329,6 +329,13 @@ Xilinx: Memory: OK Flash: OK + - Description: Spartan UltraScale+ + Model: + - xcsu35p + URL: https://www.amd.com/en/products/adaptive-socs-and-fpgas/fpga/spartan-ultrascale-plus.html#productTable + Memory: OK + Flash: TBD + - Description: Spartan 3 Model: - xc3s200 diff --git a/src/part.hpp b/src/part.hpp index e9da9d3..9b0719b 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -125,6 +125,9 @@ static std::map fpga_list = { {0x04b31093, {"xilinx", "virtexusp", "xcvu9p", 18}}, {0x14b79093, {"xilinx", "virtexusp", "xcvu37p", 18}}, + /* Xilinx Ultrascale+ / Spartan */ + {0x04e80093, {"xilinx", "spartanusp", "xcsu35p", 6}}, + /* Xilinx Ultrascale+ / ZynqMP */ /* When powering a zynq ultrascale+ MPSoC, PL Tap and ARM dap * are disabled and only PS tap with a specific IDCODE is seen. diff --git a/src/xilinx.cpp b/src/xilinx.cpp index 8dcdf29..eb34d01 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -150,6 +150,7 @@ static std::map>> { "JSHUTDOWN", {0x0D} }, { "ISC_PROGRAM", {0x11} }, { "ISC_DISABLE", {0x16} }, + { "STATUS", {0x1F} }, { "BYPASS", {0xff} }, } }, @@ -294,7 +295,9 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename, _mode = Device::SPI_MODE; } else if (_file_extension == "jed") { _mode = Device::FLASH_MODE; - } else { + } else if (_file_extension == "pdi") { + _mode = Device::MEM_MODE; + } else { _mode = Device::SPI_MODE; } } @@ -358,6 +361,14 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename, _fpga_family = KINTEXUSP_FAMILY; } else if (family == "artixusp") { _fpga_family = ARTIXUSP_FAMILY; + } else if (family == "spartanusp") { + if (_file_extension != "pdi") { + char mess[256]; + snprintf(mess, 256, "Error: only volatile PDI programing for " + "Spartan Ultrascale+ devices\n"); + throw std::runtime_error(mess); + } + _fpga_family = SPARTANUSP_FAMILY; } else if (family == "virtexus") { _fpga_family = VIRTEXUS_FAMILY; } else if (family == "virtexusp") { @@ -609,6 +620,9 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash) if (_mode == Device::MEM_MODE || _fpga_family == XCF_FAMILY) reverse = true; + if (_file_extension == "pdi") + reverse = false; + try { if (_flash_chips & PRIMARY_FLASH) { open_bitfile(_filename, _file_extension, &bit, reverse, _verbose); @@ -849,41 +863,60 @@ void Xilinx::program_mem(ConfigBitstreamParser *bitfile) * 16: Move into RTI state. X 0 1 */ _jtag->set_state(Jtag::RUN_TEST_IDLE); - /* - * 17: Enter the SELECT-IR state. X 1 2 - * 18: Move to the SHIFT-IR state. X 0 2 - * 19: Start loading the JSTART instruction - * (optional). The JSTART instruction 01100 0 5 - * initializes the startup sequence. - * 20: Load the last bit of the JSTART instruction. 0 1 1 - * 21: Move to the UPDATE-IR state. X 1 1 - */ - _jtag->shiftIR(get_ircode(_ircode_map, "JSTART"), NULL, _irlen, Jtag::UPDATE_IR); - /* - * 22: Move to the RTI state and clock the - * startup sequence by applying a minimum X 0 2000 - * of 2000 clock cycles to the TCK. - */ - _jtag->set_state(Jtag::RUN_TEST_IDLE); - _jtag->toggleClk(2000); - /* - * 23: Move to the TLR state. The device is - * now functional. X 1 3 - */ - _jtag->go_test_logic_reset(); - /* Some xc7s50 does not detect correct connected flash w/o this shift*/ - _jtag->shiftIR(tx_buf, rx_buf, _irlen); - uint8_t ir_c = rx_buf[0] & 0x03; - uint8_t isc_done = ((rx_buf[0] >> 2) & 0x01); - uint8_t isc_ena = ((rx_buf[0] >> 3) & 0x01); - uint8_t init = ((rx_buf[0] >> 4) & 0x01); - uint8_t done = ((rx_buf[0] >> 5) & 0x01); - printf("Shift IR %02x\n", rx_buf[0]); - printf("ir: %x isc_done %x isc_ena %x init %x done %x\n", ir_c, isc_done, isc_ena, - init, done); - if (!done) { - read_register("STAT"); + if (_file_extension == "pdi") { + _jtag->toggleClk(2000); + /* + * 17: For PDI devices, use the STATUS instruction + * to verify successful configuration. + */ + unsigned char tx_data[6]= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char rx_data[6]; + _jtag->shiftIR(get_ircode(_ircode_map, "STATUS"), NULL, _irlen); + _jtag->shiftDR(tx_data, rx_data, 48); + if ((rx_data[4] & 0x04) != 0x04) { + printError("PDI programing failed"); + } else { + printSuccess("PDI programing success"); + } + } + else { + /* + * 17: Enter the SELECT-IR state. X 1 2 + * 18: Move to the SHIFT-IR state. X 0 2 + * 19: Start loading the JSTART instruction + * (optional). The JSTART instruction 01100 0 5 + * initializes the startup sequence. + * 20: Load the last bit of the JSTART instruction. 0 1 1 + * 21: Move to the UPDATE-IR state. X 1 1 + */ + _jtag->shiftIR(get_ircode(_ircode_map, "JSTART"), NULL, _irlen, Jtag::UPDATE_IR); + /* + * 22: Move to the RTI state and clock the + * startup sequence by applying a minimum X 0 2000 + * of 2000 clock cycles to the TCK. + */ + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(2000); + /* + * 23: Move to the TLR state. The device is + * now functional. X 1 3 + */ + _jtag->go_test_logic_reset(); + /* Some xc7s50 does not detect correct connected flash w/o this shift*/ + _jtag->shiftIR(tx_buf, rx_buf, _irlen); + uint8_t ir_c = rx_buf[0] & 0x03; + uint8_t isc_done = ((rx_buf[0] >> 2) & 0x01); + uint8_t isc_ena = ((rx_buf[0] >> 3) & 0x01); + uint8_t init = ((rx_buf[0] >> 4) & 0x01); + uint8_t done = ((rx_buf[0] >> 5) & 0x01); + printf("Shift IR %02x\n", rx_buf[0]); + printf("ir: %x isc_done %x isc_ena %x init %x done %x\n", ir_c, isc_done, isc_ena, + init, done); + + if (!done) { + read_register("STAT"); + } } } diff --git a/src/xilinx.hpp b/src/xilinx.hpp index c55464c..d0856fb 100644 --- a/src/xilinx.hpp +++ b/src/xilinx.hpp @@ -198,6 +198,7 @@ class Xilinx: public Device, SPIInterface { ZYNQMP_FAMILY, XCF_FAMILY, ARTIXUSP_FAMILY, + SPARTANUSP_FAMILY, VIRTEXUS_FAMILY, VIRTEXUSP_FAMILY, UNKNOWN_FAMILY = 999