Systematic timing closure for high-utilization Xilinx/AMD 7-series FPGA designs.
A 14-stage escalating pipeline combining graph-based netlist analysis, ML timing models, multi-directive placement exploration, and surgical post-route optimization. 36 tools (22 Tcl, 14 Python), zero additional licenses required.
Developed from real-world experience closing 200 MHz timing on a Zynq XC7Z010 at 87% LUT utilization, where Vivado's default algorithms fail with WNS of -2.0 to -4.8 ns. Synthesizes techniques from 17 published papers and adds unique innovations not found in any individual work.
Author: Nils Achermann License: MIT Tested on: Vivado 2025.2 (compatible with 2018.3+)
The full technical paper describes the methodology, mathematical models, experimental results, and comparison with related approaches:
Systematic Timing Closure for High-Utilization Xilinx FPGA Designs (PDF)
Key results from the paper:
- Timing model trained on 16,551 real Vivado STA paths: R² = 0.86, MAE = 0.36 ns
- Multi-directive sweep achieves +0.210 ns WNS improvement over Vivado defaults
- UCB1 Multi-Armed Bandit identifies optimal directive within 8 evaluations
- Ablation study reveals when overconstraining helps vs. hurts at different deficit levels
vivado-timing-optimizer/
├── vtopt # Unified CLI entry point
├── Makefile # make test / make check
├── requirements.txt # Python dependencies
│
├── docs/paper/
│ ├── vivado_timing_optimization.pdf # Technical paper (20 pages)
│ └── figures/ # Publication figures
│ ├── system_arch_v3_src.pdf # System architecture diagram
│ ├── fpga_arch_tikz_src.pdf # XC7Z010 die layout
│ ├── graph_partitioning_src.pdf # Graph analysis algorithm
│ ├── timing_model_scatter.png # ML model R²=0.86
│ ├── wns_convergence_mab.png # MAB convergence (N=10 seeds)
│ ├── wns_waterfall_breakdown.png # Per-stage WNS waterfall
│ ├── directive_wns_validate1.png # Directive sweep results
│ └── ... # 15 figures total
│
├── tcl/ # 22 Vivado Tcl scripts
│ ├── build_optimized.tcl # Main 14-stage pipeline
│ ├── multi_directive_impl.tcl # 4-run parallel directive sweep
│ ├── critical_path_surgery.tcl # ML-scored cell movement
│ ├── useful_skew.tcl # Post-route clock skew insertion
│ ├── pvt_signoff.tcl # Multi-corner PVT sign-off
│ ├── overconstrain.tcl # Pre-place clock tightening
│ └── ... # See Tools Reference below
│
├── src/ # 14 Python analysis tools
│ ├── graph_analyzer.py # Spectral bisection + Louvain
│ ├── timing_model.py # Ridge/GB regression training
│ ├── force_directed_refine.py # CARRY4-aware placement
│ ├── mab_orchestrator.py # UCB1 directive autotuning
│ ├── calibrate_interconnect.py # PIP delay calibration
│ ├── eco_optimizer.py # RapidWright ECO engine
│ └── ... # See Tools Reference below
│
├── tests/ # Test suite (27 tests)
│ └── test_optimizer.py
│
├── examples/zynq_7010/ # Example project configuration
│ └── project_config.tcl
│
└── checkpoints/ # Golden DCP storage (runtime)
# Install Python dependencies
pip install -r requirements.txt
# Full optimized build (14-stage pipeline)
./vtopt build myproject.xpr run01 i_ch0 i_ch1 i_ch2 i_ch3
# Individual analysis tools
./vtopt analyze --graph /tmp/vtopt_netlist_graph.csv
./vtopt train --file /tmp/vtopt_timing_training.csv
./vtopt wns --dir checkpoints/
# Check installation
./vtopt status# Full pipeline
vivado -mode batch \
-source tcl/build_optimized.tcl \
-tclargs myproject.xpr run01 i_ch0 i_ch1
# Skip synthesis (re-run implementation only)
VTOPT_SKIP_SYNTH=1 vivado -mode batch \
-source tcl/build_optimized.tcl \
-tclargs myproject.xpr run01
# Tcl-only mode (skip Python analysis stages)
VTOPT_SKIP_ANALYSIS=1 vivado -mode batch \
-source tcl/build_optimized.tcl \
-tclargs myproject.xpr run01| Variable | Default | Description |
|---|---|---|
VTOPT_SKIP_SYNTH |
0 | Skip synthesis (reuse existing netlist) |
VTOPT_SKIP_ANALYSIS |
0 | Skip Python graph analysis (stages 4-5, 8) |
VTOPT_SKIP_POSTROUTE |
0 | Skip post-route fixes (stages 11-14) |
VTOPT_DRY_RUN |
0 | Report-only mode for post-route stages |
VTOPT_JOBS |
nproc/4 | Vivado parallel jobs per run |
VTOPT_MAX_SURGERY |
20 | Max cells to move in path surgery |
VIVADO_BIN |
auto-detect | Path to Vivado binary |
The pipeline applies progressively more aggressive optimization and stops as soon as WNS >= 0 ns. Easy designs close at stage 10; hard designs get surgical post-route fixes through stages 11-14.
| Stage | Tool | Description |
|---|---|---|
| 1 | build_optimized.tcl |
Synthesis with retiming + flatten |
| 2 | parse_qor_suggestions.py |
Parse Vivado ML-based QoR suggestions |
| 3 | Built-in | opt_design -control_set_merge |
| 4 | graph_analyzer.py |
Spectral bisection, Louvain clustering, CARRY4 detection |
| 5 | cell_replication.py |
Clone high-fanout FFs spanning 2+ clock regions |
| 6 | evict_non_critical.tcl |
Push 100 MHz logic to die edges (anti-pblocks) |
| 7 | pblock_auto.tcl |
I/O-aware channel-to-clock-region pblocks |
| 8 | Generated | Louvain-derived micro-pblocks (IS_SOFT TRUE) |
| 8a | overconstrain.tcl |
Pre-place: add +200 ps setup uncertainty |
| 9 | multi_directive_impl.tcl |
4 parallel runs with orthogonal directives |
| 10 | build_optimized.tcl |
Select winner by WNS comparison |
| 11 | lut_pin_swap.tcl |
Swap critical nets to LUT6 fast I5 pin (50-100 ps) |
| 12 | critical_path_surgery.tcl |
ML-scored surgical cell movement |
| 13 | eco_optimizer.py |
RapidWright ECO: fanout split, LUT merge |
| 14 | force_directed_refine.py |
CARRY4-aware spring + Coulomb placement |
Exports the post-synthesis netlist as a weighted graph where edge weights combine timing criticality and connectivity:
W_ij = alpha * exp(-lambda * S_ij / T) + beta * C_ij
Spectral bisection partitions the graph into clock-region-aligned clusters. Louvain community detection generates micro-pblock constraints. CARRY4 chains are detected and treated as atomic macro-nodes.
A gradient-boosted ensemble trained on 16,551 real Vivado STA paths predicts path delay from geometric features (Manhattan distance, fanout, clock-region crossing, cell types). Validated R² = 0.86, MAE = 0.36 ns.
The model exports as a Tcl expr formula for zero-latency delay estimation
during surgical cell movement (no Python round-trip).
Four parallel implementation runs explore orthogonal optimization spaces:
| Run | place_design |
phys_opt |
Strategy |
|---|---|---|---|
| 0 | Explore | AggressiveExplore | Broad search |
| 1 | ExtraTimingOpt | Explore | Timing-focused |
| 2 | AltSpreadLogic_high | AggressiveExplore | Congestion relief |
| 3 | AltSpreadLogic_low | Explore | Light spreading |
Automates directive selection across builds using UCB1 bandit strategy over 8 Vivado directive combinations with 4-slot parallel batching. Identifies the optimal directive within 8 evaluations across 10 independent seeds, requiring no commercial license.
CARRY4-aware placement engine using spring attractions (Hooke's law with timing-weighted edges) and Coulomb repulsion from congestion hotspots. CARRY4 chains are rigid vertical spines that never cross the HROW boundary.
These techniques are not found in any of the 17 referenced papers:
- Anti-Pblock Eviction — Evict 100 MHz logic to die edges, creating routing vacuum for 200 MHz critical paths
- LUT Pin Asymmetry Optimization — Post-route swap of critical nets from slow I0 to fast I5 pin (50-100 ps gain per LUT)
- CARRY4 Rigid Macro Handling — Entire carry chain as atomic node in force-directed placement, enforcing HROW boundary constraint
- 14-Stage Escalating Pipeline — Progressive optimization that stops when WNS >= 0, combining the best of 17 papers into a unified flow
| Tool | Description |
|---|---|
vtopt |
Unified CLI — single entry point for all tools |
tcl/build_optimized.tcl |
Main 14-stage pipeline |
tcl/multi_directive_impl.tcl |
4-directive parallel runner |
tcl/incremental_build.tcl |
Checkpoint save/restore |
| Tool | Description |
|---|---|
tcl/netlist_graph_export.tcl |
Export netlist as CSV edge-list |
src/graph_analyzer.py |
Graph analysis: spectral bisection, Louvain, CARRY4 |
src/cell_replication.py |
High-fanout FF replication candidates |
tcl/apply_replication.tcl |
Apply cell clones in Vivado |
src/parse_qor_suggestions.py |
Parse Vivado QoR suggestions |
| Tool | Description |
|---|---|
tcl/pblock_auto.tcl |
I/O-aware pblock creation |
tcl/evict_non_critical.tcl |
Anti-pblocks: push slow logic to edges |
tcl/overconstrain.tcl |
Pre-place: +200 ps setup uncertainty |
tcl/relax_constraint.tcl |
Pre-route: remove overconstrain |
| Tool | Description |
|---|---|
tcl/lut_pin_swap.tcl |
Fast LUT I5 pin optimization |
tcl/critical_path_surgery.tcl |
ML-scored cell movement |
tcl/phys_opt_loop.tcl |
Iterative phys_opt (3 directives) |
tcl/useful_skew.tcl |
Clock skew insertion for margin recovery |
tcl/pvt_signoff.tcl |
Multi-corner setup + hold verification |
tcl/hold_fix.tcl |
Hold violation XDC generation |
tcl/run_stages_11_12.tcl |
Standalone post-route stage runner |
| Tool | Description |
|---|---|
tcl/timing_model_export.tcl |
Extract training data from routed design |
src/timing_model.py |
Train Ridge/GB regression, export Tcl formula |
tcl/timing_model_lut.tcl |
Auto-generated expr delay predictor |
src/calibrate_interconnect.py |
PIP delay calibration (RapidRoute method) |
src/gp_timing_model.py |
Gaussian process timing model |
| Tool | Description |
|---|---|
src/force_directed_refine.py |
CARRY4-aware force-directed engine |
tcl/fd_export_placement.tcl |
Export current placement as CSV |
tcl/fd_apply_placement.tcl |
Apply LOC constraints |
src/eco_optimizer.py |
RapidWright ECO: fanout split, LUT merge |
src/rw_analyzer.py |
DCP connectivity analysis |
| Tool | Description |
|---|---|
src/mab_orchestrator.py |
UCB1 Multi-Armed Bandit directive selector |
tcl/seed_sweep.tcl |
Multi-seed placement exploration |
tcl/pip_delay_model.tcl |
PIP category delay extraction |
| Tool | Description |
|---|---|
tcl/timing_classify.tcl |
Failure classification (4 categories) |
src/wns_tracker.py |
WNS regression table, CI gate |
src/eco_candidates.py |
ECO candidate analysis |
src/generate_synthetic_training.py |
Synthetic training data generator |
- Vivado 2018.3+ (tested on 2025.2)
- Python 3.8+ with: networkx, scikit-learn, numpy, pandas, scipy, matplotlib
- RapidWright (optional, stages 13-14):
pip install rapidwright+ JDK 11+ - Any Xilinx/AMD 7-series, UltraScale, or Zynq device
pip install -r requirements.txtThese rules prevent the dominant timing violation patterns at high utilization:
| Rule | Fix | Typical Gain |
|---|---|---|
| T1 | Pre-register LUTRAM reads on control paths | +3 to +5 ns |
| T2 | Pipeline wide comparisons before FF CE/D | +0.5 to +1 ns |
| T3 | Migrate overloaded distributed RAM to BRAM | saves 300+ LUTs |
| T4 | Match DSP48 A-input width for AREG=1 | absorbs 1 pipeline stage |
| T5 | Use synchronous reset for DSP retiming | avoids DSP regression |
| T6 | No reset on pure datapath registers | eliminates fanout |
| T7 | case instead of if-else for parallel decode |
-2 LUT levels |
| T8 | max_fanout on high-fanout control regs |
avoids late replication |
This framework synthesizes techniques from 17 published papers:
- TD-Placer — timing-driven reweighting (lambda=20)
- AMF-Placer 2.0 — sub-linear distance model, fractional-power features
- GPlace3 — multi-electrostatic placement, criticality formula
- UTPlaceF — dynamic area adjustment for congestion
- RapidRoute — PIP delay calibration methodology
- RapidWright — 7-series ECO operations and limitations
- DREAMPlaceFPGA — macro legalization
- RapidLayout — cascade constraints
- InTime — commercial directive exploration (comparison baseline)
- DATuner — multi-fidelity autotuning
- ML Routing Congestion — validated on Zynq XC7Z020
- And 6 more (see paper for complete bibliography)
Author: Nils Achermann Issues and pull requests welcome.