Borrowing is why a latch-based design can use an uneven split of logic between stages, and why the report for the following stage looks different from an ordinary flop-to-flop report. Working one example makes both visible.
Element | Value |
|---|---|
Period | 10.0 |
FF1 (rising edge at 0), clock-to-Q | 0.40 |
Logic 1 from FF1 to the latch D | 5.60 |
Latch (transparent when the clock is low): opens 5.0, closes 10.0 | Setup 0.10, D-to-Q 0.30 |
Logic 2 from the latch Q to FF2 | 2.00 |
FF2 (rising edge at 10.0), setup | 0.10 |
T = 10.0
arrive_latch_d = 0.0 + 0.40 + 5.60 # 6.00
open_edge, close_edge, setup_latch, dq = 5.0, 10.0, 0.10, 0.30
borrowed = max(0.0, arrive_latch_d - open_edge) # 1.00
max_borrow = (close_edge - setup_latch) - open_edge # 4.90
assert abs(borrowed - 1.00) < 1e-9 and borrowed <= max_borrow
latch_setup_slack = (close_edge - setup_latch) - arrive_latch_d # 3.90
# the next stage starts at the data arrival, not at the opening edge
launch_next = arrive_latch_d + dq # 6.30
arrive_ff2 = launch_next + 2.00 # 8.30
slack_ff2 = (T - 0.10) - arrive_ff2 # 1.60
# had the latch been a flop launching at its opening edge:
slack_if_flop = (T - 0.10) - (open_edge + dq + 2.00) # 2.60
assert abs(slack_ff2 - 1.60) < 1e-9 and abs(slack_if_flop - 2.60) < 1e-9
assert abs((slack_if_flop - slack_ff2) - borrowed) < 1e-9
print("PASS: borrowed", borrowed, "ns; latch slack", round(latch_setup_slack, 2), "; FF2 slack", round(slack_ff2, 2), "(vs", round(slack_if_flop, 2), "without borrowing)")The data reaches the latch at 6.0 ns, 1.0 ns after the latch opens at 5.0 ns, so 1.0 ns is borrowed (the latch window allows up to 4.9 ns, but the usable borrow is the smaller of the latch window and the downstream slack: here 1.0 + 1.6 = 2.6 ns). The latch's own setup slack is 3.9 ns. The next stage launches at 6.3 ns instead of 5.3 ns and ends with 1.6 ns of slack at FF2, exactly 1.0 ns less than the 2.6 ns it would have if the latch launched at its opening edge. Borrowing never creates slack: it moves it from the downstream path to the upstream one.
The report shows both the latch's setup slack and the borrowed time, and the next stage is analysed from the arrival time.
Borrowed time stays below the usable maximum with margin (the smaller of the latch window and the downstream slack): a latch borrowing nearly its whole window has almost no setup margin.
The downstream stage's slack is checked in the context of the borrowing, not against the nominal opening edge.
The downstream FF2 check fails first: its 1.6 ns slack reaches zero when logic 1 grows by only 1.6 ns (latch D arrival 7.6 ns = 9.9 - 2.0 - 0.3). At +4.0 ns the arrival is 10.0 ns, past the 9.9 ns closing-edge limit, so the latch itself fails too.