UCIe · Module 7
Clocking
Where a UCIe receiver's sampling instant comes from — forwarded versus recovered clock, centre-of-UI phase, DDR, clock-to-data skew, the forwarded-clock to local domain boundary, PLL lock qualification, clock loss, glitchless switching, and why reset release needs a clock.
Chapter 7.2 asked whether enough voltage arrived. Chapter 7.3 established which conductor carries which identity. Chapter 7.4 decided how many conductors to use. All three quietly assumed the answer to a question none of them asked:
At what instant does the receiver look?
That question is not a detail. Sixty-four low-swing single-ended lanes arriving in parallel are useless without an agreed sampling instant, and the instant has to be right for every lane, across process, voltage, and temperature, on a link where the bit period is a fraction of a nanosecond. How UCIe answers it shapes the receiver's circuitry, its power, its complexity, and — the part that reaches your RTL — where the clock-domain boundaries in your design actually are.
1. The One-Sentence Model
UCIe does not ask the receiver to rediscover time from the data stream. The transmitter sends timing alongside the data, so the receiver samples in the transmitter's timing frame.
That is source-synchronous design, and the phrase to hold onto is the transmitter's timing frame. The receiver is not measuring the data against its own idea of time; it is borrowing the sender's. Which immediately raises the two questions the rest of the chapter answers: how good is the borrowed reference by the time it arrives (§7), and how does data escape that borrowed frame into the receiver's own (§9).
2. The Alternative UCIe Did Not Take
Worth understanding, because the contrast explains every choice that follows.
A long-reach serial link — PCIe, Ethernet, any SerDes — usually sends no clock at all. The receiver recovers timing from the data's own transitions using a clock-and-data-recovery loop. That works beautifully over a metre of board, and it costs:
- a CDR loop per lane, which is substantial analogue design;
- transition density guarantees, so encoding or scrambling must ensure the data never goes quiet long enough for the loop to drift;
- power, for a high-speed loop running continuously;
- lock time, because the loop must acquire before data flows.
UCIe's channel is a few millimetres of package routing, and its links are wide. Both facts point the other way. Over a short channel, a clock sent alongside the data arrives in usable condition. And when you have 64 lanes, adding one more physical resource to carry timing is cheap — one extra conductor amortised across 64, instead of 64 recovery loops.
Send the clock. Short channel makes it viable; wide link makes it economical. Everything else — the simple receiver, the low per-lane rate, the low energy per bit — follows from not having to rediscover time.
3. The Forwarded Clock
Chapter 7.3 listed a module's physical resources. Two of them are the subject here: a differential forwarded clock per direction, and the single-ended Valid lane that frames the data. Both accompany the N data lanes.
The mechanism, and this is the part to get exactly right:
- The transmitting module launches data on its data lanes and launches a clock on the forwarded-clock lanes.
- The module sets the forwarded clock's phase at the centre of the data unit interval on its mainband.
- The receiving module samples the incoming data — and the incoming Valid — with the incoming forwarded clock.
The phase relationship is the design's whole point. Placing the clock edge at the centre of the UI means the sampling instant sits as far as possible from both edges of the data eye, which maximises the timing margin available to be eaten by everything in Chapter 7.6. A clock aligned with the data edges would sample exactly where the data is transitioning — the worst possible instant.
Why the clock is differential when the data is not. Chapter 7.2 explained that single-ended data is a deliberate economy on a short channel. The clock does not get that treatment because it is the one signal whose corruption breaks every lane at once. A differential pair rejects common-mode noise, and spending two conductors out of a module's total to protect the timing reference is an excellent trade.
4. Clock and Data Are Siblings, Not Strangers
The mental model that makes source-synchronous reasoning click:
Clock and data are siblings. They come from the same transmitter, are launched by the same logic, and travel through the same package environment. They are not a signal and an unrelated observer.
Contrast the two situations a digital engineer meets:
| Source-synchronous | Unrelated clock domains | |
|---|---|---|
| Relationship | same origin, correlated delays | independent, arbitrary phase |
| What varies | relative skew, bounded and characterisable | full-period phase uncertainty |
| Failure mode | margin erosion — errors when skew exceeds budget | metastability — resolution failure |
| Remedy | timing design, deskew, calibration | synchronisers, FIFOs, handshakes |
| Verified by | STA and SI analysis | CDC tools and simulation |
That table is the chapter's most useful takeaway, because the two failure classes are diagnosed completely differently and people routinely apply one's remedy to the other's problem. A synchroniser does not fix skew. Deskew does not fix metastability.
And the crucial nuance: the arrival of a forwarded clock does not eliminate clock-domain crossings from your design. It eliminates them at the capture point. The forwarded-clock domain is still a foreign clock as far as the rest of the die is concerned, and §9 is entirely about that.
5. DDR: Half the Clock, Twice the Transfers
UCIe's mainband uses DDR signalling — data transfers on both clock edges — so the forwarded clock runs at half the transfer rate. This is verifiable in the specification's own reset-exit conditions, which describe the mainband clock being set to the slowest supported IO data rate, cited as 2 GHz for 4 GT/s. Exactly the factor of two.
Why it matters structurally:
- Half the clock frequency for a given data rate, which halves everything that scales with clock frequency in the distribution network: dynamic power, edge-rate demands, and the difficulty of getting the clock across the die with controlled skew.
- Two transfers per clock period, so the interface's throughput per lane doubles without doubling the clock.
- Both edges must be usable, which means clock duty cycle becomes a first-class concern. A distorted duty cycle shrinks one of the two half-periods, so one of the two transfers per cycle gets less margin than the other — a real effect, and one reason duty-cycle correction appears in high-rate PHY designs.
6. Setup and Hold Still Exist
A widespread misconception deserves killing early: source-synchronous does not remove timing requirements. It redefines the reference they are measured against.
Every capture flop still needs its data stable for a setup time before the capture edge and a hold time after it. What changes is what "the capture edge" means:
- In an ordinary synchronous design, launch and capture edges come from one clock tree on one die, and static timing analysis reasons about the skew within that tree.
- In a source-synchronous link, the launch edge is on the transmitting die and the capture edge is a clock that travelled with the data from that die. Timing analysis reasons about the relative delay of two paths — data and clock — from a common launch point to a common capture point.
So the quantity that matters is not absolute delay at all. Both data and clock can be arbitrarily delayed by the package with no consequence, as long as they are delayed by nearly the same amount. What consumes margin is the difference.
Source-synchronous timing is differential timing. Absolute latency is free; relative mismatch is what you pay for.
7. Clock-to-Data Skew
That difference has a name — skew — and it comes from every stage of the path:
- Transmit paths differ. The clock leaves through the clock output circuitry; each data bit leaves through its own driver. Those paths are not identical.
- Package routes differ. Chapter 6.1's escape routing does not give every conductor the same length, the same layer, or the same neighbours. Matching is a design goal, not a guarantee.
- Receive paths differ. The clock enters through its own receiver and distribution; each data lane through its own.
- PVT affects them unequally. Two paths through different circuit types do not drift identically with process, voltage, and temperature.
The consequence is direct: skew consumes timing margin, one-for-one. A clock deliberately placed at the centre of the UI is placed there to buy margin on both sides; skew moves the effective sampling point off centre and spends that margin.
Two properties make skew tractable, and both matter for how it is fixed:
It is largely systematic. For a given lane, at given conditions, the skew is repeatable rather than random. That is what makes per-lane deskew possible — measure the offset during training and compensate it. Chapter 8.3 owns the mechanism.
It varies with conditions. Systematic does not mean constant: it drifts with temperature and voltage. Which is why deskew measured once at bring-up may not remain right, and why UCIe 3.0 adds runtime TX-side recalibration (Chapter 7.2 §8). The margin question is not "was skew acceptable at bring-up" but "does it stay acceptable across the operating envelope".
8. A Forwarded Clock Is Not a Status Bit
The first RTL boundary error, and it is a conceptual one before it is a coding one.
// WRONG concept — sampling a clock as if it were data.
always_ff @(posedge core_clk)
forwarded_clk_seen_q <= forwarded_clk;Three things are wrong here, in increasing order of seriousness.
It tells you almost nothing. Sampling a clock with an unrelated clock returns whichever half of the period you happened to land in. Over many samples you get a meaningless sequence that depends on the two frequencies' ratio, not on whether the clock is healthy.
It is an unsynchronised asynchronous crossing. The forwarded clock is, by definition, not in core_clk's domain, so this flop can go metastable — and it will, constantly, since a clock is a signal that changes as often as anything can.
It confuses two categories. A forwarded clock is a clocking resource: it belongs in a clock tree, driving flops, with its own constraints in the timing setup. It is not status metadata about the link.
What you actually want, if the question is "is the clock present?", is a clock detector: dedicated hardware that produces a slow, stable, single-bit status from the presence of toggling, which then crosses into a control domain through a proper synchroniser (§13). Detecting a clock and consuming a clock are different jobs done by different hardware.
9. The Real Clock-Domain Boundary
Data captured in the forwarded-clock domain cannot stay there. The rest of the die — the remaining PHY logic, the Adapter, the protocol stack — runs on the local clock, which is generated locally and has no fixed relationship to a clock that arrived from another die. Their frequencies are nominally the same and their phase relationship is arbitrary, and even nominally equal frequencies differ by their reference tolerances.
So the structure is:
forwarded-clock receive domain → elastic / asynchronous boundary → local PHY domainand that boundary is a genuine asynchronous crossing carrying a wide bus, which — per Chapter 7.1 §15 — is exactly the case a two-flop synchroniser cannot handle.
The correct mechanism is an asynchronous FIFO: the write pointer lives in the receive domain, the read pointer in the local domain, the pointers cross to each other Gray-coded so that only one bit changes per increment, and the memory is written and read on independent clocks. Depth absorbs the rate difference over the interval between resynchronisations.
This curriculum already teaches that machinery properly and there is no value in restating it here:
- Metastability — why the problem exists at all.
- The Two-Flop Synchronizer — and its exact scope.
- Why Multi-Bit Buses Can't Use It — the failure mode in detail.
- Gray-Code Pointer Synchronization — why Gray coding makes pointer crossing safe.
- The Asynchronous FIFO — the complete structure.
What is specific to a UCIe PHY is the interface contract around it:
// Illustrative PHY RTL — not UCIe normative signal naming.
// The async FIFO itself is a standard structure (see the CDC track). What the
// PHY owns is the contract on each side of it.
module rx_domain_bridge #(
parameter int DATA_W = 512,
parameter int DEPTH = 8
) (
// Write side: the forwarded-clock receive domain.
input logic rx_fwd_clk,
input logic rx_rst_n, // synchronised into THIS domain
input logic rx_valid,
input logic [DATA_W-1:0] rx_data,
output logic rx_overflow, // sticky: write with no room
// Read side: the local PHY domain.
input logic phy_clk,
input logic phy_rst_n, // synchronised into THIS domain
input logic phy_ready,
output logic phy_valid,
output logic [DATA_W-1:0] phy_data
);Architecture. Two clocks that are nominally the same and actually independent must exchange a wide bus. Only a FIFO does that safely; the depth exists to absorb the accumulated difference between them.
State. Write pointer and read pointer in their own domains, Gray-coded copies crossing each way, the memory, and a sticky rx_overflow. Note the two separate reset inputs, each synchronised into its own domain — Chapter 8.1 §7 explains why sharing one would be a bug.
Cycle behaviour. A write occurs on a rx_fwd_clk edge when rx_valid is high; a read on a phy_clk edge when phy_valid && phy_ready. Because the pointer crossings take cycles to resynchronise, each side's view of the other is conservatively stale — the write side may believe the FIFO is fuller than it is, the read side emptier. That conservatism is what makes it safe, and it is why depth must exceed the resynchronisation latency plus the burst it has to absorb.
Contract. The receive side must never write when full. That obligation cannot be met by hoping — the far die's transmitter is not throttled by your FIFO occupancy, so the system must guarantee the local side drains at least as fast as data arrives, over the long run. Short-term differences are what the depth is for.
Failure. An undersized FIFO overflows, silently, under a burst — and because the write side is in a different clock domain from anything that could complain, the loss is invisible until an integrity check fails much later. Hence rx_overflow being sticky: an overflow that happened once and was not recorded is a debug session you cannot start.
DV. Structural CDC analysis proves the crossing is properly constructed. Simulation with deliberately mismatched clock frequencies within the tolerance budget proves the depth is adequate — running both clocks from one source proves nothing, and is the most common way this bug survives verification.
10. Never Synchronise a Bus Bit by Bit
Restating from Chapter 7.1 §15, because it appears in a new place here and that is exactly why it recurs:
// WRONG — per-bit synchronisers on a bus.
generate
for (genvar i = 0; i < DATA_W; i++) begin : g_bad_sync
always_ff @(posedge phy_clk) begin
meta_q[i] <= rx_data[i];
sync_q[i] <= meta_q[i];
end
end
endgenerateEach bit resolves on whichever destination cycle its own timing produces, so a word whose bits change together can be captured with some bits old and some new. The reconstructed value never existed at the source.
The reason this deserves a second appearance: at the forwarded-clock boundary the two clocks are nominally the same frequency, which makes the code look almost reasonable and makes the bug rare rather than constant. Bits mostly land together — until the two clocks' slow phase drift walks the sampling point across the data transition, at which point a burst of corrupt words appears and then disappears again. Rare and phase-dependent is the worst diagnostic signature there is, because it correlates with nothing you can control.
Use the right mechanism for the path: an async FIFO for streaming data, a handshake for occasional control, Gray coding for a value that changes by one, and source-synchronous capture where the data brings its own clock.
11. PLL Lock Is Status, and It Comes From Elsewhere
A PHY needs local clocks — for the transmit side, for the digital logic, for whatever internal rates the design uses — and those are generated on-die, typically by a PLL. That PLL takes time to acquire, and until it has, its output is not a usable clock.
UCIe treats this as a first-class bring-up prerequisite. The specification's RESET-state exit conditions require, among other things, that power supplies are stable, that a sideband clock is available and running, and that mainband and D2D-adapter clocks are stable and available — with the state held for a minimum duration explicitly to let circuitry including PLLs stabilise.
pll_lock is an asynchronous status bit from an analogue block. Consuming it raw is a bring-up bug:
// WRONG — raw asynchronous status driving a state transition.
if (pll_lock)
state_d = CLOCK_READY;Why this is nastier than it looks. It is a single bit, so metastability may resolve harmlessly most of the time — and this bug will pass simulation, pass bring-up on the bench, and fail on a small percentage of parts at a particular temperature. It is a statistical bug in a sequencing path, which is the combination hardest to reproduce.
The correct consumption is synchronised and qualified:
// Illustrative PHY RTL — not UCIe normative signal naming.
localparam int LOCK_CNT_W = 8;
logic lock_meta_q, lock_sync_q;
logic [LOCK_CNT_W-1:0] lock_stable_q;
logic clock_ready;
always_ff @(posedge phy_clk or negedge phy_rst_n) begin
if (!phy_rst_n) begin
lock_meta_q <= 1'b0;
lock_sync_q <= 1'b0;
lock_stable_q <= '0;
end else begin
// Two-flop synchroniser: correct here because this is ONE bit, changing
// rarely, whose consumer tolerates a cycle or two of latency.
lock_meta_q <= pll_lock_async;
lock_sync_q <= lock_meta_q;
// Qualification: an analogue lock indication can chatter around the
// acquisition boundary. Require sustained assertion, and reset the count
// on any deassertion so a glitch cannot be accumulated across.
if (!lock_sync_q) lock_stable_q <= '0;
else if (!(&lock_stable_q)) lock_stable_q <= lock_stable_q + 1'b1;
end
end
assign clock_ready = (&lock_stable_q);Architecture. An analogue block reports a condition asynchronously and imperfectly. Digital logic needs a clean, monotonic, trustworthy signal to sequence on.
State. Two synchroniser flops and a saturating stability counter. Three flops total, and every one of them is load-bearing.
Cycle behaviour. Cycle N: pll_lock_async rises. N+1: lock_meta_q — possibly metastable — takes it. N+2: lock_sync_q is clean. N+3 onward: the counter increments once per cycle while lock holds. When it saturates, clock_ready asserts. Any deassertion zeroes the counter, so the qualification restarts rather than resuming.
Contract. The PHY bring-up FSM gates its clock-ready state on this. Chapter 7.1's PHY_TRAIN sits downstream of it.
Failure. Without the counter, a chattering lock indication briefly asserts, the FSM advances, and the design begins operating on a clock that has not settled — producing bring-up failures that depend on the exact moment the FSM sampled. Without the deassert-clears branch, a signal toggling around the boundary accumulates count across its gaps and qualifies a clock that was never stable.
DV. Assert that clock_ready never asserts without a sustained window. Drive a chattering pll_lock_async in the testbench — one that asserts for fewer cycles than the qualification requires, repeatedly — and confirm clock_ready stays low. And note the limit: none of this proves the synchroniser resolves metastability. That is structural CDC analysis's job, as Chapter 7.1 §15 established.
// Illustrative — no state advance before the clock is qualified.
property p_no_advance_before_clock_ready;
@(posedge phy_clk) disable iff (!phy_rst_n)
$rose(phy_state_q == PHY_CLOCK_OK) |-> clock_ready;
endproperty
// And the qualification itself is honest: ready implies a full stable window.
property p_ready_implies_stable_window;
@(posedge phy_clk) disable iff (!phy_rst_n)
clock_ready |-> $past(lock_sync_q, 1) && $past(lock_sync_q, 2);
endpropertyThe bug the first property catches is an FSM leaving its wait state one cycle early — reading lock_sync_q instead of clock_ready, which is a single-character mistake with a field-return failure rate.
12. Clock Muxing Is Not a Ternary
// WRONG — a combinational clock multiplexer.
assign selected_clk = mode ? clk_a : clk_b;This compiles, simulates cleanly, and is broken hardware.
What happens physically. When mode changes, the output switches from one clock to the other at an arbitrary point in both their periods. The result can be a glitch — a pulse far shorter than either clock's period — or a runt, a pulse that does not reach a full logic level, or a double edge where the two clocks' transitions land close together. Any of those clocks the downstream flops at a moment nothing was prepared for. Some flops see the extra edge, some do not, and the domain's state becomes internally inconsistent in a way no reset recovers from cleanly.
Why simulation misses it. An event-driven simulator with zero-delay assignments produces a clean switch. The glitch is a physical phenomenon of two real clocks with real transition times.
The correct solution is a glitchless clock switching cell — synchronise the select into both clock domains, ensure the outgoing clock is gated off and has completed its low phase before the incoming one is gated on, and use a library integrated clock gate or an integrated clock multiplexer. Do not hand-write this in RTL. It is a structure whose correctness depends on the cell's internal timing, and every standard-cell library provides one.
The digital lesson generalises: clocks are not data, and expressions that are perfectly correct on data are wrong on clocks. That is the same category error as §8, appearing in a different place.
13. Clock Gating and Clock Loss Are Different Things
They look identical from inside a flop — no edges arrive — and they mean opposite things.
Clock gating is deliberate: the design stops toggling a clock to save power, having first ensured the logic is in a state where stopping is safe. It is an internal decision, reversible at will, and the state it left behind is valid.
Clock loss is a failure: the forwarded clock is not arriving, because the far die is powered down, in reset, faulty, or the channel is broken. It is not the local design's decision, may not be reversible, and the state left behind is whatever happened to be there when the edges stopped.
// Illustrative PHY RTL — not UCIe normative signal naming.
// A clock detector produces slow, stable status from the presence of toggling.
// This lives in a domain that is NOT the clock being observed.
logic [CLK_WD_W-1:0] fwd_clk_watchdog_q;
logic fwd_clk_lost;
always_ff @(posedge phy_clk or negedge phy_rst_n) begin
if (!phy_rst_n) begin
fwd_clk_watchdog_q <= '0;
end else if (fwd_clk_activity_sync) begin
fwd_clk_watchdog_q <= '0; // activity resets the watchdog
end else if (!(&fwd_clk_watchdog_q)) begin
fwd_clk_watchdog_q <= fwd_clk_watchdog_q + 1'b1; // saturating
end
end
assign fwd_clk_lost = (&fwd_clk_watchdog_q);Architecture. A domain cannot detect its own clock stopping — with no edges, no logic in that domain runs. Detection must therefore live in a different, still-running domain, observing activity that crossed to it.
State. A saturating watchdog counter, cleared by observed activity.
Cycle behaviour. Every phy_clk cycle without observed forwarded-clock activity increments the counter; any activity zeroes it. Saturation asserts loss. The counter width sets the detection threshold — long enough to tolerate legitimate quiet periods, short enough to detect a real failure usefully.
Contract. The link FSM consumes fwd_clk_lost to leave ACTIVE and report a fault. Chapter 7.1 §16's rule applies: the Adapter receives the conclusion, not the watchdog value.
Failure. Without detection, a lost forwarded clock produces a link that simply stops. No error, no state change, no timeout — the receive domain is frozen mid-operation and nothing in it can report that. From the Adapter's point of view the link is up and inexplicably idle, which is among the least diagnosable failures possible.
The design rule: any state machine that can wait on something clocked by a foreign clock needs a bound in a domain that keeps running. That is Chapter 7.1 §18's bounded-waiting principle, in its clocking form.
14. Who Owns Data When the Clock Stops
The ownership question, in its clocking incarnation.
Data captured into the receive domain and written into the async FIFO before a clock loss is somewhere. Its fate must be defined:
- Retained — it is in the FIFO, the read side keeps its own clock, and it can be drained even though no more will arrive. Legitimate, and often correct.
- Flushed — the architecture treats a clock loss as invalidating everything in flight, and the layer above is told so.
- Reported lost — the mechanism above is informed, so its own recovery can act.
What is never acceptable is the fourth option — the data silently disappearing while the layer above believes it was delivered. This is Chapter 7.1 §12's accepted-data rule, and Chapter 5.5's anti-pattern, appearing at the clock boundary:
// WRONG — a clock event silently discards data already captured.
always_ff @(posedge phy_clk or negedge phy_rst_n) begin
if (!phy_rst_n) fifo_flush <= 1'b0;
else if (fwd_clk_lost) fifo_flush <= 1'b1; // erases without reporting
endThere is genuine precedent for defining this explicitly rather than leaving it to implementation. UCIe/LPIF material describes credit counters being reassigned to their initial advertised values whenever RDI states transition away from Active, and a retimer draining or dumping its receiver buffer before re-entering Active. Both are the same discipline: a state transition that invalidates in-flight data must reset the accounting on both sides, so nobody is left waiting for something that no longer exists.
A flush is acceptable. An unannounced flush is not. The rule is not "never discard" — it is "never discard without the other side knowing".
15. Reset Needs a Clock, and Clocks Need Reset
The bridge into Module 8, and a genuine chicken-and-egg that has to be designed rather than assumed.
- Reset release requires a clock. The safe pattern — asynchronous assertion, synchronous deassertion — releases reset by shifting a value through flops in the destination domain. No clock, no shifting, no release.
- Clock readiness often requires logic that is out of reset. The PLL configuration, the clock detector, the mux control, the bring-up FSM sequencing it all — those are flops, and they need reset released to function.
Reset release requires a clock; clock readiness often requires reset. Their sequencing must be deliberately designed, not assumed.
UCIe's RESET-state exit conditions show this being taken seriously at the specification level: leaving RESET requires power supplies stable, a sideband clock available and running, mainband and adapter clocks stable and available, and the mainband clock set to the slowest supported rate. That last item is the elegant part — bring-up begins at the easiest operating point, where timing margin is largest, and rate increases only after the link works.
The resolution in practice is a layered ordering: a small always-available clock domain — often driven by the sideband, which is why the sideband clock appears among the prerequisites — comes up first and sequences everything else. Chapter 8.1 builds this properly.
An illustrative bring-up sequence, with the caveat that actual latencies are implementation-specific:
| Cycle | Event | clock_ready | FSM |
|---|---|---|---|
| N | pll_lock_async rises | 0 | WAIT_CLOCK |
| N+1 | lock_meta_q captures (may be metastable) | 0 | WAIT_CLOCK |
| N+2 | lock_sync_q clean and high | 0 | WAIT_CLOCK |
| N+3 … N+k | qualification counter accumulating | 0 | WAIT_CLOCK |
| N+k+1 | counter saturates | 1 | WAIT_CLOCK |
| N+k+2 | FSM samples clock_ready | 1 | CLOCK_OK |
Six rows, four of which are the design refusing to be hurried. That refusal is the feature.
16. Failure Signatures
Four clocking-adjacent failure classes, each with a different investigation:
| Symptom | Clock absent | Bad CDC | Clock-to-data skew | Lane mapping |
|---|---|---|---|---|
| Data progress | stops entirely | continues | continues | continues |
| Bit errors | none — nothing moves | occasional, bursty | yes, per-lane | every transfer |
| Reproducibility | deterministic | rare, non-deterministic | intermittent | deterministic |
| Temperature | no effect | little or none | strong effect | none |
| Supply voltage | may cause it | little | strong effect | none |
| Link rate | — | phase-relationship dependent | worse when faster | unchanged |
| Lane distribution | all | not lane-local | concentrated on worst lanes | follows logical structure |
| Watchdog / detector | asserts | silent | silent | silent |
| First move | clock path, far die, watchdog | CDC/RDC structural analysis | deskew, calibration, SI | compare both ends' maps |
The column worth learning is bad CDC, because it is the one people misdiagnose. Its distinguishing feature is that it is insensitive to temperature and supply while being sensitive to the phase relationship between two clocks — which usually means it changes when you change a frequency, or when a completely unrelated part of the system alters a PLL setting, and stays stubbornly the same when you heat the part. Chapter 7.6's environmental sweeps, which resolve most margin questions, will report nothing. That silence is the clue.
17. Who Proves What
Four tools, four questions, no overlap:
| Question | Tool |
|---|---|
| Do data and clock meet setup and hold at capture, across PVT? | STA with source-synchronous constraints |
| Are all clock-domain crossings structurally sound? | CDC analysis |
| Are reset-domain crossings sound? | RDC analysis (Chapter 8.1 §14) |
| Is there enough voltage and timing margin in the channel? | SI / channel analysis (Chapter 7.6) |
| Does the FSM sequence correctly, gate correctly, and never lose data? | RTL simulation and formal |
| Does it hold across real parts, temperature, and voltage? | Silicon validation |
None of these substitutes for another, and the gaps between them are where clocking bugs live. A design with clean STA, clean CDC, and a passing testbench can still fail at temperature — because the first proved timing at analysed corners, the second proved crossings are constructed correctly, and the third proved logic given an ideal clock.
18. Common Misconceptions
"A forwarded clock means there is no timing problem." It provides a reference. Skew between clock and data still consumes margin, still varies with PVT, and still requires deskew and calibration (§7).
"Source-synchronous means no CDC." It removes the crossing at capture. Data still has to reach the local domain, and that is a genuine asynchronous crossing of a wide bus (§9).
"The forwarded clock can be sampled like status." It is a clocking resource. To know whether it is present, use a clock detector and cross its slow, stable output properly (§8, §13).
"A two-flop synchroniser can move the data bus." Each bit resolves independently, producing words that never existed at the source — and at nominally-equal frequencies the bug is rare and phase-dependent, which is worse (§10).
"pll_lock can be consumed asynchronously." It is asynchronous status from an analogue block, and it can chatter. Synchronise it, then qualify it (§11).
"Clock muxing is a ternary operator." A combinational mux produces glitches and runt pulses that simulation does not model. Use a glitchless switching cell (§12).
"Clock gating and clock loss are the same." One is a deliberate, reversible decision with valid state behind it; the other is a failure with arbitrary state behind it (§13).
"Reset can release while the destination clock is stopped." Synchronous deassertion needs edges to shift through. No clock, no release — and that is correct behaviour, not a bug to work around (§15).
"If the link trains, skew must be fine at all corners." Training establishes a working point at the conditions present at bring-up. Skew drifts with temperature and voltage, which is why runtime recalibration exists (§7).
"Source-synchronous eliminates setup and hold." It changes what they are measured against. Absolute delay becomes free; relative mismatch is what you pay for (§6).
19. Understanding Check
20. Summary and What Comes Next
UCIe's receiver does not rediscover time — the transmitter forwards a clock alongside the data, and the receiver samples in the transmitter's timing frame. The clock is differential, one per direction, because it is the one signal whose corruption breaks every lane at once, and its phase sits at the centre of the data UI to maximise the margin on both sides. DDR halves the clock frequency for a given rate — verifiable in UCIe's own reset conditions, which cite a 2 GHz mainband clock for 4 GT/s.
Source-synchronous does not remove setup and hold; it makes them differential. Absolute delay is free, relative mismatch is what you pay for, and that mismatch — skew — comes from unequal transmit paths, package routes, receive paths, and PVT drift. It is largely systematic, which is what makes per-lane deskew possible, and it varies with conditions, which is why runtime recalibration exists.
The digital consequences: a forwarded clock is a clocking resource, not a status bit — use a detector for presence. The forwarded-clock-to-local boundary is a real asynchronous crossing of a wide bus, so it needs an async FIFO with independently synchronised resets on each side, sized against a real frequency difference. Never synchronise a bus bit by bit — and here the bug is rare and phase-dependent, which is worse than constant. Qualify PLL lock, don't just synchronise it. Never write a combinational clock mux. Distinguish deliberate gating from loss, detect loss from a domain that is still running, and never let a clock event silently discard data — a flush is acceptable, an unannounced flush is not.
And the handoff: reset release requires a clock; clock readiness often requires reset. That circularity is designed around, not worked around.
Forwarding the clock gives the receiver a timing reference. It does not guarantee that enough timing or voltage margin remains by the time clock and data arrive together at the sampling instant:
- 7.6 — Signal Integrity — everything that spends that margin, how the effects combine, and how to tell a margin problem from a digital one.
Browse the full path on the UCIe tutorials index.