A budget turns "this path must meet 2 ns" into a list of allowances per stage. It lets RTL, synthesis and physical design agree on targets, and it makes a violation easy to localise: the stage that exceeded its allowance.
Stage | Budget (ns) | Actual (ns) | Over? |
|---|---|---|---|
Clock-to-Q | 0.40 | 0.40 | |
Logic stage 1 | 0.45 | 0.52 | +0.07 |
Wire 1 | 0.15 | 0.14 | |
Logic stage 2 | 0.45 | 0.44 | |
Wire 2 | 0.15 | 0.20 | +0.05 |
Setup | 0.10 | 0.10 | |
Margin | 0.30 | - | - |
T = 2.0
budget = {"cq": 0.40, "logic1": 0.45, "wire1": 0.15, "logic2": 0.45, "wire2": 0.15, "setup": 0.10}
actual = {"cq": 0.40, "logic1": 0.52, "wire1": 0.14, "logic2": 0.44, "wire2": 0.20, "setup": 0.10}
assert abs(sum(budget.values()) - 1.70) < 1e-9
margin = T - sum(budget.values())
assert abs(margin - 0.30) < 1e-9
over = {k: round(actual[k] - budget[k], 2) for k in budget if actual[k] > budget[k] + 1e-9}
assert over == {"logic1": 0.07, "wire2": 0.05}
slack = T - sum(actual.values())
assert abs(slack - 0.20) < 1e-9
print("PASS: budget margin", round(margin, 2), "actual slack", round(slack, 2), "over-budget stages", over)The budget sums to 1.70 ns, leaving 0.30 ns of margin in a 2.0 ns period. The actual path sums to 1.80 ns, so slack is 0.20 ns, still positive even though two stages exceeded their allowances (by 0.07 and 0.05 ns). The budget shows who owns the overrun and how much margin remains, which the total alone does not. The budget assumes ideal clocks: clock uncertainty and skew are not separate stages and are covered by the margin.
Budgets are agreed across teams before implementation and revised in the open when a stage cannot meet its allowance.
Margin is explicit and owned (not hidden in stage allowances), and shrinks as the design matures.
Overruns are tracked per stage so repeated offenders (a macro, a bus) are found.
Logic stage 1 (+0.07) and wire 2 (+0.05). The path is not failing: total actual 1.80 ns against a 2.0 ns period leaves +0.20 ns, because the margin absorbed the overruns.