Two surgical fixes that together close the FASM -> frames -> bitstream
path for designs that use IOB_Y1 (slave) as an input alongside IOB_Y0
(master) as an output in the same LIOB18/RIOB18 tile. Verified
end-to-end on VC707 (~/johnson_8bit, 8-bit Johnson counter with
LEDs on Y0 and the centre-button input landing on Y1): single
`fasm2frames` + `xc7frames2bit` run with no Vivado in the loop, the
loaded bitstream advances the ring on each button press.
(1) Layer-A — IN_ONLY segbit over-claim.
LIOB18.IOB_Y1.LVCMOS..._IN_ONLY rows previously CLEARed bits
`!38_32 !38_34`. Those two bits actually belong to
INT_L.IOB_COL_BANK_ACTIVE (which SETs them). When a tile has a
Y0 OBUF + Y1 IBUF, both features fire and conflict on the same
physical bits — fasm2frames stops with FasmInconsistentBits.
Drop those two bits from every Y1 IN_ONLY row in LIOB18 and
RIOB18; let the bits default-to-zero unless bank-active sets
them. Verified that the Y0 IN_ONLY rows are untouched.
(2) Layer-B — XRAY_ALLOW_MISSING_FEATURES escape hatch.
fasm_assembler.py: parse_fasm_filename() now respects the env
var XRAY_ALLOW_MISSING_FEATURES=1. When set, missing-feature
lookups become per-line console warnings instead of a
FasmLookupError. Three IOB_Y1 features still need fuzzing
(IBUF_HP_BANK_GLUE, LVCMOS12_LVCMOS15.IN,
LVCMOS12_LVCMOS15_SSTL12_SSTL135_SSTL15.IN_ONLY) — the proper
fix is a fuzzer pass that places IBUFs on Y1 sites and merges
the captured segbits into segbits_{l,r}iob18.db, but until then
the escape hatch keeps the open-source path moving.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
End-to-end recipe for the IBUFDS+BUFG+FDRE+OBUF demo that produced the
first confirmed-working open-flow bitstream on a VC707 (xc7vx485tffg1761-2)
on 2026-06-01. Covers tooling versions, the test design + XDC, build
order (nextpnr-xilinx -> json2dcp+WireOracle -> Vivado lock-and-route ->
write_bitstream), the openFPGALoader command, expected hardware
behaviour, and the three known follow-ups (SLICE pin-assignment CRITICAL,
IOB sitetype-net overwrite, XDC LOC not auto-applied).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
propagate_IOB_SING and propagate_IOI_SING walk each IO column from a
parent IOB/IOI tile down to the SING half-tiles at the column endpoints
and alias the parent's CLB_IO_CLK bits into both halves (offset=0 for
the bottom SING, offset=99 for the top SING). The parent-type
allowlists were missing the HP-bank L-column types:
propagate_IOB_SING: ["LIOB33", "RIOB33", "RIOB18"] # LIOB18 missing
propagate_IOI_SING: ["LIOI3", "RIOI3", "RIOI"] # LIOI missing
On artix7 / kintex7 the leftmost HP column doesn't exist as a parent
LIOB18 / LIOI in the tilegrid, so the omission was latent. On
virtex7 VX (HP-only) both LIOB18 and RIOB18 columns exist, and the
gap surfaces as half the SING IOB / IOI tiles (the L-column ones)
having empty bits in tilegrid.json:
LIOB18_SING: 0/14 with bits -> 14/14
RIOB18_SING: 14/14 (unchanged)
LIOI_SING: 0/14 with bits -> 14/14
RIOI_SING: 14/14 (unchanged)
Add the missing types. Cross-checked against iob18's own measured
parent bases (e.g. LIOB18_X81Y2 -> 0x00421000 lines up with the
propagated LIOB18_SING_X81Y1 base 0x00421000 offset=0 and Y51 offset=99).
Also de-wire the iob18_sing sub-fuzzer from TILEGRID_TDB_DEPENDENCIES:
SING tiles aren't independently addressed (they share the parent IOB's
base with a word-offset variant), and half of iob18_sing's tdb entries
land at an unaligned frame (offset+1) that add_tdb's frame-alignment
assertion would reject anyway. The build rule stays so the fuzzer can
be invoked manually as a Vivado-measurement guard for the propagation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The existing iob18 sub-fuzzer filters to IOB18S (main IOB of a diff
pair) and ioi18 explicitly skips _SING tile types, so the SING-row
IOB18 singletons (one IOB18 site per LIOB18_SING / RIOB18_SING tile)
never had their tilegrid frame addresses resolved. On xc7vx485t this
left 28 tile rows un-addressed in tilegrid.json.
Add a parallel iob18_sing sub-fuzzer: places an IBUF on every SING-row
IOB18 site (14 specimens), bit-diffs to a segbits_tilegrid.tdb, and
hooks into TILEGRID_TDB_DEPENDENCIES alongside iob18 / iob18_int so
add_tdb.py merges the resulting frame addresses into tilegrid.json.
Pairs with task #17 (HP-bank glue end-to-end).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This system standardises on /opt/Xilinx/Vivado/2020.1 not the NFS share
the upstream default points at; honour the local standardisation while
remaining overridable via the env var.
Pairs with task #50 (005-tilegrid virtex7 unaligned-frame fix).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a callout under the project tagline pointing Virtex-7 / VC707 users
straight to the Virtex-7 Port Status section, and note that the
Quickstart Guide assumes the upstream Artix-7 / 2017.2 path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a callout block under the four-repo branch table noting that the
Virtex-7 work was developed and validated against Vivado 2020.1, that
other 2018+ releases may work, and that the upstream-recommended 2017.2
is too old for this device (predates xc7vx485tffg1761-2 HP-bank IOB18
support, several HCLK_IOI/CMT properties the fuzzers query, and the
write_pip_txtdata bulk paths the utils.tcl patch exercises).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add the four-repo upstream branch map (prjxray / nextpnr-xilinx-meta /
nextpnr-xilinx / demo-projects), pointing at the openXC7 mirrors.
- Replace the smoke-test-passes Achievements with the current end-to-end
state: rst_to_led / counter / counter_bufr / telegraph at 0/0 vs Vivado
with zero .frm patches, and the PicoSoC UART demo running on real VC707
silicon at 125 MHz.
- List the 7 codified HP-bank glue segbits (LIOB18 IBUF/OBUF Y0+Y1, RIOB18
IBUFDS, INT_L IOB_COL_OBUF_CASCADE_Y1 / IOB_COL_BANK_ACTIVE /
GFAN_TIE_ROOT_GLUE, HCLK_L BUFRCLK3_ACTIVE) plus the 14 IOB18 silicon
defaults.
- New 'Virtex-7 architecture discoveries' section describing the things
that turned out to differ from Kintex-7 and weren't inherited by
transplant: HP vs HR silicon, HCLK_IOI vs HCLK_IOI3, L/R-side mirror,
the bank-glue category itself (tile-in-use markers / Y0+Y1 OBUF
cascade / PUDC_B / IBUFDS pair root), the .DRIVE. vs .SLEW. classifier
gotcha, HCLK_L BUFRCLK3 leaf, GFAN T-tie INT_L+10 row glue, fasm2bels
NOCLKINV block, nextpnr-xilinx OBUF T-tie convergence + Y137/Y138 wire
mapping, and the PicoSoC RAM128/256X1S packing requirement.
- Goals now reflect what's still open: the 228-bit PicoSoC CLB-glue
family, the 236 missing IOI features, fasm2bels past NOCLKINV, and the
Y137/Y138 nextpnr-xilinx wire mapping.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Failed bitgen with 'ERROR: [DRC PDRC-2] BSCAN_JtagChain: Unsupported
programming for BSCAN site BSCAN_X0Y0 and JTAG_CHAIN attribute value
3.' Same DRC already disabled in fuzzers/005-tilegrid/cfg/generate.tcl
for the same reason — Vivado 2020.1 tightened this check vs 2017.2.
Add the matching disable here.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Was wedging the build at ~7h elapsed in iteration 1 (one specimen at
~40% through a sequential per-net Tcl loop, ~13h/specimen extrapolated
on xc7vx485t). Two compounding problems, both contained to this fuzzer:
1) generate.tcl redefined write_pip_txtdata as a slow Tcl
'foreach net { foreach pip { ... get_nodes -uphill ... } }'.
On a big device with ~1296 nets this scales as O(nets*pips) with
per-pip routing-graph queries. The custom proc produces the same
6-column output (tile pip src dst pnum pdir) that the segmakers
parse — i.e., identical to Vivado's built-in write_pip_txtdata
that 049/053 et al. use. Drop the override so the built-in is
invoked (~10x speed-up).
2) Makefile set N = 120 specimens, 6x the typical 20. Reduce to 20
to match other pip fuzzers; segmatch still converges with plenty
of variety.
Combined: ~60x faster, lighter mem footprint. Won't change behaviour
for other families that weren't memory-bound — they get the same
faster path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both fuzzers crashed in Vivado with 'ERROR: [Common 17-69] Missing
name/value pair in -dict argument' at the set_property -dict 'LOC ...
BEL ...' line. Cause: generate.tcl reserves 3*todo_lines INT tiles
via randsample_list (utils.tcl), which samples without replacement
and pads with empty strings once exhausted. The off-edge virtex7 ROI
holds only ~150 INT_L/INT_R tiles, but 053 has 64 todo lines, so
3*64=192 > 150 and the tail of the sample is empty entries; high-idx
todo lines (tile_idx = idx*3) get empty tiles -> empty driver_site
-> odd-length -dict -> error.
Reduce the per-todo-line retry window from 3 to 2 (set tiles
expr 2*todo_lines, tile_idx idx*2, retry limit tries>=2). 2*64=128
fits comfortably in ~150 pool. Other pip fuzzers use a different
indexing pattern; only these two use idx*K. For larger-ROI families
the pool still exceeds 2*N as it did 3*N, so no regression.
Verified: 053 single-specimen run reaches all 64 todo lines (idx 63
previously died at 50), emits segdata_int_l/r; 055 likewise.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Failed in dbfixup.py:153 add_tag_bits (AssertionError) because the
process_rdb name-harmonisation loop folded two distinct-bit Y1 IN
groups onto a single feature name. On Virtex-7 IOB_Y1 LVCMOS*.IN
has bits {38_00,39_01} but SSTL*.IN has only {38_00}; the visited-
iostandard logic forced both onto 'LVCMOS18_SSTL12_SSTL135_SSTL15.IN'
(driven by the Y0 grouping where they happened to share bits),
emitting two contradictory db lines that dbfixup can't merge.
(The existing filter_negbits already expects the un-merged name
IOB_Y1.LVCMOS12_LVCMOS15_LVCMOS18.IN, confirming the merge is wrong
here.) kintex7's Y1 LVCMOS and SSTL share bits, so it never hits
this case.
Fix: require identical bits before adopting a visited name; gate
via XRAY_DATABASE == virtex7 so kintex7's behaviour is provably
unchanged. Adds 'import os' for the env check.
Verified: no duplicate tags; Y1 splits correctly into
LVCMOS12_LVCMOS15_LVCMOS18.IN + SSTL12_SSTL135_SSTL15.IN.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two unrelated fixes the HP-IOB18 fuzzers need on a full-chip part:
1) top.py for both idelay and odelay collects all IOB18 tiles
('IOB18' in tile_name) but sorted with a key hardcoded to prefix
'RIOB18_' via create_xy_fun('RIOB18_'), which asserts on the first
LIOB18_* tile. kintex7's ROI only ever exposed the R side so this
was latent. Switch to a regex prefix '[LR]IOB18_' (matches the
existing '\\S+' idiom used by 037-iob18-pips). Doesn't change
which tiles get collected, so no-op for kintex7.
2) odelay generate.tcl: Vivado 2020.1 enforces DRC REQP-135 (ODELAY
VAR_LOAD/VAR_LOAD_PIPE requires CNTVALUEIN[0:4] connected) before
write_bitstream, but the fuzzer intentionally leaves CNTVALUEIN
unconnected. The idelay generate.tcl already disables IDELAY's
equivalents (REQP-79/81/84/85/87); add REQP-135 to the odelay
disable list to mirror.
Verified: idelay/odelay both run to completion on xc7vx485tffg1761-2,
producing valid segdata.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The earlier skip blamed a 'Vivado 2020.1 BEL-slot' difference; on close
investigation the real failure was generate.py:79 `assert bels == bels_tcl`
in the my_ram_N branch. The two ROI definitions disagree on X extent:
util.get_roi() (XRAY_ROI_GRID_*) yields SLICEM at SLICE X2..X12, but the
Vivado dump pblock built from $XRAY_ROI is X0..X11 — so the whole
SLICE_X12Y* column top.py LOC's primitives into falls outside the dumped
pblock and shows up as None in design.csv, tripping the assert.
The sibling RAM path already tolerated this via 'if ram != has_bel_tcl:
continue' (line ~137); mirror the same graceful skip in the SRL/LUT path.
For other families bels always == bels_tcl, so the change is a no-op.
Verified end-to-end on xc7vx485tffg1761-2: produces valid LUTRAM segdata
(ALUT/BLUT/CLUT/DLUT.{RAM,SRL,SMALL} + WA7USED/WA8USED/WEMUX.CE), pushes
into segbits_clblm_l/r (clblm coverage 95% -> ~100% vs kintex7).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
xc7vx485t has no IOB33/IOI3 (HR) tiles, so the HR-bank IOB/IOI fuzzers (030-iob,
035-iob-ilogic, 035a-iob-idelay, 035b-iob-iserdes, 036-iob-ologic, 037-iob-pips,
047-hclk-ioi-pips) have no sites. Gate them on a new HAS_HIGH_RANGE_BANKS flag
(0 for virtex7); the HP iob18/ioi18 fuzzers cover the part. Decouple
037-iob18-pips and 047a-hclk-idelayctrl-pips from the now-skipped HR fuzzers.
Kintex-7 and the other families are unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- settings/virtex7.sh: move XRAY_ROI and XRAY_ROI_GRID off the device bottom
edge (SLICE_X0Y50:X11Y99; grid 5-20/261-312). Edge tiles at Y0 can't exercise
features like BRAM36 ECC/cascade, and the bottom-edge BRAM is unsolvable.
- prjxray/segmaker.py: when a tile has no bitstream info (dummy tile, or an edge
tile dropped from the tilegrid such as BRAM_L_X114Y0 on xc7vx485t), account
for any tags on it and skip with a warning instead of asserting. Fixes the
BRAM config/FIFO fuzzers (027, 029, ...) for virtex7; no-op for normal dummy
tiles. Also print the unsolved tags before the all-tags-used assertion.
- fuzzers/Makefile: skip 018-clb-ram for virtex7 (Vivado 2020.1 packs SRL/RAM
into different BEL slots than the fuzzer pins).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Port prjxray to the Virtex-7 family, modelled on Kintex-7, targeting
xc7vx485tffg1761-2 (vc707). Non-breaking for the existing families.
Family registration:
- settings/virtex7.sh, settings/virtex7/devices.yaml
- Makefile: virtex7 in DATABASES/XRAY_PARTS + db-extras-virtex7 targets
- utils/update_parts.py, update_resources.py: virtex7 choice
- CI matrix (Pipeline.yml), Vivado edition (xilinx.sh), README
Architecture adaptations for the HP-bank-only VX part (verified non-breaking):
- update_resources.tcl: fall back to HP banks when no HR banks exist
- XRAY_IOSTANDARD env (default LVCMOS33; LVCMOS18 for virtex7), parameterised
across the fuzzer generate.tcl files
- fuzzers: enable HP-bank (iob18/ioi18) + IOI/HCLK handling for virtex7;
GTX skipped (ffg1761 bonds only ~7 of 14 GTX quads)
- 005-tilegrid: HP/HR bank tile handling; iob18_int INT offset 3->2;
ioi18 AUTO_FRAME; cfg PDRC-2 DRC disable; add_tdb skips unsolved edge tiles;
per-specimen retry for transient FlexLM SIGSEGV under concurrency
- per-family Vivado version gate (virtex7 -> v2020.1.1)
- XRAY_ROI and XRAY_ROI_GRID tuned to a compact CLBLL+CLBLM region
General fixes:
- tools/bitread.cc: fix use-after-free of the mmap'd bitstream (exposed by the
larger Virtex-7 bitstream)
- utils/environment.python.sh: add repo root to PYTHONPATH (PEP 660 editable
install doesn't expose the repo-root utils/ package)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>