Clock-Tree Synthesis (CTS)
The clock reaches every flip-flop on the chip. Learn clock-tree goals (skew, latency, transition, power), H-trees, buffered trees and meshes, skew and its effect on setup and hold, useful skew, clock gating in the tree, non-default routing rules, OCV/CRPR, and how to compute tree depth and timing windows.
Before CTS the clock is 'ideal' — it arrives everywhere at the same instant. CTS builds the real network of buffers and wires that distributes the clock from its source (PLL) to tens of thousands or millions of sinks.
The clock network toggles every cycle and often consumes 30–40% of dynamic power. It must deliver sharp edges with small, controlled differences in arrival time (skew), because skew directly eats setup or hold margin.
An analogy
A clock tree is the water-pipe system of a building that must deliver water to every tap at exactly the same moment: equal-length branches and properly sized pumps (buffers) at each level.
Goals of the clock tree
Reach every sink; keep skew small (typically tens of ps); keep latency reasonable; keep transitions sharp (slow edges add jitter and short-circuit power); and use as little power and area as possible. These goals conflict, so CTS is an optimisation.
Tree structures
Common topologies:
- H-tree: geometrically symmetric, inherently balanced; used at the top level
- Buffered tree from clustering: flexible, what CTS tools build by default
- Clock mesh/grid: shorts many drivers together; very low skew, high power (high-end CPUs)
- Multi-source CTS / spines: hybrid of tree and mesh
Skew, setup and hold
If the capture flop's clock arrives later than the launch flop's, the data gets more time (setup improves) but the new data may overwrite the old too early at the capture flop (hold worsens). Useful-skew optimisation deliberately delays some clocks to borrow time from fast paths for slow ones.
Variation and clock sign-off
On-chip variation makes clock paths faster or slower in different places; STA models it with derates/AOCV/POCV and removes the pessimism on shared tree segments (CRPR). Clock nets are routed with NDRs (double width/spacing) and shielding to reduce crosstalk and EM. Clock gating cells are placed in the tree; CTS balances through them. Jitter from the PLL adds to uncertainty.
Formulas
- Setup with skew:
T ≥ t_cq + t_logic,max + t_su − skew (skew = t_capture − t_launch) - Hold with skew:
t_cq + t_logic,min ≥ t_h + skew - Tree depth:
levels = ⌈log_fanout(sinks)⌉ - Buffers in an ideal tree:
Σ fanoutⁱ, i = 0 … levels−1 - Clock power:
P_clk = C_clk · V² · f (α = 1)
Worked example: Tree depth
Given: 10 000 sinks, fan-out 4
Find: Levels and buffers.
- log₄10 000 = 6.64 → 7 levels
- 1 + 4 + … + 4⁶ = 5461
Answer: 7 levels, ≈ 5461 buffers (ideal tree)
Worked example: Useful skew
Given: t_cq 50, logic 800, setup 40 ps; skew +30 ps
Find: Minimum period.
- 50 + 800 + 40 − 30
Answer: 860 ps (1.163 GHz) vs 890 ps with zero skew
Worked example: Clock power
Given: C_clk = 300 pF, 0.9 V, 1.5 GHz
Find: Clock power.
- 300p × 0.81 × 1.5G
Answer: ≈ 0.36 W — why clock gating matters
Tree size and the setup/hold effect of skew
import math
sinks = 10000
fanout = 4
levels = math.ceil(math.log(sinks, fanout))
print(f"{sinks} flops with fanout-{fanout} buffers -> {levels} buffer levels, "
f"{sum(fanout**i for i in range(levels))} buffers (ideal tree)")
tcq, tlogic, tsetup, thold = 50, 800, 40, 30 # ps
for skew in (-30, 0, 30): # capture clock later (+) or earlier (-)
tmin = tcq + tlogic + tsetup - skew
hold_margin = tcq + 20 - thold - skew # shortest path = 20 ps of logic
print(f"skew {skew:+d} ps: min period {tmin} ps ({1e3/tmin:.3f} GHz), hold slack {hold_margin} ps")
# 10000 flops with fanout-4 buffers -> 7 buffer levels, 5461 buffers (ideal tree)
# skew -30 ps: min period 920 ps (1.087 GHz), hold slack 70 ps
# skew +0 ps: min period 890 ps (1.124 GHz), hold slack 40 ps
# skew +30 ps: min period 860 ps (1.163 GHz), hold slack 10 psPositive skew (capture clock later) helps setup but hurts hold by the same amount — the basis of 'useful skew'. Too much positive skew on a short path causes hold violations, which cannot be fixed by slowing the clock.
Clock-tree constraints (SDC + OpenROAD style, illustrative)
create_clock -name core_clk -period 1.0 [get_ports clk]
set_clock_uncertainty -setup 0.05 [get_clocks core_clk] ;# jitter + margin
set_clock_uncertainty -hold 0.02 [get_clocks core_clk]
set_clock_transition 0.08 [get_clocks core_clk]
# CTS with clock buffers/inverters only, wider spaced wires for the clock
clock_tree_synthesis -buf_list {CLKBUF_X4 CLKBUF_X8} -root_buf CLKBUF_X16 \
-sink_clustering_enable
set_propagated_clock [all_clocks] ;# use real latencies from now on
repair_clock_nets
report_clock_skewAfter CTS the clock becomes 'propagated': STA uses real buffer and wire delays instead of ideal timing, and hold fixing starts.
Common mistakes
- Wrong: Using regular logic buffers in the clock tree. — Right: Use clock buffers/inverters with balanced rise/fall.
- Wrong: Fixing hold before CTS. — Right: Fix hold after the clock is propagated.
- Wrong: Chasing zero skew everywhere. — Right: Control skew where it matters; useful skew helps.
Applications
- CPUs: Clock meshes and resonant clocking at multi-GHz.
- SoCs: Hundreds of clocks and gated branches built by CTS.
- FPGAs: Dedicated global clock networks.
- Low-power chips: Deep clock gating in the tree.
Key takeaways
- CTS balances skew, latency, transition and power.
- Skew trades setup against hold 1:1.
- Hold fixing comes after propagated clocks.
- Clock is the biggest single dynamic-power consumer.
Going further
- Multi-source CTS: Tapped H-tree plus local trees for low skew in large blocks.
- Resonant clocking: LC resonance recovers clock energy.
- Clock domain aware CTS: Balancing between synchronous domains.