Two paths with the same total delay can need opposite fixes. A path dominated by one slow cell wants resizing; a path dominated by one long wire wants buffering or a shorter route; a path with many small stages wants restructuring. Looking at the breakdown before touching anything saves iterations.
Stage | Cell delay | Wire delay |
|---|---|---|
FF1 clock-to-Q | 0.40 | 0.05 |
NAND2 | 0.12 | 0.18 |
XOR2 | 0.20 | 0.22 |
AOI22 | 0.18 | 0.35 |
Setup of FF2 | 0.10 | - |
stages = [("clk-to-q", 0.40, 0.05), ("nand2", 0.12, 0.18), ("xor2", 0.20, 0.22), ("aoi22", 0.18, 0.35)]
cell = sum(c for _, c, _ in stages)
wire = sum(w for _, _, w in stages)
total = cell + wire
assert abs(cell - 0.90) < 1e-9 and abs(wire - 0.80) < 1e-9 and abs(total - 1.70) < 1e-9
wire_share = wire / total
assert abs(wire_share - 0.8/1.7) < 1e-9
worst_wire = max(stages, key=lambda s: s[2])
assert worst_wire[0] == "aoi22"
print("PASS: total", round(total, 2), "ns, wire share", round(100 * wire_share), "%, biggest wire at", worst_wire[0])Of 1.70 ns, wires take 0.80 ns, about 47%; the numbers are illustrative — real shares depend on the design. The single largest wire (0.35 ns, at the AOI22 output) is the best first target: buffering or a different route there can recover more than resizing any one cell. The cells together cost 0.90 ns, of which the clock-to-Q (0.40) cannot be changed by the logic, only by the flop choice.
Dominant contributor | Typical fix |
|---|---|
One slow cell | Upsize, change threshold voltage class, restructure |
One long wire | Buffer, change layer, move cells closer |
Many stages | Restructure logic, reduce depth |
High fanout net | Split, buffer tree, duplicate the driver |
Large clock-to-Q or setup | Choose a faster flop type |
The 0.35 ns wire at the AOI22 output. How much delay a buffer or a better route recovers depends on what it actually saves.