Block-Level I/O Budgeting
Block-Level I/O Budgeting (Deep)
A big chip is built from blocks. Each block is a smaller piece of logic. Many people work on these blocks at the same time. Each person needs to constrain their own block. But a signal often starts in one block and ends in another. That path crosses the boundary between them. So who owns the timing of that path? The answer is budgeting. Budgeting means splitting one path's time across the blocks it touches. SDC is the format we use to write these constraints. SDC stands for the timing constraint format that compliant tools read. We never expand the letters here; we just call it SDC. In this chapter we split a top-level path into block-level pieces. We derive input and output delays for each block. We use virtual clocks to model the missing context. And we look at what goes wrong when the split is too generous or too tight.

Picture one register-to-register path. It starts at a flip-flop inside block A. A flip-flop is a small memory that updates on a clock edge. The path then leaves block A through an output port. It travels across top-level wire. It enters block B through an input port. It ends at a flip-flop inside block B. The whole path has one clock period to finish. Say the clock period is 6.83 nanoseconds. A nanosecond is one billionth of a second. That 6.83 ns must cover three things. It must cover the logic inside block A after the start flop. It must cover the wire at the top level. And it must cover the logic inside block B before the end flop. But block A's owner cannot see inside block B. And block B's owner cannot see inside block A. Each owner only has their own block open in the timing tool. So we must tell each owner how much of the 6.83 ns belongs to them. That share is their budget. If block A is given 2.40 ns, then block A's logic plus its output wire must fit in 2.40 ns. The remaining 4.43 ns is left for the top wire and block B. We hand block B a matching number. When both blocks meet their share, the full path meets timing once they are joined. That is the whole idea of I/O budgeting.
| Path part | What it covers | Owned by |
|---|---|---|
| Launch clock | Edge that starts the path | Shared |
| Block A logic | Gates after the start flop | Block A |
| Top-level wire | Routing between blocks | Top |
| Block B logic | Gates before the end flop | Block B |
| Capture clock | Edge that ends the path | Shared |
Output Delay and Input Delay as Budget Carriers
We carry budget across a boundary using two SDC commands. They are set_output_delay and
set_input_delay . An output delay says how much time outside the block is used after the data
leaves. An input delay says how much time outside the block is used before the data arrives. Think of block A. Its data leaves through an output port. After that port, the signal still travels on top wire and through block B logic. That outside time is the output delay on block A's port. If the top wire plus block B logic needs 4.43 ns, then block A must finish at least 4.43 ns before the period ends. So we set an output delay of 4.43 ns on block A's output port. Now think of block B. Its data arrives through an input port. Before that port, the signal already spent time in block A logic and on top wire. That spent time is the input delay on block B's port. If block A logic plus top wire used 2.91 ns, then block B sees data arrive 2.91 ns into the period. So we set an input delay of 2.91 ns on block B's input port. The two numbers come from the same total. The output delay on the sender plus the sender's own logic must equal the period. The input delay on the receiver leaves the rest for the receiver's logic. Done right, the two views agree.

The cleanest budgets come from a real top-level path. First you let the top-level tool see the full path. The tool reports how long each segment takes. Then you split the period along those segments.
Here is a worked example. Suppose the period is 6.83 ns. The top-level report shows block A logic takes 1.78 ns. The top wire takes 0.62 ns. Block B logic takes 2.05 ns. Add those and you get 4.45 ns of real delay. There is 2.38 ns of slack. Slack is spare time left over. Now we split. We give block A its own logic time plus a piece of slack. We give block B its own logic time plus the rest of the slack. The top wire we split between them by where the boundary sits. A common rule is to put the wire on the receiver side, or to halve it. Let us put the wire fully on block B's side and split slack evenly. Block A gets 1.78 ns logic plus 1.19 ns slack, which is 2.97 ns inside the block. That means block A's output delay is 6.83 minus 2.97, which is 3.86 ns. Block B gets 2.05 ns logic plus 0.62 ns wire plus 1.19 ns slack, which is 3.86 ns inside. Its input delay is 6.83 minus 3.86, which is 2.97 ns. Notice the two numbers mirror each other. Block A's output delay equals block B's internal time. That is the sign of a consistent split.
| Quantity | Value (ns) | Note |
|---|---|---|
| Period | 6.83 | Full path budget |
| Block A logic | 1.78 | From top report |
| Top wire | 0.62 | Assigned to B |
| Block B logic | 2.05 | From top report |
| Total slack | 2.38 | Spare time |
| Block A output delay | 3.86 | Outside A |
| Block B input delay | 2.97 | Before B |
Virtual Clocks for Budgeting
Sometimes the other block is not ready. You cannot run a top-level path because the neighbor does not exist yet. You still must budget your block. This is where a virtual clock helps. A virtual clock is a clock with no port. We make it with create_clock but we give it no source object. It has a period and edges, but it drives nothing real. We use it as the reference for input and output delays at the boundary. It models the timing of the outside world without needing the outside logic. Why bother? Because input and output delays must reference a clock. If the boundary signal is timed against a clock that does not physically enter your block, a virtual clock stands in for it. It also keeps your block-level setup clean. Your real internal clock stays for internal paths. The virtual clock handles the budget at the edges. Say your internal clock is 5.27 ns. The interface to the neighbor runs on a different clock at 7.41 ns. You make a virtual version of that interface clock. Then you reference your boundary delays to it. The tool now checks boundary paths against the right period, even though the neighbor block is empty.
# standard SDC — portable across compliant tools
create_clock -name if_clk_virt -period 7.41
set_input_delay -clock if_clk_virt 3.10 [get_ports cfg_in]
set_output_delay -clock if_clk_virt 4.05 [get_ports stat_out]
A second worked example shows the payoff. The virtual clock period is 7.41 ns. You set an input delay of 3.10 ns on a port. That tells the tool the data arrives 3.10 ns after the virtual edge. Your block now has 7.41 minus 3.10, which is 4.31 ns, to process that input before its own capture. You can time your block fully with no neighbor in sight.
Over-Budgeting and Under-Budgeting
A split can be wrong in two ways. It can give a block too much time or too little. Both cause trouble at integration. Integration is when the blocks are joined at the top. Over-budgeting gives a block more time than the path can really spare. Suppose you tell block A it has 3.50 ns inside, when the path can only allow 2.97 ns. Block A's owner relaxes. Their logic grows to fill 3.50 ns and still reports as passing. But at the top, the real path now needs 3.50 ns plus the neighbor's share. The total exceeds the period. The path fails only after joining. Each block looked fine alone. The failure hid until too late. Under-budgeting is the opposite. You give a block less time than fair. Block B's owner is told they have only 2.10 ns when 3.86 ns is available. They struggle. They add extra effort, bigger gates, more area, more power, all to hit a target that was never required. The chip works but it wasted resources. And the slack you stole sat unused on the other side. The fix for both is a budget that adds up. The sum of all block shares plus wire must equal the period, no more and no less. A small margin is fine. A wild mismatch is not.
| Symptom | Likely cause | Effect at top |
|---|---|---|
| Blocks pass alone, top fails | Over-budgeting | Real path too long |
| Block hard to close, wastes area | Under-budgeting | Effort spent for nothing |
| Both blocks close, top closes | Balanced budget | Healthy margin |
| Mirror numbers disagree | Split error | Hidden mismatch |
The safest habit is to re-derive budgets from a real top-level path once neighbors exist. Early virtualclock budgets are estimates. Replace them with measured splits as soon as you can. Then both block owners and the top owner see the same numbers.
Interview Q&A
time among the blocks it crosses. It is needed because each block is constrained on its own. No owner can see inside the neighbor. The budget tells each owner how much of the shared period belongs to them, so the path closes once blocks are joined.
is used outside its port after data leaves. An input delay tells a block how much time was used before data arrived at its port. The sender's output delay should equal the receiver's internal time, and the two views should mirror each other.
tool. It reports the delay of each segment: sender logic, wire, receiver logic. You then split the period across those segments, sharing the wire and the slack. Each block's internal time plus its boundary delay equals the period.
period but no real port. You use it when the neighbor block does not exist yet. It serves as the reference for boundary input and output delays, so you can time your block fully against the right period without the neighbor's logic.
than the path can spare. The block grows to fill the time and still passes alone. At integration the real path is too long and fails. The danger is that the failure stays hidden until the blocks are joined.
share plus the wire equals the period, with only a small margin. The sender's output delay equals the receiver's internal time. If those mirror numbers disagree, the split has an error.
Key Takeaways
- Budgeting splits a shared period across the blocks a path crosses, so each owner can constrain their block alone.
- Output delay and input delay carry the budget across a boundary; the sender's output delay should mirror the receiver's internal time.
- Derive budgets from a real top-level path by splitting the period across measured segments of sender logic, wire, and receiver logic.
- A virtual clock lets you budget a boundary before the neighbor exists, giving boundary delays a correct period to reference.
- Over-budgeting hides failures until integration while under-budgeting wastes area and power; a balanced budget adds up to the period.
ChipBuddy
← Home
Comments
Leave a Reply