# Timing Closure

> 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).

- Skill: `lnc0831/timing-closure` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lnc0831/timing-closure`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lnc0831/timing-closure/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: LNC0831 (https://skillmd.com/u/lnc0831)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/lnc0831/timing-closure

---


# 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.

