Latch-Based Timing & Time Borrowing
Chapter 20 — Latch-Based Timing & Time Borrowing (Deep Dive)
Most static timing analysis (STA, checking chip speed without running it) starts with flip-flops. A flip-flop grabs data at one clock edge. The path fits inside one clock period. The math is clean. Latches break that clean picture. A level-sensitive latch (a memory cell that is open for a whole clock phase, not just an instant) stays open longer. That long open window is both its strength and its trouble. This chapter explains how latches are timed. It covers time borrowing (letting a slow stage use the next stage's time), where it stops working, and how a tool reports it.
Latches vs. Flip-Flops: The Transparency Window
A flip-flop samples data at the active clock edge. After that, it holds. Its input only matters for a tiny window around that edge. A latch is different. It is open (transparent, data flows through) for the whole active level of its clock. Think of the high phase. It is closed (opaque, holding) for the low phase. While a latch is open, data flows from D to Q. It is delayed only by the latch's own delay. For setup, the moment that matters is the closing edge. That is when the latch goes opaque. It freezes whatever value is there. Think of a door held open all day. It only locks in what is inside when it shuts.

This one difference changes everything:
| Property | Edge-triggered flop | Level-sensitive latch |
|---|---|---|
| Data captured | At one clock edge | At the closing edge of the open |
phase
| Input live | Instant around edge | Entire open (transparent) phase |
|---|---|---|
| Setup reference | Active edge | Closing edge |
| Late data tolerated? | No — must arrive before | Yes — up to the open window's end |
edge
Time can be shifted between No (hard period boundary) Yes — time borrowing
Hold analysis One edge Complicated by the open phase failure. The data just needs to settle before the latch closes. It is captured fine even if it arrived after the normal cycle boundary. That tolerance is what makes time borrowing work.
Why Latch-Based Design Can Be Faster (and Harder)
In a flop pipeline, every stage must fit inside one clock period. Say one stage is slow and the next is fast. The fast stage's spare time is wasted. The slow stage still fails. Uneven logic costs you speed. Latches let a slow stage spill its lateness into the next latch's open phase. This averages the delays across stages. It does not force each stage to fit alone. The pipeline runs at the average stage delay, not the worst stage delay. That is real speed. It is why fast datapaths used latches for years. The cost is complexity. The capture point moves around. Borrowing piles up down a chain. Hold checking spans many edges. The tool must track an open window for each latch. It is easy to write timing that is quietly wrong. It is easy to let the tool hide a real failure as "borrowed time."
Two-Phase Non-Overlapping Clocks and Master/Slave Decomposition
The classic latch style uses two clock phases. We call them phase 1 and phase 2. They are nonoverlapping (never both high at once). Latches alternate phases down the pipeline. A phase-1 latch feeds logic into a phase-2 latch. That feeds logic into the next phase-1 latch. And so on.

Non-overlap stops data racing through two latches at once. When one is open, the next is closed. The "dead time" gap between phases is the safety margin against hold races. A flip-flop is really this same structure squeezed into one cell. It is a master latch on one phase and a slave latch on the opposite phase, back to back. The master is open while the slave holds. At the active edge, the master closes and the slave opens. The slave grabs the master's frozen value. Seeing a flop as master and slave makes the latch checks below feel familiar.
Setup and Hold Checks at a Latch
For a latch, mark the open phase by its opening edge and closing edge. Let:
T_open= time the latch becomes transparentT_close= time the latch becomes opaque (the capturing reference)W = T_close − T_open= duration of the transparent windowt_su,t_hold= the latch's own setup and hold requirements relative to the closing edge Setup at a latch. Data must be valid by setup time before the closing edge. Not before the opening edge. So the latest data may arrive up to:required_data_arrival ≤ T_close − t_suSay data arrives beforeT_open. It just waits until the latch opens. Say data arrives afterT_openbut beforeT_close − t_su. Then the latch borrows that overrun from its own open phase. That is the borrow. Hold at a latch. Hold is the risky side. While the latch is open, new data from the previous stage can ripple through. It can wreck the value that should have been captured. The hold check guards against
data arriving too soon. Because the latch is open for a whole phase, the window for a fast path to break through is much wider than for a flop. Hold checks at latches usually reference the opening edge of the capturing latch. They must also track how the launching clock relates to it.
| Check | Flop reference | Latch reference | Practical effect |
|---|---|---|---|
| Setup | Active capture | Closing edge of open edge | Latch tolerates late data (borrowing) phase |
| Hold | Active capture | Opening edge of open edge | Wider exposure to fast-path races phase |
| Slack sign | Late vs. period | Late vs. closing edge convention | Borrowed time shows as consumed margin |
Time Borrowing: The Mechanism
Time borrowing happens when data arrives at a latch late. It arrives after the normal cycle boundary (after the latch opens). But it still arrives before the latch closes minus setup. The amount it runs past the boundary is the borrowed time. That overrun is stolen from the open phase. So the next stage now has less open phase to use. The hard limit: you cannot borrow more than the open phase allows. The most a single latch can borrow is its window width minus its setup:
borrow_max = W − t_su = (T_close − T_open) − t_su
Borrow more than that and the latch closes on data that is still changing. That is a real setup violation. Borrowing also chains. Stage N borrows. The latch at the end of stage N captures late. That late capture launches stage N+1 late. Stage N+1 now starts late and may need to borrow too. So borrowing adds up down the chain. As long as each stage has enough open-phase slack for the running total, the pipeline is fine. The moment the total borrow passes a latch's borrow_max , the chain fails at that latch. The real cause may sit several stages upstream.

Worked Example 1 — Single-Stage Borrow
Setup. A latch opens at t = 0 ns and closes at t = 8 ns. So its window is W = 8 ns. Its setup is t_su = 0.4 ns . The logic feeding it sends data that arrives at t = 9.5 ns, using the same reference. Is this a violation? For a flop capturing at the cycle boundary (say t = 6 ns), data at 9.5 ns is 3.5 ns late. A clear failure. For the latch, the deadline is the closing edge minus setup:
deadline = T_close − t_su = 8 − 0.4 = 7.6 ns
Data at 9.5 ns is past 7.6 ns. So this is a setup violation. Now let us run the borrow story with better data. Suppose data instead arrives at t = 6.5 ns.
- The normal cycle boundary (where a flop would demand the data) is at t = 6 ns.
- Data runs past the boundary by 6.5 − 6.0 = 0.5 ns. That is the borrowed time.
- Borrow available relative to the boundary:
T_close − boundary − t_su = 8 − 6 − 0.4 = 1.6 ns. - Borrowed 0.5 ns ≤ 1.6 ns available, so it passes. There is 1.1 ns of borrow margin left. So the same latch fails at 9.5 ns but passes easily at 6.5 ns. The 0.5 ns borrow fits in the open phase. A flop would have failed in both cases.
Worked Example 2 — Multi-Stage Borrow Accumulation
Now a three-latch chain on a two-phase clock. Each latch has a window of W = 6 ns and t_su = 0.3 ns . So borrow_max = 6 − 0.3 = 5.7 ns per latch. The normal per-stage budget is 6 ns of logic delay per stage. The latches alternate phases, so each stage's open window starts at its own boundary. The stages have uneven logic:
Logic Per-stage Stage Incoming Cumulative borrow at latch
| delay | budget | overrun | borrow | output | |
|---|---|---|---|---|---|
| Stage 1 → | 7.2 ns | 6.0 ns | +1.2 ns | 0.0 ns | 1.2 ns |
L1
Stage 2 → 6.6 ns 6.0 ns +0.6 ns 1.2 ns 1.8 ns
Stage 3 → 4.8 ns 6.0 ns −1.2 ns 1.8 ns 0.6 ns Walk through it:
- L1: Stage 1 takes 7.2 ns against a 6.0 ns budget. It runs over by 1.2 ns. L1 borrows 1.2 ns. Check:
1.2 ≤ 5.7, so it passes. L1 captures 1.2 ns late. So it launches stage 2 1.2 ns late.
- L2: Stage 2 takes 6.6 ns (0.6 ns over budget). It also started 1.2 ns late. Total borrow at L2 = 1.2 (inherited) + 0.6 (own) = 1.8 ns. Check: 1.8 ≤ 5.7, so it passes. L2 launches stage 3 1.8 ns late.
- L3: Stage 3 is fast. It takes 4.8 ns against a 6.0 ns budget, so it is under by 1.2 ns. It pays down some debt. Cumulative borrow = 1.8 + (−1.2) = 0.6 ns. Check: 0.6 ≤ 5.7, so it passes. The chain has nearly caught up. The key lesson: a fast stage repays borrowed time. The pipeline survives because stage 3's slack soaks up the debt from stages 1 and 2. Suppose stage 3 were slow instead, say 7.5 ns (1.5 over). Then the borrow at L3 would be 1.8 + 1.5 = 3.3 ns. Still under 5.7, but climbing. A fourth slow stage could overflow and fail at that latch. The root cause would still sit back at stage 1. This is why latch timing closure is non-local. A violation at one latch can come from logic several stages upstream. That upstream logic itself reports as "passing with borrow."
Hold Risk in Latch Designs
The open phase is where hold problems live. While a latch is open, anything at its D input flows to Q. Say the previous stage launches new data while the capturing latch is still open. That new data can overrun the value that should have been frozen. That is a hold (race) failure. Take two latches L_a and L_b. L_b is open during the same window that L_a is launching. A short, fast path from L_a to L_b can deliver next-cycle data into L_b before it closes. That corrupts the capture. Non-overlapping two-phase clocks defend against exactly this. The dead time means the launching latch's phase ends before the capturing latch's phase begins. So there is no instant where both are open. Hold fixes in latch designs are tricky because:
- The exposure window is a whole phase, not an edge. That is far wider than a flop's hold window.
- Adding delay (buffers) to fix hold eats into the open-phase budget. Setup and borrowing rely on that same budget. Setup and hold pull against each other on one window.
- Clock skew between the two phases widens or shrinks the dead time. So clock distribution quality is a hold concern, not just a setup one.
# illustrative — generic, not tool-specific# tighten the non-overlap (dead time) assumption for hold analysisset_clock_uncertainty -hold 0.15 [get_clocks {phi1 phi2}]# verify both phases are declared non-overlapping in the clock definitionreport_clock_relationships -phases {phi1 phi2}
How STA Reports Borrowing — and Its Dangers
A latch-aware tool reports borrowing for each endpoint latch. It shows how much time was borrowed and how much capacity is left. The report shows the data arrival, the open and close edges, the maximum allowed borrow, the borrow used, and the leftover slack.
# illustrative — generic, not tool-specific
# request a latch-aware timing report that exposes time borrowing
report_timing -to [get_pins L2/D] \
-derate \
-attributes {open_edge close_edge time_borrowed borrow_limit slack}
To turn the borrowing model on, you mostly declare the clocks right. You also bound the borrow where you do not want it:
# standard (SDC)
create_clock -name phi1 -period 16 -waveform {0 8} [get_ports clk_phi1]
create_clock -name phi2 -period 16 -waveform {8 16} [get_ports clk_phi2]
# cap how much any latch is permitted to borrow (defensive)
set_max_time_borrow 2.5 [get_clocks phi1]
set_max_time_borrow 2.5 [get_clocks phi2]
Unbounded borrowing is the big pitfall of this whole method. Let latches borrow without limit, and the tool will happily call a chain "met." Each latch found just enough open phase to soak up the running debt. But:
- Borrowing hides slow logic. A path that is truly too slow shows as "passed, borrowed 4.2 ns" instead of "failed." The number sits in a per-latch field. It does not flash red at the top of the report.
- It pushes the failure downstream. The real violation shows up at whichever latch runs out of window. That is often far from the slow logic. Engineers debug the wrong stage.
- It is fragile to derating. A path passing on borrowed time has little or no normal positive slack. Under on-chip variation, voltage droop, or aging, the borrow demand grows. The chain tips into violation with no warning margin. The safe habit is to set
set_max_time_borrowlow. Make borrowing a planned, bounded choice. Do not let it be an accident the tool covers up. Then audit the borrow reports. Any latch using most of its borrow limit is a hidden failure waiting for the next corner. Symptom in report What it actually means Action Latch passes, large Upstream logic too slow; debt Rebalance upstream logic, not thistime_borrowedabsorbed here latch Borrow used ≈ borrow limit One corner away from failure Reduce upstream delay; lower max borrow to expose it Violation at latch N, clean Accumulated borrow overflowed Trace borrow chain back to true root upstream window N cause Hold fail in open phase Fast path races through Check non-overlap, add delay carefully transparent latch
Interview Q&A
captures at one instant, the active edge. Any data later than that edge minus setup is lost. A latch is open for a whole phase. It only freezes at the closing edge. Data that arrives after the opening edge but before the closing edge minus setup is still captured fine. The overrun is borrowed from the open phase.
own setup: borrow_max = (T_close − T_open) − t_su . Borrow past that and the latch closes on data that is still changing. That is a real setup violation.
a late launch into the next stage. So borrowing adds up. Stage N+1's open phase shrinks by however much stage N borrowed. A fast stage with positive slack can repay the debt. A string of slow stages runs the total up until some latch's window overflows. The chain fails there.
phase. So the window for a fast path to race new data through is a whole phase wide. That is far wider than a flop's narrow hold window. Two-phase non-overlapping clocks defend with dead time between phases. The quality of that non-overlap, and clock skew, becomes a direct hold concern.
Borrowing can soak up truly slow logic and report it as "passed, borrowed X ns" instead of a failure. The real problem hides in a per-latch field. The violation surfaces far downstream from its cause. The path has nearly zero normal slack. So any derate (OCV, voltage droop, aging) pushes it into violation with no margin. Capping borrow with set_max_time_borrow forces these paths to show themselves.
clock phases, back to back. The master is open while the slave holds. At the active edge, the master closes and freezes its value. The slave opens and captures it. The non-overlap of the two inside phases is what makes a flop edge-triggered. It is also what makes a flop immune to the transparency races a single latch faces.
Key Takeaways
- A latch is open for a whole clock phase. Its setup deadline is the closing edge minus setup, not an instant. That tolerance for late data is the basis of time borrowing.
- Time borrowing lets a slow stage steal time from the next latch's open phase. So a pipeline runs at the average stage delay, not the worst. That is real speed, at the cost of complexity.
- Maximum borrow per latch is
(T_close − T_open) − t_su. Borrow more and the latch captures changing data: a true setup violation. - Borrowed time adds up down a latch chain. Fast stages repay it. Slow stages run it up. A failure can appear at a latch far from the logic that caused it.
- Hold is the risky side. A full open phase widens the race window. Two-phase non-overlapping clocks and their dead time are the defense, which makes clock skew a hold issue.
- A flip-flop is just a master/slave latch pair on opposite phases.
- Always bound borrowing with
set_max_time_borrow. Unbounded borrowing hides slow logic, moves violations downstream, and leaves paths with no margin against derating. Audit any latch using most of its borrow limit. It is a hidden failure.
ChipBuddy
← Home
Comments
Leave a Reply