Bugfix release
Timing & Analysis
-
Post-route per-clock Fmax for Python scripts. New
reportClockFmaxJson()Python API returns a JSON map of{"<clock>": {"achieved": MHz, "constraint": MHz}}— fixing the gap where--post-routescripts (e.g. apio's timing flow) had no way to read routed frequencies. -
LUT self-loop arcs no longer deadlock timing analysis. A cell input bound to its own output net (post-route pin fixup) is now skipped in the topological sort instead of appearing as a false combinational loop and aborting the walk.
-
Fractured LUT shared pins no longer create false timing arcs. Post-placement legalisation binds shared pins to both cells of a fractured pair; timing now checks
X_ORIG_PORT_<pin>and only reports an arc when the cell's function actually uses the pin. Fixes deadlocked post-route timing walks. -
Combinational DSP48E1 timing (phase 1). Fully-combinational DSPs (all registers bypassed) are no longer invisible to timing analysis. A conservative per-input propagation delay is now reported (worst-case over SDF variants, validated against Vivado 2026.1 reference runs). Registered DSPs keep their existing behavior; accurate per-variant delays are follow-up work.
Routing & Placement
-
Global VCC/GND nodes now span every tile. Previously, the constant-driver BEL in a tile whose GLBL wire wasn't part of the global node could only reach its own row, causing "Invalid global constant node" routing failures. All tiles' GLBL wires are now included (~0.5 MB per chipdb). Fixes #38 and #41 (gatecat#54).
-
LUT6_2 packing fixed by splitting into a LUT5/LUT6 pair. The old packing rule mapped LUT6_2 with the LUT6 transform, which only renamed port "O" — leaving O5 without a bel pin and aborting routing with "No wire found for port O5". LUT6_2 cells are now split into two ordinary LUT cells (O5 from INIT[31:0], O6 from the half selected by I5). When both halves are five-input functions they're constrained back onto one site, so a LUT6_2 still costs one physical LUT. Genuine six-input O6 halves are left unconstrained and occupy two LUTs.
-
Imported BEL-pinned carry children no longer fail constraint checks.
pack_carries_atomicunconditionally set an absolute z on CARRY4 feed-through LUTs; when the child was already BEL-pinned from an imported placement on a non-standard column (e.g. Virtex-7 X43), the absolute z disagreed with the actual BEL and the constraint legaliser aborted. Absolute z is now only imposed on nextpnr-inserted feed-throughs.
Cross-Platform / Stability
-
Router2 crash on Windows fixed. Under mingw-w64,
boost::container::flat_map's sorted invariant broke forbound_nets, causingcount(key)==1paired withat(key)==throw. Replaced withstd::map— identical routing output on Linux, correct on Windows. -
Chipdb POD read past end-of-mapping on Windows fixed.
getPipName/getWireNamecopied full POD records by value, reading trailing bytes past the memory-mapped file on the last record — a hard page fault on Windows, benign UB on Linux/macOS. Changed to const references.
XDC / Constraints
- Virtual clocks no longer crash the tool.
create_clockwithout a target (common in Vivado-generated XDC) hit an uncaughtstd::out_of_range. Now warns and skips the command instead of crashing.