diff --git a/spiOverJtag/Makefile b/spiOverJtag/Makefile index f34c4bf..f45236a 100644 --- a/spiOverJtag/Makefile +++ b/spiOverJtag/Makefile @@ -3,7 +3,7 @@ XILINX_PARTS := xc3s500evq100 \ xc6slx16ftg256 xc6slx16csg324 xc6slx25csg324 xc6slx45csg324 xc6slx100fgg484 \ xc6slx25tcsg324 xc6slx45tfgg484 xc6slx150tfgg484 xc6slx150tcsg484 \ xc6vlx130tff784 \ - xc7a12t xc7a15t xc7a25t xc7a35t xc7a50t xc7a75t xc7a100t xc7a200t \ + xc7a12t xc7a15t xc7a25t xc7a35t xc7a50t xc7a75t xc7a100t xc7a100tfgg676 xc7a200t \ xc7s6 xc7s15 xc7s25 xc7s50 xc7s75 xc7s100 \ xc7k70tfbg484 xc7k70tfbg676 \ xc7k160tffg676 \ diff --git a/spiOverJtag/README.md b/spiOverJtag/README.md index ca26d1d..89b8f49 100644 --- a/spiOverJtag/README.md +++ b/spiOverJtag/README.md @@ -59,6 +59,24 @@ Clean temporary/build files: make clean ``` +## Package-specific bitstreams + +Some boards within the same FPGA family/size use different SPI flash pin +mappings depending on the device package. When the SPI pins differ between +packages, `build.py` must produce a distinct bitstream per package rather +than aliasing one build to every package via symlinks. + +To build a package-specific bitstream, pass the full device name +(device + package) to `make`. Example for the QMTech XC7A100T (FGG676) +core board: + +```bash +make spiOverJtag_xc7a100tfgg676.bit.gz +``` + +This consumes `constr_xc7a_fgg676.xdc` and produces a bitstream wired to +the FGG676 SPI dedicated configuration pins (C8/B19/A18/B18/A19). + ## Add support for a new device ### 1. Register the part in `Makefile` diff --git a/spiOverJtag/build.py b/spiOverJtag/build.py index 7a42fb8..595df7f 100755 --- a/spiOverJtag/build.py +++ b/spiOverJtag/build.py @@ -120,7 +120,21 @@ else: os.sys.exit() if model in ["xc7a", "xc7s"]: - pkg = packages[family][part][0] + # Accept either a bare device (e.g. "xc7a100t" -> first package) or a + # device suffixed with a package (e.g. "xc7a100tfgg676" -> "fgg676"). + if part in packages[family]: + pkg = packages[family][part][0] + device = part + else: + m = re.match(r"(xc7[as]\d+t?)([a-z]+\d+)", part) + if not m or m.group(1) not in packages[family]: + print(f"Error: cannot parse part/package from '{part}'") + os.sys.exit() + device = m.group(1) + pkg = m.group(2) + if pkg not in packages[family][device]: + print(f"Error: package '{pkg}' not listed for device '{device}'") + os.sys.exit() pkg_name = f"{model}_{pkg}" if model in ["xc7k"]: m = re.match(r"(xc7k\d+t)(\w+)", part) @@ -193,7 +207,7 @@ if tool in ["ise", "vivado"]: cst_type = "xdc" # Artix/Spartan 7 Specific use case: if family in ["Artix", "Spartan 7"]: - tool_options = {'part': f"{part}{pkg}-1"} + tool_options = {'part': f"{device}{pkg}-1"} elif family == "Xilinx UltraScale": if part in ["xcvu9p-flga2104", "xcku5p-ffvb676"]: tool_options = {'part': part + '-1-e'} @@ -285,10 +299,17 @@ if tool in ["vivado", "ise"]: with gzip.open(f"{flash_type}OverJtag_{part}.bit.gz", 'wb', compresslevel=9) as bit_gz: shutil.copyfileobj(bit, bit_gz) - # Create Symbolic links for all supported packages. - if family in ["Artix", "Spartan 7"]: + # Create Symbolic links for all supported packages โ€” only when building + # the bare device target (e.g. "xc7a100t"). When the explicit + # device+package form is used (e.g. "xc7a100tfgg676"), the bitstream is + # package-specific and must not be aliased to other packages. + if family in ["Artix", "Spartan 7"] and part in packages[family]: in_file = f"{flash_type}OverJtag_{part}.bit.gz" for pkg in packages[family][part]: out_file = f"{flash_type}OverJtag_{part}{pkg}.bit.gz" - if not os.path.exists(out_file): + # Don't overwrite a package-specific bitstream with a symlink to + # the first-package build (this was the original bug). + if os.path.islink(out_file) or not os.path.exists(out_file): + if os.path.islink(out_file): + os.unlink(out_file) subprocess.run(["ln", "-s", in_file, out_file]) diff --git a/spiOverJtag/constr_xc7a_fgg676.xdc b/spiOverJtag/constr_xc7a_fgg676.xdc index 00375a5..b179c41 100644 --- a/spiOverJtag/constr_xc7a_fgg676.xdc +++ b/spiOverJtag/constr_xc7a_fgg676.xdc @@ -3,11 +3,27 @@ set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH {4} [current_design] set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] -set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVTTL} [get_ports {csn}] -set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVTTL} [get_ports {sdi_dq0}] -set_property -dict {PACKAGE_PIN R15 IOSTANDARD LVTTL} [get_ports {sdo_dq1}] -set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVTTL} [get_ports {wpn_dq2}] -set_property -dict {PACKAGE_PIN N14 IOSTANDARD LVTTL} [get_ports {hldn_dq3}] +# Drive the startup sequencer from JTAG TCK so the bitstream reaches EOS +# (End-Of-Startup, i.e. DONE=HIGH) when loaded over JTAG via DLC10. +# Without this Vivado defaults to Cclk (CCLK) โ€” but CCLK is only driven +# during SelectMAP/SPI configuration, not during JTAG. The result is a +# bitstream whose COR0 register has STARTUPCLK=00 (Cclk); we verified the +# previous CI build had COR0=0x02003fe5 (bits[27:26]=00). UG470 ยง6.3 +# Table 6-3 lists the valid Vivado literal as `JtagClk` (camelCase) โ€” +# all-caps `JTAGCLK` is silently rejected and Vivado falls back to Cclk. +set_property BITSTREAM.STARTUP.STARTUPCLK JtagClk [current_design] + +# XC7A75T/100T/200T-FGG676 SPI flash pins on QMTech XC7A100T-2FGG676I core board. +# Verified against QMTECH_XC7A75T_100T_200T-CORE-BOARD-V01-20210109.pdf schematic +# (https://github.com/ChinaQMTECH/QMTECH_XC7A75T-100T-200T_Core_Board). +# These are Bank 14 dual-purpose user IO pins (D00..D03, FCS_B) wired to the +# on-board N25Q064A SPI flash (JEDEC 0x20BA17). CCLK is driven internally via +# the STARTUPE2 primitive instantiated in xilinx_spiOverJtag.v. +set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [get_ports {csn}] +set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVCMOS33} [get_ports {sdi_dq0}] +set_property -dict {PACKAGE_PIN R15 IOSTANDARD LVCMOS33} [get_ports {sdo_dq1}] +set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {wpn_dq2}] +set_property -dict {PACKAGE_PIN N14 IOSTANDARD LVCMOS33} [get_ports {hldn_dq3}] create_clock -period 33.000 -name jtag_tck -waveform {0.000 16.500} [get_pins {bscane2_inst/DRCK}] create_clock -period 33.000 -name vers_tck -waveform {0.000 16.500} [get_pins {bscane2_version/DRCK}] diff --git a/spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gz b/spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gz deleted file mode 100644 index 6910300..0000000 Binary files a/spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gz and /dev/null differ