Creating & Managing the Clock Spec
The clock tree specification — often shortened to "spec" or "constraints" — is the contract between the designer's intent and the synthesis engine. Before a single buffer is placed, the tool needs to know which nets are clocks, where those clocks should terminate, what the latency and skew goals are, and which library cells it is permitted to use. The spec captures all of this. Get it right and synthesis becomes a controlled optimization toward your targets. Get it wrong — leave a generated clock undefined, forget to exclude a feedback pin, allow a high-drive cell that violates electromigration rules — and the tool will dutifully build a tree that meets a specification you never intended. This chapter treats the spec as a living object with a lifecycle: it is derived, reviewed, modified, validated, regenerated, and eventually deleted. Mastering that lifecycle is what separates an engineer who can run a flow from one who can debug it.

How a Specification Is Produced
There are two complementary paths to a spec, and a real flow uses both. Automatic derivation. The tool walks the timing graph starting from every defined clock root (the points declared with create_clock and create_generated_clock in the timing constraints). From each root it traces forward through the clock network, honoring how clocks propagate through buffers, dividers, gates, and multiplexers, until it reaches the sinks — the clock pins of sequential elements and macros. Along the way it classifies pins, identifies where clock domains meet, and proposes default targets (skew, insertion delay, transition) plus a default set of legal buffer and inverter cells drawn from the library. The output is a structured, editable description of how each clock should be built. Manual authoring and overrides. Automatic derivation is only as good as the upstream constraints and the tool's heuristics. Anything the tool cannot infer — a non-default skew group, a macro whose internal insertion delay must be modeled as a pin delay, a feedback loop that must be ignored — you author or override by hand. In practice the discipline is: let the tool derive the bulk, then surgically correct the parts it cannot know. It is worth being precise about why the two paths are complementary rather than redundant. Automatic derivation excels at the mechanical, error-prone work that humans do badly: enumerating thousands of sink pins, tracing every branch of a deeply nested clock network, and binding each entry to a concrete netlist object. No engineer should type out sink lists by hand. Manual authoring, conversely, excels at the judgment calls that the tool has no information to make: how much skew this particular domain can tolerate, which physical cells are blessed for clock use on this technology node, and which structural pins represent function versus feedback versus test. The boundary between the two is the
boundary between enumeration and intent. Keeping that boundary clear in your head — and in your scripts — is the foundation of a maintainable flow.
Automatic Specification Creation
A typical command to bootstrap the spec in automatic mode looks like this:
# Derive the full clock tree specification from existing timing constraints
create_clock_spec -auto \
-clocks {clk_core clk_periph pll_out} \
-output ./specs/clock_spec.txt
# Inspect what was inferred before trusting it
report_clock_spec -clocks {clk_core} -verbose
What the tool typically infers automatically:
| Inferred item | Basis for inference | Why you must still review it |
|---|---|---|
| Sink pins | Forward trace from clock root to register clock pins | Gated or conditionally clocked sinks may be missed or wrongly included |
| Stop / non-stop pins | Pin function (e.g., terminates at a register) | Feedback and reconvergent paths may need manual stop/ignore |
| Default skew / | Generic library and global defaults latency targets | Almost never the values your timing budget actually wants |
| Legal buffer / inverter | All cells of the right function in the | Often too permissive — includes cells you |
| list | library | must not use |
| Transition / | Library max_transition defaults | May be looser than your sign-off design rules |
capacitance limits
| Clock domain | Points where two clocks meet | Cross-domain handling may need explicit |
|---|---|---|
| boundaries | exclusion |
The lesson: automatic derivation produces a starting point, not a finished spec. Treat every inferred default as a hypothesis to confirm. A useful review habit is to compare the inferred sink count against an independent expectation. If you know the block contains roughly forty thousand flops on clk_core , and the spec reports thirty-one thousand sinks, something swallowed nine thousand of them — a missing generated clock, an overbroad exclude, or a don't-touch segment that severed a branch. Sink-count arithmetic is one of the fastest sanity checks available, and it catches whole classes of derivation errors before they ever reach synthesis. Similarly, scanning the inferred cell list for any part outside your approved subset takes seconds and prevents the engine from reaching for a 32x clock buffer that the EM methodology forbids.
Common Specification Modifications
Once the spec exists, you shape it to match the design's timing budget and the foundry's rules. The most frequent edits fall into a handful of categories. Changing targets (skew, latency, transition). The global defaults rarely match a real budget. You tighten skew where setup/hold margin is scarce, relax it where it buys nothing, and set an insertionlatency target that balances power against on-chip-variation (OCV) derating.
# Per-clock target overrides
set_clock_spec_target -clock clk_core -skew 30ps -max_latency 600ps
set_clock_spec_target -clock clk_periph -skew 80ps -max_latency 900ps
# Tighten transition below the library default for cleaner edges
set_clock_spec_target -clock clk_core -max_transition 80ps
Restricting the cell list. The single highest-leverage edit. Narrowing the buffer/inverter list controls drive strength, leakage, area, and EM behavior. Many teams maintain an approved "clock cell" subset and forbid everything else.
# Replace the auto-derived list with an approved subset
set_clock_spec_cells -clock clk_core \
-buffers {CKBUF_X4 CKBUF_X8 CKBUF_X16} \
-inverters {CKINV_X4 CKINV_X8}
# Forbid a problematic cell everywhere
remove_clock_spec_cells -all -cells {CKBUF_X32}
Adding exclude / ignore pins. Feedback paths (PLL feedback, divider returns), test-clock muxes, and pins that should not be balanced are marked so the engine neither traces through them nor tries to meet skew at them.
# Stop tracing at a PLL feedback pin; ignore a scan-only sink for balancing
add_clock_spec_exclude -pins {pll_inst/FBKIN}
add_clock_spec_ignore -pins {scan_clk_mux/Y}
Marking don't-touch. Pre-existing structure — a hand-built root, a hard macro's internal tree, an ECO-protected segment — is protected so synthesis routes around it rather than rebuilding it.
set_clock_spec_dont_touch -objects {clkroot_buf hm_cpu/CLK}
Per-clock overrides and pin insertion delays. When a sink hides internal latency (a macro clock pin that fans out internally, or a hierarchical block already balanced), you model that delay so the parent tree balances to the silicon, not just to the boundary pin.
# Model 120 ps of internal latency behind a macro clock pin
set_clock_spec_pin_delay -pins {hm_cpu/CLK} -delay 120ps -type insertion
The table below is a quick reference for the modifications you will reach for most often.
| What | Why | Generic command |
|---|---|---|
| Tighten / relax skew | Match real setup-hold budget per | set_clock_spec_target -skew |
domain
Cap insertion latency Balance power vs. OCV derate set_clock_spec_target -
| Restrict cell list | Control drive, leakage, EM, area | set_clock_spec_cells |
|---|---|---|
| Forbid a cell globally | Remove a DRC/EM offender | remove_clock_spec_cells |
| Exclude a pin from | Break feedback / reconvergence | add_clock_spec_exclude |
tracing
Ignore a pin for Skip non-functional or test sinks add_clock_spec_ignore
| Don't-touch a segment | Protect pre-built or ECO structure | set_clock_spec_dont_touch |
|---|---|---|
| Model pin insertion | Account for hidden macro/block delay | set_clock_spec_pin_delay latency |
Updating Annotations After the Spec Is Created
The spec is not a one-time artifact. The design moves underneath it: floorplan changes, macros get placed, clocks get added or restructured, hierarchy is flattened, or upstream constraints are revised. Each of these can invalidate the spec's annotations — its stored notion of which pins are sinks, where roots are, and what the exclusions mean. Rather than regenerate from scratch every time, you refresh the annotations so the spec re-binds to current netlist objects while preserving your manual edits.
# Re-bind sink/root/exclude annotations to the current netlist
update_clock_spec_annotations -clocks {clk_core clk_periph}
# Confirm nothing your edits touched got orphaned by the netlist change
report_clock_spec -unannotated
A pin you excluded may have been renamed; a sink you tightened may no longer exist; a new register fanout may have appeared with no target assigned. The -unannotated style report is your safety net — it lists spec entries that no longer map to a real object, which are precisely the silent failures that produce a tree meeting the wrong constraints.
A practical rule: run an annotation update after any change to the netlist, the floorplan, or the timing constraints, and re-report before synthesizing. The reason annotation drift is so dangerous is that it fails silently and plausibly. A spec that points at a renamed pin does not throw an error during synthesis; it simply omits that pin from balancing, and the resulting tree looks healthy in every summary report. The failure only surfaces much later — in a hold violation at sign-off, or worse, in silicon — long after the cheap moment to catch it has passed. This is why the validation step is non-negotiable: the unannotated report is the only place where a stale spec announces itself loudly. Make it part of the muscle memory of every iteration, the same way you would never tape out without checking for unconstrained paths.
Deleting and Regenerating Cleanly
Sometimes the cleanest fix is to throw the spec away. After major restructuring — a new clocking scheme, a different macro set, a fundamentally changed floorplan — incremental edits accumulate into an inconsistent, hard-to-debug state. A clean regeneration is faster and safer. The trap is leaving stale state behind. Deleting a spec must remove all of its annotations and overrides, not just the visible target lines, or the next derivation inherits ghosts of the old one.
# Remove the entire spec and all associated annotations
delete_clock_spec -all
# Verify a clean slate before re-deriving
report_clock_spec ;# should report no active specification
# Re-derive from the (now corrected) constraints
create_clock_spec -auto -clocks {clk_core clk_periph pll_out}
If you only need to regenerate one clock — say clk_periph was rebuilt while clk_core is stable and approved — scope the delete so you do not disturb work you trust.
delete_clock_spec -clocks {clk_periph}
create_clock_spec -auto -clocks {clk_periph}
The lifecycle operations as a whole:
| Operation | Purpose | Generic command |
|---|---|---|
| Derive | Bootstrap spec from constraints | create_clock_spec -auto |
| Report | Inspect inferred / current state | report_clock_spec |
| Modify | Apply targets, cells, exclusions | set_clock_spec_* , add_clock_spec_* |
| Update | Re-sync annotations to netlist | update_clock_spec_annotations |
| Validate | Catch orphans / conflicts | report_clock_spec -unannotated |
| Delete | Clear spec and annotations | delete_clock_spec |
| Regenerate | Clean re-derivation | delete_clock_spec then create_clock_spec -auto |
A Disciplined Workflow
The five-step loop below is worth internalizing. Each step has a fail-fast check so problems surface at the cheapest possible moment rather than after hours of synthesis.
- 1. Derive. Run automatic derivation against complete, sane timing constraints. If a clock is missing
from the spec, your create_clock / create_generated_clock set is incomplete — fix the constraints, not the spec.
- 2. Review. Report the spec and read it like a reviewer who assumes the tool guessed wrong. Confirm
sink counts per clock against expectation, scan the cell list for forbidden parts, and verify domain boundaries.
- 3. Modify. Apply targets, cell restrictions, exclusions, don't-touch, and pin delays. Keep these edits in
a separate, version-controlled script so they are reproducible and survive a regeneration.
- 4. Validate. Re-report and explicitly check for unannotated entries and conflicting targets. A skew
target tighter than achievable, or an excluded pin that is also a required sink, should be caught here.
- 5. Regenerate. When the netlist or floorplan moves, prefer
update_clock_spec_annotations; when
the change is structural, delete_clock_spec and re-derive, then re-apply your modification script.
# Reproducible spec construction: derive, then source your overrides
create_clock_spec -auto -clocks {clk_core clk_periph pll_out}
source ./scripts/clock_spec_overrides.tcl ;# all manual edits live here
update_clock_spec_annotations
report_clock_spec -unannotated -conflicts
Keeping every manual edit in clock_spec_overrides.tcl is the single habit that makes the whole lifecycle manageable. When you regenerate, you re-source one file and your intent is restored exactly. When you debug, the file is the documentation of every deviation from the tool's defaults.

Interview Q&A
derivation infers from upstream constraints and generic defaults. It cannot know your real per-domain skew budget, your foundry's approved clock-cell subset, your EM rules, or which feedback and test pins must be excluded. Automatic mode gives a correct structure with frequently wrong targets and permissions. Skipping review means synthesizing a tree that meets a specification you never intended — the classic "the tool met timing but the design is wrong" failure.
timing constraints, not the spec. Automatic derivation only traces clocks that are declared with
create_clock or create_generated_clock . A generated clock through a divider or gate that was
never declared simply won't be traced, so its sinks never enter the spec. Fix the constraint, then rederive. Other suspects: the clock root sits behind an excluded/ignored pin, or its path is broken by a don't-touch segment.
Default to updating annotations — it re-binds sinks, roots, and exclusions to the current netlist while preserving all your manual edits, then you validate with an unannotated report. Full deletion and rederivation is reserved for structural change: a new clocking scheme, different macros, or revised clock constraints. The cost of a needless full regeneration is losing manual overrides; mitigate that by always keeping overrides in a sourceable script.
Model it as a pin insertion delay on the macro's clock pin. That tells the engine the effective clock arrival is later (or earlier) than the boundary pin suggests, so the parent tree balances to the actual internal sink rather than to the pin. Without it, the parent tree looks balanced at the boundary but is skewed at the silicon, producing real hold or setup failures that no boundary-level report will reveal.
Key Takeaways
- The clock tree spec is a contract: it defines clocks, sinks, targets, legal cells, and exclusions before any buffer is placed.
- Use both paths — automatic derivation for the bulk, manual overrides for what the tool cannot infer.
- Every automatically inferred default (skew, latency, transition, cell list) is a hypothesis to confirm, not a fact to trust.
- The highest-leverage edits are restricting the cell list and excluding feedback/test pins.
- Keep the spec in sync with annotation updates after netlist, floorplan, or constraint changes; validate with an unannotated/conflict report.
- Delete cleanly — remove all annotations, not just visible targets — before a structural regeneration.
- Store every manual modification in one version-controlled override script so the spec is reproducible and self-documenting.
- Follow the loop: derive, review, modify, validate, regenerate.
ChipBuddy
← Home
Comments
Leave a Reply