Violation counts and TNS tell different stories. Early in closure, most of the TNS sits in a few large violations; late in closure, a long list of small ones remains, each needing individual attention. Seeing both numbers prevents the late-stage surprise that 'we are 90% done' means a lot of work is left.
deficits = [0.2 * 0.9 ** i for i in range(50)] # 50 violations, deficits in ns, decaying
total = sum(deficits)
assert round(total, 2) == 1.99
cum, k90 = 0.0, None
for i, d in enumerate(deficits, start=1):
cum += d
if cum / total >= 0.90:
k90 = i
break
assert k90 == 22
assert round(cum / total, 3) == 0.906
remaining = len(deficits) - k90
remaining_tns = round(total - cum, 3)
assert remaining == 28 and remaining_tns == 0.187
print("PASS: fixing the worst", k90, "of", len(deficits), "violations removes", round(100 * cum / total, 1), "% of TNS; the other", remaining, "violations carry only", remaining_tns, "ns")Fixed (worst first) | TNS removed | Violations left |
|---|---|---|
22 of 50 | 90.6% | 28 |
50 of 50 | 100% | 0 |
Fixing the worst 22 of 50 violations removes 90.6% of the TNS, and the remaining 28 carry only 0.187 ns between them: an average of 6.7 ps each. By TNS the project looks nearly done; by count, more than half the violations are still there. Those small ones can be the hard ones, paths that sizing and buffering did not fix, or endpoints with a negative setup-plus-hold sum that need a structural change; the decay here is a synthetic distribution, not a measured one. Plan for the tail early: it determines the end date, not the bulk.
Both counts and TNS are shown, with the share of TNS in the worst N violations.
The tail endpoints are examined for structural problems early, before the bulk is closed.
Waiver candidates in the tail have their evidence prepared in parallel, not at the end.
0.187 / 28 ≈ 6.7 ps each, against 200 ps (0.2 ns) for the worst violation: the tail is small in slack but large in count.