feat: add QMTech XC7A100T (FGG676) board support#663
Open
gHashTag wants to merge 23 commits into
Open
Conversation
The existing spiOverJtag bitstream for xc7a100tfgg676 was a symlink to the
xc7a100t (csg324) build. csg324 and fgg676 packages use different SPI flash
pin mappings, so on a QMTech XC7A100T FGG676 core board the proxy bitstream
drove SPI on the wrong pins, making the flash unreadable (JEDEC ID returned
as 0xFFFFFE).
Changes:
- spiOverJtag/constr_xc7a_fgg676.xdc: switch to the XC7A*-FGG676 dedicated
SPI configuration pins per UG470/UG475 (CSn=C8, MOSI=B19, MISO=A18,
WPn=B18, HOLDn=A19). CCLK is driven internally via STARTUPE2.
- spiOverJtag/build.py: accept the full "device+package" form
(e.g. xc7a100tfgg676) and emit a package-specific bitstream; only create
per-package symlinks when building the bare device target, and replace
pre-existing symlinks so package-specific builds win.
- spiOverJtag/Makefile: register xc7a100tfgg676 as its own build target.
- spiOverJtag/README.md: document package-specific bitstream builds.
- spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gz: removed (stale symlink to
the csg324 build). Regenerate locally with Vivado:
cd spiOverJtag && make spiOverJtag_xc7a100tfgg676.bit.gz
- src/board.hpp: add qmtechArtix7_100T board (xc7a100tfgg676, SPI flash).
- doc/boards.yml: list qmtechArtix7_100T with OK/OK status.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gHashTag
pushed a commit
to gHashTag/t27
that referenced
this pull request
May 12, 2026
Refs #592 trabucayre/openFPGALoader#663 - fpga/bscan_spi_qmtech/bscan_spi_qmtech.v - plain Verilog port of the openocd xilinx_bscan_spi.py Migen module (BSCANE2 USER1 + STARTUPE2 + marker/length/data shift state machine). - fpga/bscan_spi_qmtech/bscan_spi_qmtech.xdc - FGG676 dedicated SPI pin LOCs (C8/B19/A18 = FCS_B/MOSI/DIN), LVCMOS33, SPI_BUSWIDTH=1. - fpga/bscan_spi_qmtech/Makefile - standalone openXC7 driver (yosys + nextpnr-himbaechel + fasm2frames + xc7frames2bit). - cli/tri/src/fpga.rs - new tri fpga build-proxy [--install] subcommand that drives the same pipeline through std::process, no shell or Python. - docs/fpga/SPI_FLASH_DEBUG.md - new "Solution" section with the openXC7 build flow and a pointer to the Vivado-based PR #663 fallback. - docs/NOW.md - entry for 2026-05-12. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner
|
The board integration LGTM. |
gHashTag
pushed a commit
to gHashTag/t27
that referenced
this pull request
May 12, 2026
…ing blocked bbaexport + bbasm + nextpnr-xilinx user-pin route all succeed on xc7a100tfgg676-1 (Fmax 254 MHz). The real proxy bitstream requires LOC C8/B19/A18 onto FCS_B/DQ0/DQ1 (dedicated configuration pins via STARTUPE2). openXC7 pack_clocking_xc7.cc aborts in dict::at() at prepare_clocking after placing cs_n on OPAD_X0Y10 (GTP_CHANNEL). This matches trabucayre/openFPGALoader#663 — the spiOverJtag FGG676 build is currently Vivado-only across the open-source ecosystem. Docker-Vivado (ce0f7ae build-proxy-docker) remains the SSOT for fpga/tools/bscan_spi_xc7a100t.bit until openXC7 grows STARTUPE2 support. Closes #592 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds .github/workflows/build-fgg676.yml that attempts to build the
package-specific spiOverJtag_xc7a100tfgg676.bit.gz inside a public
Vivado Docker image. The job:
- Frees disk space on the ubuntu-latest runner
- Pulls fpgatools/vivado:latest (best-effort public image)
- Sources settings64.sh, installs edalize, runs
'python3 build.py xc7a100tfgg676 spi'
- Verifies the resulting bitstream contains the string
'7a100tfgg676' (not '7a100tftg256') and reports sha256
- Uploads spiOverJtag-fgg676 artifact with .bit, .bit.gz and
Vivado logs from tmp_xc7a100tfgg676/
This complements upstream PR trabucayre#663 which fixed build.py to honor an
explicit device+package argument so package-specific bitstreams are
no longer aliased to the first-package build (ftg256).
The fpgatools/vivado:latest image launches as a non-root user, so apt-get update fails with EACCES and pip3 is not preinstalled. Run the container with --user 0:0 and install python3/python3-pip via apt before pip-installing edalize. Also chown/chmod the build outputs so they are readable by the runner user after the container exits.
The fpgatools/vivado:latest container ships Ubuntu 18.04 with system
Python 3.6. Newer edalize 0.6.x pulls Jinja2 3.x which uses
importlib.metadata APIs that don't work cleanly under 3.6, leading to
'A loader was not found for the package' AssertionError from
PackageLoader('edalize', 'templates'). Pin edalize==0.4.0 and
Jinja2<3 / MarkupSafe<2.1 to match what the build was originally
written against.
edalize 0.4.0 already declares 'Jinja2>=3' in install_requires, which clashes with Python 3.6 / setuptools 39 in the Ubuntu 18.04 Vivado container. edalize 0.2.4 has no formal install_requires, so a manual Jinja2 2.11.3 + MarkupSafe < 2.1 pin works.
edalize 0.2.x predates the get_edatool API exposed by spiOverJtag/build.py, so 'from edalize.edatool import get_edatool' fails with ImportError. edalize 0.3.0 is the earliest release that exposes get_edatool *and* declares Jinja2 >= 2.11.3 (without forcing >= 3), so pin to that combo.
Drop the 'pip install --upgrade pip<21' step — when run as 'python3 -m pip' it succeeds but apparently confuses the shell state (Run build step silently exits after Successfully installed pip-20.3.4). Use the stock pip 9.0.1 directly with --disable-pip-version-check. Also verify that 'from edalize.edatool import get_edatool' works before invoking build.py.
The ca-certificates trigger inside fpgatools/vivado:latest can return non-zero (likely because of how Xilinx ships its own Java cacerts hook). Under 'set -e' that silently terminates our entire bash block before python3 build.py ever runs — the Run-build step appears to succeed but exits in ~10 s with no useful log lines past 'Running hooks in /etc/ca-certificates/update.d'. Drop 'set -e' and gate apt-get with '|| true', then explicitly verify python3 is on PATH.
The comment 'A loader was not found for the package' contained an apostrophe which terminated the surrounding 'bash -c'...' single-quoted string. Bash then exited cleanly after the python3 --version line and the rest of the build script (pip install + python3 build.py) was silently dropped. That's why Run-build kept finishing in ~11 s with no error — there *was* no command to run.
Replace the (unreliable) public Vivado Docker image strategy with a real silent install of AMD Vivado ML Standard on the runner. Free ~45 GiB with easimon/maximize-build-space, fetch the AMD Unified Installer via a presigned URL from the XILINX_INSTALLER_URL secret, run xsetup AuthTokenGen with XILINX_USER / XILINX_PASS to authorize the web installer to pull device data, and install only the Artix-7 family to keep the footprint inside the disk budget. Builds spiOverJtag_xc7a100tfgg676.bit via python3 build.py and verifies the embedded device string is 7a100tfgg676 (not the wrong ftg256). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The heredoc body at column 0 collapsed the surrounding 'run: |' literal block, causing GitHub Actions to reject the workflow with 'workflow file issue' (0 jobs scheduled). Replace with a block of printf statements at the correct indent so YAML parses cleanly. Validated locally with python yaml.safe_load. Updates t27#592
Owner
|
The modifications in |
Owner
|
Could you open a new PR for the specific case of the QMTech / bitstream? |
Bypass the AMD presigned-URL/CAPTCHA flow by staging the unmodified Vivado Web Installer .bin (sha256 a78dbe89…) as a release asset on this fork (tag: vivado-installer-2025.2). The CI job now downloads it with 'gh release download' using the automatic GITHUB_TOKEN — no human-in-the-loop, no XILINX_INSTALLER_URL secret needed. The runner still authenticates to xilinx.com at install time to fetch the Artix-7 device payload (~10 GiB), but that download happens at gigabit on a GitHub-hosted runner instead of 40 KB/sec via QEMU on the Mac, so the previously-fatal bandwidth blocker is gone. Updates t27#592
AMD's xsetup -b AuthTokenGen requires an interactive TTY because the Java installer reads the password via /dev/tty. GitHub-hosted runners run in non-interactive mode (FATAL Could not get a console!!!). Replace the runtime AuthTokenGen with a pre-generated wi_authentication_key shipped as base64 repo secret WI_AUTHENTICATION_KEY_B64. Lifetime ~7 days; regenerate locally with xsetup -b AuthTokenGen and rotate the secret. Drops dependency on XILINX_USER and XILINX_PASS for the install step (still used by Vivado at runtime via the token, just not via stdin). Updates t27#592
xsetup for Vivado ML Standard 2025.2 rejects WebTalkTerms in the --agree list and prints the EULA help text, exiting with code 1. Only XilinxEULA and 3rdPartyEULA are valid here. The WebTalk consent is already disabled via InstallOptions in the config file. Updates t27#592
Download, extract, install, and tmp directories were all on the root
partition which only has ~145G total and was at 100% used (357M free)
right after the 363MB installer download. easimon/maximize-build-space
provisions ~104G of dedicated space at ${{ github.workspace }} but the
workflow never used it.
Move INSTALLER_DIR, XSETUP_EXTRACT_DIR, XILINX_INSTALL_DIR and TMPDIR
under ${{ github.workspace }}. Drop sudo for the install dir since it
is now owned by runner. Print df -h before and after install for
diagnostics.
Updates t27#592
The hand-written Modules= line listed 'System Generator for DSP' which the 2025.2 installer no longer accepts, causing xsetup to exit with 'value specified ... is not valid' before installing. Generate a reference config from the installer itself (xsetup -b ConfigGen), then patch it with awk: - Destination -> LVM build-mount install dir - Modules -> Artix-7:1, every other entry :0 - InstallOptions -> every option :0 - Create*Shortcuts/FileAssociation -> 0 This is resilient to AMD renaming or removing modules between Vivado releases. No shell scripts or python (constitution rule). Updates t27#592
xsetup -b ConfigGen is interactive (asks for edition choice on stdin) and on a non-TTY shell it spammed 'Not a valid choice' indefinitely, producing >700 MB of logs in 6 minutes and killing the runner. Revert to a hand-written install_config.txt but remove 'System Generator for DSP', which the 2025.2 installer no longer recognizes. All other module names match the canonical schema. Updates t27#592
Every hand-written install_config.txt is doomed to drift: the 2025.2 installer no longer accepts 'System Generator for DSP' (run 25748594744 failed on 'Vivado Simulator' too). Per UG973 (Running the Installer, 2025.2), invoking xsetup with --edition / --product / --location uses the installer's own default module selection, which is by construction self-consistent with its schema. The 'Vivado ML Standard' default install for Vivado is ~30 GB, well under our 103 GB build-mount. The install_config step still runs and writes the file for diagnostic purposes, but Install Vivado no longer consumes it. Updates t27#592
Run 25748987884 hung 19+ minutes on a single apt-get install batch.
The likely culprits are (a) an unattended-upgrades dpkg lock held by
the runner image, and (b) the legacy libtinfo5/libncurses5 packages
which no longer exist on the ubuntu-24.04 runner.
Changes:
- Wait up to 25s for the dpkg lock to free up before proceeding.
- Wrap apt-get update and the strict install in `timeout`, so a
network hang fails the step in minutes instead of forever.
- Drop libtinfo5/libncurses5 entirely (gone in ubuntu-24.04).
- Install GUI libs one-by-one with their own per-pkg timeout, so
a single missing package skips instead of taking down the batch.
Updates t27#592
Run 25750098370 hit apt-get update timeout (180s) against a flaky mirror and aborted the whole step. The github-runner image ships an up-to-date package cache, so we can safely continue with cached lists when update is slow. Updates t27#592
Run 25750432794 finally reached Install Vivado, accepted the EULAs, and failed with: ERROR - At least one device must be selected. To avoid this error, add a valid Modules entry... Modules=Zynq UltraScale+ MPSoC:1,Engineering Sample Devices:0,DocNav:1 So --edition/--product alone don't pick any device families. Write a minimal install_config that lists ONLY Artix-7:1 (the architecture QMTech XC7A100T uses) and DocNav:0 (cited by xsetup as a valid module name). Omit every other module entry entirely so we can't trip on renamed/removed names like 'System Generator for DSP' (2025.1) or 'Vivado Simulator' (2025.2). Updates t27#592
The previous constr_xc7a_fgg676.xdc tried to use dedicated configuration bank pins (C8/B19/A18/B18/A19) which Vivado rejected on XC7A100T-FGG676: B19/A19 fall in GTP bank terminals and B18 is not a valid package pin. Per QMTECH_XC7A75T_100T_200T-CORE-BOARD schematic (ChinaQMTECH/QMTECH_XC7A75T-100T-200T_Core_Board), the on-board N25Q064A SPI flash is wired through Bank 14 user IO dual-purpose pins (D00..D03, FCS_B). These match the existing constr_xc7a_fbg676.xdc because FBG676/FGG676 share ball-out for XC7A75T/100T/200T. PACKAGE_PIN P18 -> csn (FCS_B / IO_L6P_T0_FCS_B_14) PACKAGE_PIN R14 -> sdi_dq0 (D00 / IO_L1P_T0_D00_MOSI_14) PACKAGE_PIN R15 -> sdo_dq1 (D01 / IO_L1N_T0_D01_DIN_14) PACKAGE_PIN P14 -> wpn_dq2 (D02 / IO_L2P_T0_D02_14) PACKAGE_PIN N14 -> hldn_dq3 (D03 / IO_L2N_T0_D03_14) CCLK is driven internally via STARTUPE2 in xilinx_spiOverJtag.v. Updates t27#592
Without JTAGCLK the startup sequencer never reaches EOS=1 when the bitstream is loaded over JTAG (DLC10/Platform Cable). Symptoms: INIT_COMPLETE=1, MMCM_LOCK=1, CRC_ERROR=0, ID_ERROR=0 but DONE=0 and EOS=0 forever (STAT=0x4000190C on XC7A100T). UG470 §6.3 Table 6-3: STARTUPCLK selects the clock for the 8-step startup FSM. Default is CFGCLK = CCLK from STARTUPE2, which is only driven during SelectMAP / SPI configuration, not during JTAG load. JTAGCLK reuses TCK so the sequencer can complete under JTAG. Affects only XC7A*-FGG676 (QMTech XC7A100T core board).
gHashTag
pushed a commit
to gHashTag/t27
that referenced
this pull request
May 14, 2026
Refs #592 trabucayre/openFPGALoader#663 - fpga/bscan_spi_qmtech/bscan_spi_qmtech.v - plain Verilog port of the openocd xilinx_bscan_spi.py Migen module (BSCANE2 USER1 + STARTUPE2 + marker/length/data shift state machine). - fpga/bscan_spi_qmtech/bscan_spi_qmtech.xdc - FGG676 dedicated SPI pin LOCs (C8/B19/A18 = FCS_B/MOSI/DIN), LVCMOS33, SPI_BUSWIDTH=1. - fpga/bscan_spi_qmtech/Makefile - standalone openXC7 driver (yosys + nextpnr-himbaechel + fasm2frames + xc7frames2bit). - cli/tri/src/fpga.rs - new tri fpga build-proxy [--install] subcommand that drives the same pipeline through std::process, no shell or Python. - docs/fpga/SPI_FLASH_DEBUG.md - new "Solution" section with the openXC7 build flow and a pointer to the Vivado-based PR #663 fallback. - docs/NOW.md - entry for 2026-05-12. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gHashTag
pushed a commit
to gHashTag/t27
that referenced
this pull request
May 14, 2026
…ing blocked bbaexport + bbasm + nextpnr-xilinx user-pin route all succeed on xc7a100tfgg676-1 (Fmax 254 MHz). The real proxy bitstream requires LOC C8/B19/A18 onto FCS_B/DQ0/DQ1 (dedicated configuration pins via STARTUPE2). openXC7 pack_clocking_xc7.cc aborts in dict::at() at prepare_clocking after placing cs_n on OPAD_X0Y10 (GTP_CHANNEL). This matches trabucayre/openFPGALoader#663 — the spiOverJtag FGG676 build is currently Vivado-only across the open-source ecosystem. Docker-Vivado (ce0f7ae build-proxy-docker) remains the SSOT for fpga/tools/bscan_spi_xc7a100t.bit until openXC7 grows STARTUPE2 support. Closes #592 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for the QMTech XC7A100T core board (FGG676 package, MT25QL128 SPI flash) and fixes the root cause that prevented
xc7a100tfgg676-targeted flash programming from working at all.The bug
spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gzwas a symlink tospiOverJtag_xc7a100t.bit.gz, which is built with thecsg324pinout. csg324 and fgg676 are different physical packages with different SPI flash routing, so on a real FGG676 board the proxy bitstream drove SPI on the wrong pins. Users hit this as a JEDEC ID readback ofFF FF FEand high-Z MISO instead of the expected20 BA 18for MT25QL128.The root cause is in
spiOverJtag/build.py: after building the bitstream for the first package inpackages[family][part](here,csg324), itln -s'd all other package variants to that same.bit.gz. That's fine when the SPI pinout is identical across packages of one device size — but for xc7a100t it isn't.Changes
spiOverJtag/constr_xc7a_fgg676.xdc— switch to the dedicated SPI configuration pins on XC7A*-FGG676 (per UG470/UG475):CSn→C8,MOSI→B19,MISO→A18,WPn→B18,HOLDn→A19CCLKis driven internally throughSTARTUPE2(unchanged).spiOverJtag/build.py— accept the fulldevice+packageform (e.g.xc7a100tfgg676) and emit a package-specific bitstream. Only generate per-package symlinks when building the bare-device target, and replace pre-existing symlinks so a package-specific build wins.spiOverJtag/Makefile— registerxc7a100tfgg676as its own target.spiOverJtag/README.md— document package-specific bitstream builds.spiOverJtag/spiOverJtag_xc7a100tfgg676.bit.gz— removed (was a stale symlink). Regenerate locally with Vivado:src/board.hpp— newqmtechArtix7_100Tboard entry (xc7a100tfgg676, SPI flash).doc/boards.yml— listqmtechArtix7_100TwithMemory: OK/Flash: OK.Note on the regenerated bitstream
The repo previously shipped
spiOverJtag_xc7a100tfgg676.bit.gzas a (broken) symlink. This PR removes that file rather than committing a new one, because Vivado is required to regenerate it and the maintainer's build environment is the canonical place for it. Reviewers / users can build it with the command above.Test plan
python3 -c 'import ast, pathlib; ast.parse(pathlib.Path("spiOverJtag/build.py").read_text())'passes (syntax check).src/board.hppcompiles in the openFPGALoader build (verified locally withg++ -c -std=c++11 -I src).cd spiOverJtag && make spiOverJtag_xc7a100tfgg676.bit.gz— confirms FGG676 pinout is applied (set_property PACKAGE_PIN C8 ... csn, etc.) instead of falling back to csg324.openFPGALoader -b qmtechArtix7_100T --detectreports JEDEC ID20 BA 18and--write-flash/--read-flashround-trips successfully.🤖 Generated with Claude Code