Close FPGA timing on a Vivado design driven by the SynthPilot MCP — iterate synthesis/implementation, diagnose critical paths, and apply the safest constraint/strategy fix until worst negative slack (WNS) and total negative slack (TNS) reach ≥ 0. Use when the user wants to close timing, fix negative WNS/TNS, resolve setup/hold violations, or asks why a design "doesn't meet timing". Requires the SynthPilot MCP (Vivado + Tcl server on :9999).
Scanned 9/8/2026
Install to Claude Code
npx -y skills add LNC0831/oh-my-fpga --skill timing-closure --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Timing Closure?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lnc0831-timing-closure)More formats (shields.io, HTML) on the badges page.
---
name: timing-closure
description: >-
Close FPGA timing on a Vivado design driven by the SynthPilot MCP — iterate
synthesis/implementation, diagnose critical paths, and apply the safest
constraint/strategy fix until worst negative slack (WNS) and total negative
slack (TNS) reach ≥ 0. Use when the user wants to close timing, fix negative
WNS/TNS, resolve setup/hold violations, or asks why a design "doesn't meet
timing". Requires the SynthPilot MCP (Vivado + Tcl server on :9999).
argument-hint: "[run name, target clock, or constraint to focus on (optional)]"
---
# Timing Closure
A disciplined loop that drives a Vivado design to timing closure using the SynthPilot
MCP tools. You are the conductor: measure → diagnose → classify → apply the *safest*
fix → re-measure → repeat. **Never make a number go green by hiding a real violation.**
## When to use
- "close timing", "fix WNS/TNS", "why doesn't this meet timing", "resolve setup violations"
- After synthesis or implementation reports negative slack.
## When NOT to use
- No project open / no design to analyze → set the project up first (or use `full-flow-demo`).
- The user wants a one-shot report only, not iteration → just call `report_timing_summary`.
- Hold (min-delay) violations dominate and routing is already done → these usually need
Vivado's own hold-fix; surface that rather than looping.
## Prerequisites (verify first)
- `test_connection` succeeds (Vivado + Tcl server up).
- A project is open (`get_project_info`). If only RTL exists, run synthesis first.
## The loop
### Step 0 — Baseline (measure, don't guess)
1. Ensure a timed design exists. If none, `run_synthesis` (or `run_synthesis_async` +
poll `get_run_status` for large designs). Synthesis WNS is optimistic — true signoff is
post-implementation, but synth WNS is a fast first signal.
2. Read the numbers: `report_timing_summary`, then `extract_timing_metrics` for structured
WNS/TNS/WHS/THS, failing-endpoint count, and per-clock breakdown.
3. Record the baseline (WNS, TNS, #failing endpoints, clocks involved). Every later
iteration is compared against this. **State it to the user.**
If WNS ≥ 0 **post-implementation**: report closed, show the margin, stop. (Post-synth
positive is necessary but not sufficient — confirm with `run_implementation`.)
### Step 1 — Diagnose the worst paths
- `analyze_critical_paths` and `report_worst_timing_paths` → the top failing paths.
- For the worst path, `report_timing_path` / `report_datapath_delay` to split delay into
**logic vs net vs clock-skew/uncertainty**.
- `report_high_fanout_nets` → fanout-driven net delay.
- `get_all_clocks` / `analyze_clock_domain_timing` / `report_clock_interaction` → is the
failing path **intra-clock**, or a **cross-clock / async** path that shouldn't be timed
at all?
### Step 2 — Classify the failure (this decides the fix)
Map the worst paths to one of these, in this priority order:
| # | Signature | Likely cause | Safe fix |
|---|---|---|---|
| A | Failing path crosses **unrelated/async clocks**, or is a config/quasi-static reg | Missing exception | `set_false_path` or `set_clock_groups` (async) / `set_multicycle_path` (multi-cycle) — **only if genuinely valid** |
| B | I/O path failing, no/incorrect input/output delay | Missing I/O constraint | `set_input_delay` / `set_output_delay` from the real interface spec |
| C | Big **net** delay, high fanout | Placement/fanout | implementation strategy (Performance_*), or recommend register duplication / `max_fanout` |
| D | Big **logic** delay, deep combinational path, intra-clock | Real logic depth | recommend RTL pipelining/retiming — do NOT fake-pass with exceptions |
| E | Small negative, close to met | Last-mile | `set_implementation_strategy` Performance_Explore / phys-opt; tighten nothing |
### Step 3 — Apply the smallest safe change
- **Constraints (A/B):** apply the specific exception, then `save_constraints`. Before any
`set_false_path`/`set_multicycle_path`, **state the assumption** ("treating CDC path
reg_a→reg_b as async; valid only if a synchronizer exists") and get confirmation if the
path could be real.
- **Strategy (C/E):** `set_implementation_strategy` (e.g. Performance_ExplorePostRoutePhysOpt)
/ `set_synthesis_strategy`. Cheapest lever, try before touching RTL.
- **RTL (D):** describe the pipelining/retiming change and the expected slack gain;
**recommend, don't silently rewrite** — hand back to the user to approve RTL edits.
- Apply **one class of change per iteration** so cause→effect is attributable.
### Step 4 — Re-measure
- Re-run the affected stage (`run_implementation` for signoff; `run_synthesis` if you
changed RTL/synth strategy). Use the async variants + `get_run_status` for long runs.
- Re-read WNS/TNS via `extract_timing_metrics`. Compare to baseline and to the previous
iteration. Did it improve, regress, or stall?
### Step 5 — Loop or stop
Repeat Steps 1–4 until **one** stop condition:
- ✅ **Closed:** WNS ≥ 0 (and WHS ≥ 0) post-implementation → report margin, list the
changes applied, save constraints, stop.
- ⛔ **No safe move left:** remaining failures are class D (real logic depth) or require
risky exceptions → stop, present the options + trade-offs, hand to the human.
- ⛔ **Stalled:** 2 consecutive iterations with no WNS improvement → stop, don't thrash.
- ⛔ **Budget:** a caller-specified max iterations reached.
## Safety rails (do not violate)
1. **No fake-pass.** Never apply `set_false_path`/`set_multicycle_path`/waivers to a path
that could be real just to clear a violation. If unsure it's safe, ask.
2. **Evidence before claims.** "Timing met" requires a fresh post-implementation
`extract_timing_metrics` showing WNS ≥ 0 — never infer it.
3. **Show your work.** Each iteration: report what you changed, why (which class), and the
before/after WNS/TNS. Keep a running summary table.
4. **One change class per loop** so a regression is traceable.
5. **Hands off RTL** unless the user approves the specific edit.
## Output (each iteration + final)
```
Iter N | change: <what> (class <X>) | WNS <before> → <after> ns | TNS <before> → <after> | failing <n>→<m>
```
Final: closed/not-closed verdict, the ordered list of changes that worked, remaining risks,
and (if not closed) the recommended RTL/architectural next step.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!