git:20260914.2c79f06 to git:20260919.cdf15ac

37 added, 98 removed. Audit A to A.

---
name: timing-analysis
- description: Use when running static timing analysis on synthesis netlist, analyzing setup/hold violations, reviewing timing reports, or re-analyzing after synthesis changes; not for synthesis or power analysis.
+ description: Assess post-synthesis setup and hold timing with PrimeTime and determine what the evidence establishes under the task's constraints.
---
# Static Timing Analysis
- Your sole responsibility: run PrimeTime over the post-synthesis netlist, independently of the
- timing engine inside synthesis, and close the run through the `timing` CLI. You never grade setup
- or hold by eye — `finalize` classifies both off the report and writes the verdict.
-
- ## Iron Rule
-
- - Write only under `{workdir}`. Every injected input location is read-only, as is every other
- stage's output.
- - **Scripts are black boxes, never Read their source.** Invoke them per this skill's documented
- command lines (flags via `--help`); on a non-zero exit act on the documented failure protocol
- (stderr, stdout verdict), not the source. Sole exception: debugging a suspected bug in a
- script itself.
-
- ## What you read, and what you produce
-
- `<skill>` is this skill's own base directory, named on the first line of this file.
-
- `{workdir}/dispatch.json` carries the `inputs` table. You open one thing it points at:
- `<requirements>/requirements.json`, whose rows judged by `timing-analysis` are yours — the report
- is the evidence, and you declare a verdict on each through `finalize`; a row that points at a file
- under `<intent>/` is read there. The rest `bootstrap`
- handles: it resolves `<TOP>` from the single `out/<TOP>_syn.v` under the synthesis stage root and
- bakes absolute paths into the TCL, which reads that netlist and the SDC synthesis exported
- beside it.
-
- The one thing you supply is `LIB_DB`, the std-cell Liberty `.db` synthesis linked against.
-
- Everything under `{workdir}` is produced by the tools you invoke, and `finalize` enumerates it
- into `artifacts[]`: the deployed `run_sta.tcl` and `config.tcl`, plus `timing-report.txt` — the
- setup, hold and `check_timing` output that is this stage's deliverable and the only thing the
- gate reads.
+ Assess the implementation under the required timing conditions. Read `dispatch.json`, original intent,
+ requirements, the synthesized netlist and SDC, and the materials carried into `{workdir}`. Determine the necessary
+ work from what changed. An existing report may support a new judgment when its implementation,
+ conditions and analysis scope still apply. The implementation input is the complete synthesis
+ `out/` directory, including supporting constraints; a changed package calls for reassessment.
- ## Workflow
+ Write under `{workdir}`; upstream inputs remain read-only. The framework carries previously
+ published setup and products into this directory, without the old verdict. Keep useful analysis
+ and comparison material in `evidence/`.
- ### 1. Deploy
+ ## Tools
- Export `LIB_DB`, then run `bootstrap` to lay down the run scaffold:
+ `<skill>` is this skill's directory. Prepare missing editable setup when needed:
```bash
- export LIB_DB=<path-to-slow.db>
python3 <skill>/scripts/timing/__main__.py bootstrap --workdir {workdir}
```
- It deploys `run_sta.tcl` + `config.tcl`, resolves `<TOP>`, verifies the netlist and SDC the TCL
- reads, and aborts when `{workdir}` already holds a deployment. `pt_shell` reads `LIB_DB` out of
- the `config.tcl` written here rather than out of the environment, so exporting it afterwards
- changes nothing: bootstrap refuses to deploy without it instead of leaving you a workdir whose
- STA cannot run. Non-zero exit: stderr names the cause, and nothing was deployed, so the retry is
- not blocked.
-
- ### 2. Run the STA
-
- Run PrimeTime from the workdir, so its auto-logs (`pt_shell_command.log`, `.svf`) land inside the
- gitignored workdir rather than the tree root:
+ Bootstrap locates the netlist and matching SDC, installs missing `run_sta.tcl` and `config.tcl`, and
+ preserves authored files. Set the actual cell library as `LIB_DB` in `config.tcl` and inspect the calculation
+ settings. Run the required analysis:
```bash
- cd {workdir} && pt_shell -f run_sta.tcl
+ cd {workdir}
+ python3 <skill>/scripts/timing/__main__.py run --workdir .
```
- The TCL reports setup and hold, runs `check_timing`, counts the boundary, and redirects all of
- it into `{workdir}/timing-report.txt`. Read what it printed rather than what it returned:
- `pt_shell` exits 0 even on a script error, so its exit code settles nothing.
+ The entrypoint computes in a temporary directory using the current setup. Scripts write outputs
+ relative to their working directory: `timing-report.txt` and any supplementary `reports/` tree.
+ Old reports are withdrawn and replaced only after successful,
+ readable output. The log and failed calculation remain available until retry. Wait for completion
+ and inspect warnings, effective constraints and the libraries used.
- ### 3. Close
+ ## Judge and close
- Run `finalize` to write the envelope. Every run ends here, a `pt_shell` that never reached the
- report included, and you never hand-assemble it:
+ Review `check_timing` and untested checks against port roles and constraints. The CLI compares
+ output-bit count with PrimeTime's `out_setup` Total, which includes Untested entries; matching
+ counts do not establish that every output was timed.
+ Numerical `timing_slack_ns` rows compare the minimum worst setup/hold slack with their own bounds;
+ VIOLATED identifies negative slack even when displayed as zero. Overall STA acceptance separately
+ requires both directions to be MET. Give evidence-based judgments for other timing requirements.
```bash
- python3 <skill>/scripts/timing/__main__.py finalize \
- --workdir {workdir} [--fix-owner <rule>] \
- [--fail-reason "<cause>"] \
- [--requirements '[{"id": "R-249", "met": true, "actual": "setup +0.31 ns, hold +0.12 ns", "measured": "reports/timing_setup.rpt and timing_hold.rpt, worst path each"}]']
+ python3 <skill>/scripts/timing/__main__.py finalize --workdir {workdir} \
+ [--requirements '[{"id":"R-1","met":true,"actual":"measured value","measured":"report and scope"}]'] \
+ [--fail-reason "unresolved cause"] [--fix-owner <rule>]
```
- It classifies each direction on the report's `(MET)` / `(VIOLATED)` marker — never the displayed
- number, which prints `0.00` for a violation smaller than the reported precision — records the
- worst slack and worst path per direction into `stage_specific.timing`, folds your
- `--requirements` verdicts in as `stage_specific.requirements[]` — refusing an envelope that leaves any `timing-analysis` row
- unjudged, and failing the run on any `met: false` — reads the PrimeTime version off the report
- header, and enumerates `artifacts[]`. Every verdict carries `measured` — which report, which row, which scope you read it from — because the row names a dimension and `measured` is what that name indexed.
-
- Two MET markers are not enough for a pass. The markers describe the paths PrimeTime analyzed and
- say nothing about the ones it was never asked to, so `finalize` also compares how many output
- bits the run timed against how many the design has: short of that, the STA reached only part of
- the boundary and the verdict does not cover the design. That is a `tooling` fail rather than a
- `ppa` one, and the SDC it read is synthesis's export of what specification declared, so read both
- before naming the owner.
-
- The check is on outputs alone, and `check_timing`'s unconstrained-endpoint count is in the report
- for you rather than for the gate. Input timing depends on port role: clocks are declared as clocks,
- synchronous resets receive input delays, and asynchronous resets are excluded from those delays.
- Read the unconstrained endpoints against the port roles and SDC before judging them.
-
- The flags carry what the report cannot:
-
- - **`--fail-reason`**, which fills `stage_specific.fail_reason`, when `pt_shell` produced nothing
- gradeable: no license, a `link_design` or `read_sdc` abort, a crash after the redirect opened.
- You are the one who watched it run. Supplying it is itself the declaration of failure, so it
- wins over the gate; write the cause you actually read rather than a category, since nothing
- parses it.
- - **`--fix-owner`** on every failure, license failures included, since it is what fills
- `stage_specific.fix_owner`. A `fail_reason` naming the guilty stage in prose while the flag was
- omitted reads to the caller as "this stage could not tell", and brings a human in to re-derive
- an answer you already had. You cannot edit the netlist or the constraints, so the owner is
- never this stage: name the producer of whichever input you read and found wrong, and omit the
- flag only when your own environment broke or you read the evidence and still cannot say.
-
- Exit 0 means written, pass or fail. Exit 2 is BLOCKED and never a `status=fail`: an empty
- `--fail-reason`, or a program exception. stderr names which.
-
- ## Return Contract
+ Use `--fail-reason` for invalid or incomplete analysis even if a report contains numbers. Name the
+ owner from the defect: local analysis errors belong here; incorrect implementation or constraints
+ belong to their producer. Resolve local problems and retry the affected work. Acceptance changes
+ follow the user's actual authorization.
- Emit `STATUS: DONE` as your last line once `result.json` exists, or
- `STATUS: BLOCKED <one-line reason>` when nothing could be written. What runs next is the
- caller's decision, taken from `result.json`.
+ Finalize withdraws the previous result before judging. Exit 0 means a pass/fail result was written;
+ return `STATUS: DONE`. On a nonzero exit, resolve the cause or return `STATUS: BLOCKED <cause>`.