Compare commits

...

19 Commits

Author SHA1 Message Date
Gwenhael Goavec-Merou b76840b20d doc/FPGAs.yml: added Kintex UltraScale+ reference 2025-11-05 18:43:38 +01:00
Gwenhael Goavec-Merou 530b7a9993
Merge pull request #593 from AEW2015/master
basic PDI prog for Spartan Ultrascale+
2025-10-23 06:50:14 +02:00
Andrew E Wilson 648a4a833a basic PDI prog for Spartan Ultrascale+ 2025-10-23 06:44:19 +02:00
Gwenhael Goavec-Merou 03be134cdd
Merge pull request #591 from hennomann/patch-1
Update part.hpp to support Lattice LFE3-150EA device
2025-10-02 12:02:15 +02:00
hennomann 617cd29dff
Update part.hpp to support Lattice LFE3-150EA device
This PR adds device ID support for the Lattice ECP3 LFE3-150EA.
Previously, only the LFE3-70E was supported.

We've tested the implementation and confirmed it works as expected.
We're happy to contribute to expanding ECP3 device support!
2025-10-02 11:32:19 +02:00
Gwenhael Goavec-Merou 205e6f9ea5 spiOverJtag/build.py: fixed model value for kintex7 with ISE 2025-09-25 17:09:38 +02:00
Gwenhael Goavec-Merou f8ae76e771 spiOverJtag/build.py: added default value for pkg variable 2025-09-25 15:47:48 +02:00
Gwenhael Goavec-Merou 8694b3c295 board: added HyVision PCIe OPT01 rev.F (Kintex7 xc7k70tfbg676) 2025-09-24 16:56:46 +02:00
Gwenhael Goavec-Merou a7b72321fe spiOverJtag: added xc7k70tfbg676 variant 2025-09-24 16:48:02 +02:00
Gwenhael Goavec-Merou 8e6eb1085c spiOverJtag/build.py: simplify Kintex7 uses 2025-09-24 16:46:57 +02:00
Gwenhael Goavec-Merou 13cf0c59b9 xilinx: load_bridge: uses configBitstreamParser getFilename to print real name instead of theorical (with or without .gz) 2025-09-24 08:07:27 +02:00
Gwenhael Goavec-Merou 6935936a92 configBitstreamParser: added filename getter 2025-09-24 08:06:28 +02:00
Gwenhael Goavec-Merou 39b3ca5871 spiInterface: all methods: added a \n after command displayed (avoids unclear message when calee methods uses printxxx) 2025-09-24 08:06:06 +02:00
Gwenhael Goavec-Merou fdc9edc6cb spiOverJtag/xilinx_spiOverJtag.v: spartan3e: spi_drck -> drck 2025-09-22 10:18:24 +02:00
Gwenhael Goavec-Merou 29a5bc3515 spiOverJtag/xilinx_spiOverJtag.v: fixed code for virtex6 targets 2025-09-20 18:05:55 +02:00
Gwenhael Goavec-Merou 6df4bce1fd xilinx: allows Flash write for Xilinx Spartan3 targets 2025-09-20 17:31:32 +02:00
Gwenhael Goavec-Merou 1ccc2a0d5b spiOverJtag/xilinx_spiOverJtag.v: can't declare spi_clk for spartan3e device. Fixed sck assign for spartan3e 2025-09-20 17:30:10 +02:00
Gwenhael Goavec-Merou 5e67fee9f5 spiOverJtag/build.py: gzip file must be produces for all Xilinx devices 2025-09-17 20:16:03 +02:00
Gwenhael Goavec-Merou 06b4e2f143 spiOverJtag/xilinx_spiOverJtag.v: don't redeclares tdo for spartan3e 2025-09-17 11:55:52 +02:00
11 changed files with 198 additions and 67 deletions

View File

@ -308,6 +308,14 @@ Xilinx:
Memory: OK
Flash: OK (primary)
- Description: Kintex UltraScale+
Model:
- xcku3p
- xcku5p
URL: https://www.xilinx.com/products/silicon-devices/fpga/kintex-ultrascale-plus.html#productTable
Memory: OK
Flash: OK
- Description: Virtex 6
Model:
- xc6vlx130t
@ -329,6 +337,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

View File

@ -401,6 +401,13 @@
Memory: OK
Flash: NA
- ID: hyvision_opt01
Description: HyVision PCIe OPT01 rev.F
URL: NA
FPGA: Kintex7 xc7k70tfbg676
Memory: OK
Flash: OK
- ID: honeycomb
Description: honeycomb
URL: https://github.com/Disasm/honeycomb-pcb

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import os
import re
from edalize.edatool import get_edatool
@ -15,6 +16,17 @@ packages = {
"xc7a100t" : ["csg324", "ftg256", "fgg484", "fgg676"],
"xc7a200t" : ["sbg484", "fbg484", "fbg676", "ffg1156"],
},
# Added but seems not possible to use same bitstream
# for all Kintex with the same size but different package.
"Kintex 7": {
"xc7k70t" : ["fbg484", "fbg676"],
"xc7k160t" : ["fbg484", "fbg676", "ffg676"],
"xc7k325t" : ["fbg676", "ffg676", "fbg900", "ffg900"],
"xc7k355t" : ["ffg901"],
"xc7k410t" : ["fbg676", "ffg676", "fbg900", "ffg900"],
"xc7k420t" : ["ffg901", "ffg1156"],
"xc7k480t" : ["ffg901", "ffg1156"],
},
"Spartan 7": {
"xc7s6" : ["ftgb196", "cpga196", "csga225"],
"xc7s15" : ["ftgb196", "cpga196", "csga225"],
@ -43,6 +55,7 @@ currDir = os.path.abspath(os.path.curdir) + '/'
files = []
parameters = {}
pkg_name = None
pkg = None
model = ""
subpart = part[0:4].lower()
@ -78,6 +91,7 @@ elif subpart == "xc7k":
family = "Kintex7"
tool = "ise"
speed = -2
model = subpart
elif subpart == "xc7s":
family = "Spartan 7"
tool = "vivado"
@ -104,6 +118,12 @@ else:
if model in ["xc7a", "xc7s"]:
pkg = packages[family][part][0]
pkg_name = f"{model}_{pkg}"
if model in ["xc7k"]:
m = re.match(r"(xc7k\d+t)(\w+)", part)
pkg = m.group(2)
pkg_name = f"{model}_{pkg}"
if tool == "ise":
model = m.group(1)
if tool in ["ise", "vivado"]:
pkg_name = {
@ -120,12 +140,6 @@ if tool in ["ise", "vivado"]:
"xc6slx150tcsg484" : "xc6s_csg484",
"xc6slx150tfgg484" : "xc6s_t_fgg484",
"xc6vlx130tff784" : "xc6v_ff784",
"xc7k70tfbg484" : "xc7k_fbg484",
"xc7k70tfbg676" : "xc7k_fbg676",
"xc7k160tffg676" : "xc7k_ffg676",
"xc7k325tffg676" : "xc7k_ffg676",
"xc7k325tffg900" : "xc7k_ffg900",
"xc7k420tffg901" : "xc7k_ffg901",
"xc7vx330tffg1157" : "xc7v_ffg1157",
"xcku040-ffva1156" : "xcku040_ffva1156",
"xcku060-ffva1156" : "xcku060_ffva1156",
@ -152,10 +166,7 @@ if tool in ["ise", "vivado"]:
"xc6slx150tcsg484": "xc6slx150t",
"xc6slx150tfgg484": "xc6slx150t",
"xc6vlx130tff784": "xc6vlx130t",
"xc7k325tffg676": "xc7k325t",
"xc7k325tffg900": "xc7k325t",
"xc7k420tffg901": "xc7k420t",
}[part],
}.get(part, model),
'package': {
"xc3s500evq100": "vq100",
"xc6slx9tqg144": "tqg144",
@ -170,10 +181,7 @@ if tool in ["ise", "vivado"]:
"xc6slx150tcsg484": "csg484",
"xc6slx150tfgg484": "fgg484",
"xc6vlx130tff784": "ff784",
"xc7k325tffg676": "ffg676",
"xc7k325tffg900": "ffg900",
"xc7k420tffg901": "ffg901",
}[part],
}.get(part, pkg),
'speed' : speed
}
else:
@ -254,7 +262,7 @@ backend = get_edatool(tool)(edam=edam, work_root=build_dir)
backend.configure()
backend.build()
if tool == "vivado":
if tool in ["vivado", "ise"]:
import shutil
import subprocess
import gzip

Binary file not shown.

View File

@ -10,10 +10,14 @@ module spiOverJtag
`ifdef spartan3e
output wire sck,
`endif
`ifdef virtex6
output wire sdi_dq0
`else
output wire sdi_dq0,
input wire sdo_dq1,
output wire wpn_dq2,
output wire hldn_dq3
`endif
`endif // xilinxultrascale
`ifdef secondaryflash
@ -27,11 +31,12 @@ module spiOverJtag
wire capture, drck, sel, update, shift;
wire tdi, tdo;
wire spi_clk;
`ifndef spartan3e
`ifndef virtex6
/* Version Interface. */
wire ver_sel, ver_cap, ver_shift, ver_drck, ver_tdi, ver_tdo;
wire spi_clk;
spiOverJtag_core spiOverJtag_core_prim (
/* JTAG state/controls */
@ -59,13 +64,14 @@ module spiOverJtag
.wpn_dq2(wpn_dq2),
.hldn_dq3(hldn_dq3)
);
`endif
`endif /* !virtex6 */
`endif /* !spartan3e */
`ifdef spartan6
assign sck = spi_clk;
`else // !spartan6
`ifdef spartan3e
assign sck = spi_clk;
assign sck = drck;
`else // !spartan6 && !spartan3e
`ifdef xilinxultrascale
assign sck = drck;
@ -100,7 +106,52 @@ module spiOverJtag
.USRDONEO (1'b1), // 1-bit input: User DONE pin output control.
.USRDONETS(1'b1) // 1-bit input: User DONE 3-state enable output.
);
`else // !spartan6 && !spartan3e && !xilinxultrascale
`elsif virtex6 // !spartan6 && !spartan3e && !xilinxultrascale
wire di;
wire runtest;
reg fsm_csn;
// jtag -> spi flash
assign sdi_dq0 = tdi;
assign tdo = (sel) ? di : tdi;
assign csn = fsm_csn;
wire tmp_cap_s = capture && sel;
wire tmp_up_s = update && sel;
always @(posedge drck, posedge runtest) begin
if (runtest) begin
fsm_csn <= 1'b1;
end else begin
if (tmp_cap_s) begin
fsm_csn <= 1'b0;
end else if (tmp_up_s) begin
fsm_csn <= 1'b1;
end else begin
fsm_csn <= fsm_csn;
end
end
end
STARTUP_VIRTEX6 #(
.PROG_USR("FALSE")
) startup_virtex6_inst (
.CFGCLK(), // unused
.CFGMCLK(), // unused
.CLK(1'b0), // unused
.DINSPI(di), // data from SPI flash
.EOS(),
.GSR(1'b0), // unused
.GTS(1'b0), // unused
.KEYCLEARB(1'b0), // not used
.PACK(1'b1), // tied low for 'safe' operations
.PREQ(), // unused
.TCKSPI(), // echo of CCLK from TCK pin
.USRCCLKO (drck), // user FPGA -> CCLK pin
.USRCCLKTS(1'b0), // drive CCLK not in high-Z
.USRDONEO (1'b1), // why both USRDONE are high?
.USRDONETS(1'b1) // ??
);
`else // !spartan6 && !spartan3e && !xilinxultrascale && !virtex6
STARTUPE2 #(
.PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams.
.SIM_CCLK_FREQ(0.0) // Set the Configuration Clock Frequency(ns) for simulation.
@ -130,8 +181,8 @@ module spiOverJtag
assign hldn_dq3 = 1'b1;
// jtag -> spi flash
assign sdi_dq0 = tdi;
wire tdo = (sel) ? sdo_dq1 : tdi;
assign csn = fsm_csn;
assign tdo = (sel) ? sdo_dq1 : tdi;
assign csn = fsm_csn;
wire tmp_cap_s = capture && sel;
wire tmp_up_s = update && sel;
@ -169,7 +220,9 @@ module spiOverJtag
.TDO2 () // 1-bit input: USER2 function
);
`else
`ifdef spartan6
`ifdef virtex6
BSCAN_VIRTEX6 #(
`elsif spartan6
BSCAN_SPARTAN6 #(
`else
BSCANE2 #(
@ -181,8 +234,12 @@ module spiOverJtag
// is asserted, DRCK toggles when
// CAPTURE or SHIFT are asserted.
.RESET (), // 1-bit output: Reset output for TAP controller.
`ifdef virtex6
.RUNTEST(runtest),
`else
.RUNTEST(), // 1-bit output: Output asserted when TAP
// controller is in Run Test/Idle state.
`endif
.SEL (sel), // 1-bit output: USER instruction active output.
.SHIFT (shift), // 1-bit output: SHIFT output from TAP controller.
.TCK (), // 1-bit output: Test Clock output.
@ -197,6 +254,7 @@ module spiOverJtag
);
/* BSCAN for Version Interface. */
`ifndef virtex6
`ifdef spartan6
BSCAN_SPARTAN6 #(
`else
@ -217,6 +275,7 @@ module spiOverJtag
.TDO (ver_tdo)
);
`endif
`endif /* !virtex6 */
`ifdef secondaryflash
wire drck_sec;

View File

@ -179,6 +179,7 @@ static std::map <std::string, target_board_t> board_list = {
JTAG_BOARD("genesys2", "xc7k325tffg900", "digilent_b", 0, 0, CABLE_DEFAULT),
JTAG_BOARD("gr740-mini", "", "ft4232hp_b", 0, 0, CABLE_MHZ(1)),
JTAG_BOARD("hseda-xc6slx16", "xc6slx16ftg256", "", 0, 0, CABLE_DEFAULT),
JTAG_BOARD("hyvision_opt01", "xc7k70tfbg676", "", 0, 0, CABLE_DEFAULT),
/* most ice40 boards uses the same pinout */
SPI_BOARD("ice40_generic", "lattice", "ice40", "ft2232",
DBUS7, DBUS6, 0,

View File

@ -23,6 +23,11 @@ class ConfigBitstreamParser {
const uint8_t *getData() const {return _bit_data.data();}
int getLength() {return _bit_length;}
/**
* \brief return bitstream file name
*/
std::string getFilename() const {return _filename;}
/**
* \brief display header informations
*/

View File

@ -125,6 +125,9 @@ static std::map <uint32_t, fpga_model> 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.
@ -301,6 +304,7 @@ static std::map <uint32_t, fpga_model> fpga_list = {
/* Lattice ECP3 */
{0x01014043, {"lattice", "ECP3", "LFE3-70E", 8}},
{0x01015043, {"lattice", "ECP3", "LFE3-150EA", 8}},
/* Lattice ECP5 */
{0x21111043, {"lattice", "ECP5", "LFE5U-12", 8}},

View File

@ -26,7 +26,8 @@ SPIInterface::SPIInterface(const std::string &filename, int8_t verbose,
bool SPIInterface::detect_flash()
{
bool ret = true;
printInfo("protect_flash: ", false);
printInfo("Detect flash:");
/* move device to spi access */
if (!prepare_flash_access()) {
@ -55,7 +56,7 @@ bool SPIInterface::detect_flash()
bool SPIInterface::protect_flash(uint32_t len)
{
bool ret = true;
printInfo("protect_flash: ", false);
printInfo("protect_flash:");
/* move device to spi access */
if (!prepare_flash_access()) {
@ -98,7 +99,7 @@ bool SPIInterface::unprotect_flash()
SPIFlash flash(this, false, _spif_verbose);
/* configure flash protection */
printInfo("unprotect_flash: ", false);
printInfo("unprotect_flash:");
ret = (flash.disable_protection() == 0);
if (!ret)
printError("Fail");
@ -129,7 +130,7 @@ bool SPIInterface::set_quad_bit(bool set_quad)
SPIFlash flash(this, false, _spif_verbose);
/* configure flash protection */
printInfo("set_quad_bit: ", false);
printInfo("set_quad_bit:");
ret = flash.set_quad_bit(set_quad);
if (!ret)
printError("Fail");
@ -148,7 +149,7 @@ bool SPIInterface::set_quad_bit(bool set_quad)
bool SPIInterface::bulk_erase_flash()
{
bool ret = true;
printInfo("bulk_erase: ", false);
printInfo("bulk_erase:");
/* move device to spi access */
if (!prepare_flash_access()) {

View File

@ -150,6 +150,7 @@ static std::map<std::string, std::map<std::string, std::vector<uint8_t>>>
{ "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") {
@ -365,9 +376,6 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename,
_ircode_map = ircode_mapping.at("virtexusp");
} else if (family.substr(0, 8) == "spartan3") {
_fpga_family = SPARTAN3_FAMILY;
if (_mode != Device::MEM_MODE) {
throw std::runtime_error("Error: Only load to mem is supported");
}
} else if (family == "xcf") {
_fpga_family = XCF_FAMILY;
if (_mode == Device::MEM_MODE) {
@ -612,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);
@ -715,11 +726,11 @@ bool Xilinx::load_bridge()
bitname = PathHelper::absolutePath(bitname);
#endif
std::cout << "use: " << bitname << std::endl;
/* first: load spi over jtag */
try {
BitParser bridge(bitname, true, _verbose);
printSuccess("Use: " + bridge.getFilename());
bridge.parse();
if (_fpga_family == SPARTAN3_FAMILY)
xc3s_flow_program(&bridge);
@ -852,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");
}
}
}

View File

@ -198,6 +198,7 @@ class Xilinx: public Device, SPIInterface {
ZYNQMP_FAMILY,
XCF_FAMILY,
ARTIXUSP_FAMILY,
SPARTANUSP_FAMILY,
VIRTEXUS_FAMILY,
VIRTEXUSP_FAMILY,
UNKNOWN_FAMILY = 999