Skip to content

Bugfix release

Choose a tag to compare

@hansfbaier hansfbaier released this 02 Aug 20:46
· 113 commits to stable-backports since this release
c13fcbf

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-route scripts (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_atomic unconditionally 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 for bound_nets, causing count(key)==1 paired with at(key)==throw. Replaced with std::map — identical routing output on Linux, correct on Windows.

  • Chipdb POD read past end-of-mapping on Windows fixed. getPipName/getWireName copied 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_clock without a target (common in Vivado-generated XDC) hit an uncaught std::out_of_range. Now warns and skips the command instead of crashing.