Depth-based derating knows how many stages a path has, but not which cells they are. A path through two highly variable cells is not like a path through nine steady ones. Statistical methods keep a sigma for each cell delay, combine them along the path, and so give each path its own margin.
import math
def path_stats(means, sigmas, ksig=3.0):
mean = sum(means)
sigma = math.sqrt(sum(s * s for s in sigmas)) # independent variations add in quadrature
return round(mean, 4), round(sigma, 5), round(mean + ksig * sigma, 4)
uniform = path_stats([0.10] * 9, [0.004] * 9)
assert uniform == (0.9, 0.012, 0.936)
outlier = path_stats([0.10] * 9, [0.004] * 7 + [0.015, 0.015])
assert outlier[0] == 0.9 and round(outlier[1], 4) == 0.0237
assert round(outlier[2], 3) == 0.971
print("PASS: uniform path (mean, sigma, 3-sigma delay)", uniform, "; two noisy cells", outlier)Nine uniform stages have sigma 0.012 ns, so the 3-sigma delay is 0.936 ns. Replacing two stages with noisier cells (sigma 0.015 ns each) raises the path sigma to 0.0237 ns and the 3-sigma delay to 0.971 ns: the same mean and stage count, a 0.035 ns larger margin. A depth-based table would give both paths the same derate. The number of sigmas is a policy choice, and the independence assumption is the method's main approximation.
The sigma data come from library characterisation with a known coverage, and the number of sigmas is part of the signoff policy.
Cells with unusually large sigma (long wires, weak drivers, low-voltage cells) are identified, because they dominate path variation.
Results are compared against the depth-based analysis on a sample of paths, and large differences are explained.
sqrt(4 × 0.003²) = 0.006 ns, twice a single cell's sigma and not four times.