The simplest fix for a slow cell is to make it bigger. A bigger cell has lower output resistance, so it drives its load faster, but it also has larger input capacitance, which slows the previous stage. Beyond some size the loss on the previous stage exceeds the gain. Knowing the trade-off keeps you from oversizing.
stage delay = intrinsic + R_drive x C_load
upsizing by factor k: R_drive -> R/k , C_in -> C_in x k
previous stage sees its load increase by C_in x (k - 1)# Two stages: stage 1 (fixed, R1) drives stage 2 (size k, input cap c_in*k); stage 2 drives a fixed load.
R1, R2, c_in, load, i1, i2 = 2000.0, 2000.0, 1.0e-15, 20e-15, 20e-12, 20e-12
def total(k):
d1 = i1 + R1 * (c_in * k) # stage 1 sees stage 2's input cap
d2 = i2 + (R2 / k) * load # stage 2 drives the fixed load faster when bigger
return d1 + d2
vals = {k: total(k) for k in (1, 2, 4, 8)}
best = min(vals, key=vals.get)
assert best == 4
assert vals[4] < vals[2] < vals[1]
assert vals[8] > vals[4] # past the optimum, bigger is slower overall
print("PASS: total delay (ps) by size:", {k: round(v * 1e12, 1) for k, v in vals.items()}, "best size", best)The total delay falls from size 1 to size 4 and rises again at size 8. The optimum balances the two terms (here near the square root of the load ratio). The exact numbers are illustrative, but the shape is real: sizing has diminishing returns, and the previous stage pays for it. Tools search this trade-off across the whole path; when you resize by hand, check the stage before.
A manual sizing change is checked on the driver upstream and on slew, not only on the cell resized.
Area and power cost per picosecond gained is considered; the last 10 ps cost far more than the first.
Threshold-voltage swaps (faster, leakier cells) are controlled by a leakage budget.
The extra input capacitance of the larger stage-2 cell slows stage 1 by more than stage 2 gains from its lower drive resistance, so the sum increases past the optimum.