Skip to content

UCIe · Module 8

Link Calibration

How a UCIe PHY chooses and maintains its operating point — candidate, best, and active codes, apply-settle-measure, sweeping for a passing window and taking its centre, commit gating, runtime recalibration, hysteresis, and calibration debug.

Training ended with a decision: these lanes work, this is their identity, these are their relative timings, commit. Every one of those answers was discrete — a lane is usable or it is not, a logical position maps to one physical resource or another.

Underneath sits a different kind of question, and it does not have a discrete answer. The receiver samples at some instant within the bit period and compares against some threshold. The transmitter drives with some strength into some termination. Each of those is a control code with a range of values, most of which work and some of which work better — and which ones work depends on the process corner this die landed on, the temperature it is running at, the supply it is being fed, and the channel it happens to be attached to.

Calibration is how the PHY answers that question, and — because the conditions move — how it keeps answering it.

1. The One-Sentence Model

Training decides whether the link can work. Calibration decides where it should operate inside the available margin.

The distinction matters because the two failure modes are completely different. A training failure means a resource is missing or unidentifiable — a lane is dead, a map is incoherent, a required lane never appeared. A calibration failure means the resources are all present and no setting of them is good enough, which is a margin finding and points at Chapter 7.6, not at Chapter 8.3.

And there is a second distinction hiding inside the first. Training's output is a fact about the assembly, stable for as long as the assembly is. Calibration's output is an optimum under conditions, and conditions change. That is why calibration is the only bring-up phase that has a runtime counterpart.

2. Calibration Is Not Training

TrainingCalibration
Questionwhich resources exist and how are they arranged?what setting gives the best margin?
Answer typediscrete — lane, identity, mapa code within a range
Methodobserve evidence, decidesearch: apply, measure, compare
Output stabilitystable while the assembly isdrifts with temperature and voltage
Failure meansa resource is missing or unidentifiableno setting is good enough
Runs again whenthe link is retrainedconditions move
Fixed byrepair, remap, reconfigurationmore margin — or accepting less performance

The row worth dwelling on is the last. A calibration failure is rarely fixed by more calibration. If no setting produces a passing result, the search is reporting honestly that the margin is not there, and the answer lies in the channel, the PDN, the rate, or the package — the whole of Chapter 7.6.

3. What Actually Gets Calibrated

Chapter 8.3 §3 listed UCIe's MBTRAIN sub-states, and several of them are calibration by another name:

  • VALVREF and DATAVREF — reference-voltage training for the valid and data lanes. This is choosing the decision threshold, which Chapter 7.2 §5 identified as part of the signal path on a single-ended link.
  • TXSELFCAL and RXCLKCAL — transmitter and receiver clock self-calibration.
  • VALTRAINCENTER, DATATRAINCENTER1, DATATRAINCENTER2 — centring the data-to-clock relationship. In a published Intel/Cadence interoperability demonstration the PHY implemented these sub-states as a data-to-clock eye sweep, with the partner responding to enable the test — an illustration of how centring can be realised, not a statement that the specification prescribes that algorithm.
  • SPEEDIDLE — the speed change, after which everything above must hold at the new rate.

Two things follow that shape the rest of this chapter.

A sweep is the actual mechanism. The *TRAINCENTER sub-states are described as a sweep with a cooperating partner — which is precisely the apply-measure-compare loop of §5, running inside the training state machine rather than after it. §9's window search is not a hypothetical architecture; it is what those sub-states do.

Calibration is interleaved with training, not sequential to it. Chapter 8.3 made this point and it bears repeating here, because the chapter order in this curriculum implies otherwise. Treating them as separate chapters is a teaching decision; the specification's flow interleaves them, and a bring-up debug that assumes a clean boundary will look for one that is not there.

A calibration control loop. The calibration controller applies a candidate code, which after a settle delay reaches the PHY electrical and timing controls, then the channel and monitor produce a measurement, which is accumulated and compared to update the best-point state. The best-point state selects the next candidate and, at the end of the search, commits the active code that drives live traffic.Cal controllersearch FSMCandidate codeunder testPHY controldelay, drive, refBest-point statewindow edges, scoreMeasurementaccumulated samplesChannel + monitorpass or fail resultActive codecommitted — the only code that drives live trafficapplysettleresultcomparenext codecommit12
Figure 1 — calibration as a closed control loop, and the three copies of the setting that make it safe. The controller applies a candidate code, waits for the circuit to settle, and measures; the measurement updates the best-point state, which picks the next code. Only when the search completes does the best code become active. The candidate must never reach the datapath — a search deliberately visits bad settings, and the point of the separation is that traffic never sees them.

4. Three Copies of the Setting

The core digital-control pattern of the chapter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration RTL — not normative UCIe naming or encoding.
localparam int CODE_W = 6;
 
typedef struct packed {
  logic [CODE_W-1:0] phase;      // sampling-point code
  logic [CODE_W-1:0] vref;       // decision-threshold code
} cal_code_t;
 
cal_code_t candidate_code_q;   // currently under test — visits bad values
cal_code_t best_code_q;        // best measured so far this search
cal_code_t active_code_q;      // what the live datapath actually uses

Why three and not two. Each answers a different question, and collapsing any pair creates a distinct bug:

CopyQuestionIf merged with another
candidatewhat am I testing right now?merged with active → live traffic sees every bad setting (§5)
bestwhat is the best I have found?merged with candidate → the last code wins, not the best (§8)
activewhat is the datapath using?merged with best → settings change mid-traffic (§12)

Architecture. A search must be free to try bad values; the datapath must never see one; and the winner must survive being followed by losers. Those three requirements need three registers.

State. Three code structs. Real area, and the same trade as Chapter 8.3 §10's shadow configuration — paid for the same reason.

Cycle behaviour. candidate_code_q changes on every search step. best_code_q changes only on an improvement. active_code_q changes only at commit.

Contract. The PHY's analogue controls follow candidate_code_q during calibration and active_code_q during operation — which requires a defined mode, §13.

Failure. See the table. All three are real and all three have appeared in production designs.

5. The Candidate Must Not Drive Live Traffic

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — the code under test drives the operating datapath.
assign phy_control_code = candidate_code_q;

A search deliberately visits settings that do not work — that is how it finds the boundaries of the passing region. If the datapath is carrying traffic while the search runs, every one of those bad settings corrupts data.

The failure is worse than it first appears, because it is self-reinforcing. If the link's own error rate is what triggers recalibration, and the recalibration corrupts data while searching, then the search creates the condition that requested it. Chapter 7.6 §14's recalibration storm arrives here with a new mechanism.

Two architecturally sound answers:

Calibrate in a mode where traffic is not flowing. The link is quiesced, the search runs, the result is committed, traffic resumes. Simple and correct, and it costs link availability.

Calibrate a resource that is not in use. Search on a spare lane, or on a redundant path, or in a direction that is idle — then apply the result. Costs nothing in availability and needs the resource to exist and to be representative.

What is never acceptable is the third option: searching on the live path and hoping the bad points are brief.

6. Apply, Settle, Measure

The sequencing point that separates a working calibration controller from one that produces nonsense.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — measuring in the same breath as applying.
assign phy_candidate = candidate_code_q;
assign measurement   = monitor_result;      // sampled immediately

A calibration code drives an analogue circuit. Changing a delay code repositions a sampling point; changing a reference code moves a threshold; changing drive strength alters an output stage. None of that happens instantaneously — the circuit has a settling time, and during it the behaviour corresponds to no valid setting at all.

Measure during that window and the result describes a transient. The search then compares transients against each other and picks whichever one happened to look best — a plausible-looking answer derived from measurements of nothing. The link may even work afterwards, marginally, which is the worst outcome because nobody investigates.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration RTL — not normative timing.
localparam int SETTLE_W = 10;        // illustrative width
logic [SETTLE_W-1:0] settle_timer_q;
logic                settled;
 
assign settled = (&settle_timer_q);
 
always_ff @(posedge cal_clk or negedge rst_n) begin
  if (!rst_n)                          settle_timer_q <= '0;
  else if (cal_state_q != CAL_SETTLE)  settle_timer_q <= '0;   // phase-local
  else if (!settled)                   settle_timer_q <= settle_timer_q + 1'b1;
end

Architecture. A digital controller driving analogue hardware must respect the hardware's response time. The settle delay is where the digital domain acknowledges that its outputs are not instantaneous.

State. One saturating, phase-local timer — the same discipline as Chapter 8.3 §14, for the same reason.

Cycle behaviour. Cleared on entry to CAL_SETTLE, counts while there, saturates. settled gates the transition to measurement.

Contract. The settle duration must exceed the analogue block's worst-case settling time across PVT. That number comes from the circuit designer, not from a tutorial, and it is one of the more common places a digital and analogue team's assumptions fail to meet.

Failure. Too short and the search measures transients. Too long and calibration takes proportionally longer, which matters when it is on the bring-up critical path or when runtime recalibration must fit inside a traffic gap.

7. One Measurement Is Not a Measurement

Chapter 8.3 §5 made this argument for lane qualification. It applies with more force here, because a calibration search makes many decisions from measurements, and each bad one steers the search.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration RTL — not normative naming or thresholds.
localparam int SAMPLE_TARGET = 64;    // illustrative
localparam int PASS_W        = $clog2(SAMPLE_TARGET + 1);
 
logic [PASS_W-1:0] pass_count_q;
logic [PASS_W-1:0] sample_count_q;
logic              measurement_done;
logic              candidate_passes;
 
assign measurement_done = (sample_count_q == PASS_W'(SAMPLE_TARGET));
// Score as a count, compared against a precomputed threshold — no division.
assign candidate_passes = (pass_count_q >= PASS_W'(PASS_THRESHOLD));
 
always_ff @(posedge cal_clk or negedge rst_n) begin
  if (!rst_n) begin
    pass_count_q   <= '0;
    sample_count_q <= '0;
  end else if (cal_state_q != CAL_MEASURE) begin
    pass_count_q   <= '0;                  // per-candidate state
    sample_count_q <= '0;
  end else if (!measurement_done) begin
    sample_count_q <= sample_count_q + 1'b1;
    if (monitor_pass) pass_count_q <= pass_count_q + 1'b1;
  end
end

Architecture. The underlying quantity is a rate — Chapter 7.6's point that marginality is statistical — so a single observation at a candidate code is a coin flip weighted by that code's margin. A code near the edge of the passing region will pass sometimes, and a search that trusts one sample will place a window edge in a different place every run.

State. Two counters, cleared per candidate rather than per search, because each candidate needs its own evidence.

Cycle behaviour. Both advance once per observation while measuring; the comparison is against a precomputed threshold, so no division is needed — Chapter 7.6 §13's arithmetic point.

Contract. The comparison logic in §8 consumes candidate_passes only when measurement_done.

Failure. With one sample, the measured window edges move between runs, so the chosen centre moves, so the deployed margin varies part to part and boot to boot — producing a population where some units are fine and some are marginal for no discoverable reason.

8. Track the Best, Not the Last

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — every candidate overwrites the winner.
always_ff @(posedge cal_clk) begin
  if (measurement_done) begin
    best_code_q  <= candidate_code_q;      // unconditional
    best_score_q <= pass_count_q;
  end
end

At the end of the sweep, best_code_q holds the last code tested, not the best. If the sweep runs from low to high, the design deploys the highest code — which is typically at the edge of the passing region or outside it entirely.

This bug is unusually good at hiding. If the passing region happens to extend to the top of the range, the last code passes, calibration reports success, and the link works — with almost no margin on one side. It then fails at temperature, in the field, on some units, and the calibration log says everything went fine.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — the winner survives being followed by losers.
always_ff @(posedge cal_clk or negedge rst_n) begin
  if (!rst_n) begin
    best_score_q <= '0;
    best_code_q  <= '0;
    best_valid_q <= 1'b0;
  end else if (cal_state_q == CAL_PREPARE) begin
    best_score_q <= '0;                    // per-search state
    best_valid_q <= 1'b0;
  end else if (measurement_done && (pass_count_q > best_score_q)) begin
    best_score_q <= pass_count_q;
    best_code_q  <= candidate_code_q;
    best_valid_q <= 1'b1;
  end
end

Architecture. A search visits candidates in an arbitrary order, so the winner must be retained against everything that follows it.

State. A best score, a best code, and a validity bit. best_valid_q matters: it distinguishes "the best code is X" from "no candidate has ever passed", and §11 refuses to commit without it.

Cycle behaviour. Updated only on a strict improvement, and cleared at the start of each search.

Contract. The commit in §11 uses best_code_q and requires best_valid_q.

Failure. Without the strict comparison, the last code wins. Without the per-search clear, a later search inherits a previous search's best — under conditions that have since changed, which is exactly what the new search was run to account for.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — the best score never decreases within one search.
property p_best_score_monotonic;
  @(posedge cal_clk) disable iff (!rst_n)
    (cal_state_q != CAL_PREPARE) |=> (best_score_q >= $past(best_score_q));
endproperty

9. Finding the Window, Choosing the Centre

For a timing-phase sweep the result is not a single best point — it is a region. Sweeping the sampling code across its range produces a run of codes that pass, bounded by codes that fail:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
code:    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
result:  ✗  ✗  ✗  ✓  ✓  ✓  ✓  ✓  ✓  ✓  ✓  ✓  ✗  ✗  ✗  ✗
                 ▲                    ▲
              first                 last          centre = 7 or 8

The right answer is the centre of the passing window, not its first element and not its widest-scoring element.

Why the centre. The window's edges are where margin runs out. A code adjacent to a failing code has margin on one side only, so any drift in that direction fails immediately. The centre is equidistant from both failure boundaries, so it tolerates the largest drift in either direction — and drift is guaranteed, because temperature and voltage move.

This is the same argument Chapter 7.5 §3 gave for the forwarded clock's phase sitting at the centre of the data UI, arriving from the other end: there it was a design choice about where to place the clock, here it is a measurement of where the usable region actually turned out to be on this part.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration RTL — window-edge capture during a sweep.
logic [CODE_W-1:0] first_pass_q;
logic [CODE_W-1:0] last_pass_q;
logic              seen_pass_q;
logic [CODE_W-1:0] window_centre;
logic [CODE_W:0]   centre_sum;          // one extra bit — no overflow
 
always_ff @(posedge cal_clk or negedge rst_n) begin
  if (!rst_n) begin
    first_pass_q <= '0;
    last_pass_q  <= '0;
    seen_pass_q  <= 1'b0;
  end else if (cal_state_q == CAL_PREPARE) begin
    seen_pass_q  <= 1'b0;               // per-search state
  end else if (measurement_done && candidate_passes) begin
    if (!seen_pass_q) begin
      first_pass_q <= candidate_code_q.phase;   // first pass of this sweep
      seen_pass_q  <= 1'b1;
    end
    last_pass_q <= candidate_code_q.phase;      // keeps advancing while passing
  end
end
 
// Width-safe midpoint: sum in CODE_W+1 bits, then shift.
assign centre_sum    = {1'b0, first_pass_q} + {1'b0, last_pass_q};
assign window_centre = centre_sum[CODE_W:1];    // == sum >> 1, correctly sized

Architecture. A sweep produces a region, and the deployable setting is derived from the region's shape rather than from any single measurement.

State. Two edge codes and a seen flag. last_pass_q is updated on every passing candidate, so at the end of a monotonic sweep it holds the last one — which is correct only because the sweep is monotonic in code order, an assumption worth stating rather than assuming.

Cycle behaviour. Edges captured as the sweep progresses; the centre is combinational.

Contract. The commit uses window_centre, not best_code_q, when the metric is a pass/fail region. Where the metric is a graded score, best_code_q is appropriate — and the design must be clear about which it is, because using a graded best on a pass/fail sweep picks an arbitrary member of the passing set.

Failure — the arithmetic one. Writing assign window_centre = (first_pass_q + last_pass_q) >> 1; with both operands CODE_W wide makes the addition CODE_W wide too, so it wraps whenever the sum exceeds the range. With CODE_W = 6, first = 40 and last = 50 gives a sum of 90, which wraps to 26, and the centre computes as 13 — a code well outside the passing window, deployed with confidence. The extra bit is not pedantry; it is the difference between the middle of the window and an arbitrary failing code.

A caution on the model. A real sweep may produce a ragged boundary rather than a clean run, or more than one passing region. Taking the midpoint of the outermost first and last passes across a split region can land squarely in the gap between them. A production controller tracks the widest contiguous run rather than the outermost edges — worth knowing that this simple version has an assumption in it.

10. Windows Tell You More Than Codes

The width of the passing window is a margin measurement, and it is often more useful than the code that was chosen:

ObservationWhat it means
Wide windowcomfortable margin; drift is well tolerated
Narrow windowthe link works and has little headroom — Chapter 7.6's whole subject
No windowno setting passes; a margin or configuration problem, not a search problem
Window position moves with temperatureexpected — this is why runtime recalibration exists
Window position differs greatly between lanesper-lane channel or skew differences worth investigating
Window width differs greatly between partsprocess spread, or an assembly problem on the narrow ones

Log the window, not just the chosen code. A fleet where every part calibrates successfully with a two-code window is a fleet about to have a field problem, and the chosen codes alone will not reveal it.

11. Commit Requires a Valid Result

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — never commit a setting that no measurement supports.
property p_no_commit_without_valid_window;
  @(posedge cal_clk) disable iff (!rst_n)
    cal_commit_pulse |-> (seen_pass_q && best_valid_q && measurement_done);
endproperty
 
a_no_commit_without_valid_window :
  assert property (p_no_commit_without_valid_window)
  else $error("Calibration committed with no valid measurement or window.");

The bug it catches is a controller that reaches the end of its sweep, finds nothing passed, and commits whatever best_code_q holds — which is its reset value, or a leftover from a previous search. The link then operates at an arbitrary code, and the failure looks like a marginal channel rather than a calibration controller that gave up quietly.

The correct behaviour on an empty window is to fail visibly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration RTL — sticky failure cause, not normative.
typedef enum logic [2:0] {
  CAL_FAIL_NONE      = 3'd0,
  CAL_FAIL_NO_WINDOW = 3'd1,   // swept the range, nothing passed
  CAL_FAIL_TIMEOUT   = 3'd2,   // measurement or settle never completed
  CAL_FAIL_MONITOR   = 3'd3,   // the measurement source never became valid
  CAL_FAIL_CONFIG    = 3'd4    // the search was configured illegally
} cal_fail_cause_t;
 
cal_fail_cause_t cal_fail_cause_q;   // sticky until an explicit clear

The four causes point in four different directions, and that is the value of recording them. NO_WINDOW sends you to Chapter 7.6 — margin, channel, PDN, rate. TIMEOUT and MONITOR are controller or handshake problems and stay inside this chapter. CONFIG is a software or integration error.

12. Stability While Operating

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — the active code does not move under live traffic.
property p_active_cal_stable_while_operational;
  @(posedge cal_clk) disable iff (!rst_n)
    (link_operational && !cal_commit_pulse) |=> $stable(active_code_q);
endproperty

This is the property that makes §4's three-copy separation enforceable rather than aspirational, and it is the calibration counterpart of Chapter 8.3 §16's configuration-stability assertion and Chapter 7.3 §7's lane-map stability.

Note the exemption for cal_commit_pulse. A commit is a change to the active code, so the property must permit it — and that is exactly why §13 insists a commit under live traffic requires a defined safe point rather than simply being allowed.

13. Runtime Recalibration

Chapter 7.2 §8 and Chapter 7.6 §9 both arrived at the same conclusion from different directions: a setting chosen at bring-up is optimal for bring-up conditions, and conditions move. Temperature drifts tens of degrees between idle and sustained load. Supply moves with activity. Devices age.

UCIe 3.0 responds to this directly: the Consortium lists runtime TX-side recalibration among the specification's additions, alongside doubling the maximum rate to 48 and 64 GT/s. The two are related — the faster the signalling, the smaller the margins, and the less tolerable it is to calibrate once and hope. What the specification adds is the capability; the trigger policy in §14 is an implementation choice, and whether a given design uses it at all is a product decision.

But changing a live link's operating point is not free, and the rules are the ones this chapter and Chapter 7.1 have already established:

The candidate must still not touch live traffic (§5). Runtime does not relax this — it makes it harder, because the link is by definition in use.

A commit needs a safe point. Changing the active code mid-transfer changes the sampling instant or threshold underneath data already in flight. The commit must land where nothing is mid-flight, which is the same quiesced-boundary requirement as Chapter 8.3 §11's configuration commit.

Accepted data must survive. Chapter 7.1 §12's rule: once the PHY has accepted a transport unit, its fate must remain knowable. A recalibration that discards in-flight data without reporting it is the same violation as a fault that does, wearing a different hat.

Calibration must not silently invalidate traffic already accepted. Stalling, reporting, or draining first are all acceptable. Vanishing is not.

14. Policy, and the Recalibration Storm

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — every error requests a recalibration.
assign recal_req = physical_error_event;

Chapter 7.6 §14 walked through this failure and it recurs here with a sharper edge, because now the recalibration itself disturbs the link. A transient causes an error; the error triggers a recalibration; the recalibration interrupts traffic and — if the search touches the live path — may cause further errors; and the loop sustains itself. Throughput collapses while every individual mechanism behaves exactly as designed.

The response is standard control practice, and it needs three independent mechanisms because there are three independent ways to oscillate:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration policy RTL — not normative UCIe behaviour.
localparam int ENTER_THRESH = 32;    // errors/window to request recalibration
localparam int EXIT_THRESH  =  4;    // errors/window to clear the condition
localparam int HOLDOFF_W    = 22;
localparam int DWELL_W      = 18;
 
logic                 degraded_q;      // hysteresis state
logic [HOLDOFF_W-1:0] holdoff_q;       // minimum gap between recalibrations
logic [DWELL_W-1:0]   dwell_q;         // minimum time operational after one
logic                 recal_req;
 
always_ff @(posedge cal_clk or negedge rst_n) begin
  if (!rst_n) begin
    degraded_q <= 1'b0;
    holdoff_q  <= '0;
    dwell_q    <= '0;
  end else begin
    // 1. Two thresholds — the condition cannot chatter around one boundary.
    if (window_end) begin
      if      (!degraded_q && (window_errors >= ENTER_THRESH)) degraded_q <= 1'b1;
      else if ( degraded_q && (window_errors <= EXIT_THRESH))  degraded_q <= 1'b0;
    end
 
    // 2. Holdoff — the ACTION is rate-limited independently of the condition.
    if      (recal_req)   holdoff_q <= '1;
    else if (|holdoff_q)  holdoff_q <= holdoff_q - 1'b1;
 
    // 3. Dwell — the link must run for a while before it may be disturbed again.
    if      (cal_commit_pulse) dwell_q <= '1;
    else if (|dwell_q)         dwell_q <= dwell_q - 1'b1;
  end
end
 
assign recal_req = degraded_q && link_operational
                && (holdoff_q == '0) && (dwell_q == '0);

Architecture. A noisy measurement drives an expensive, disruptive action on a system whose own behaviour the action perturbs. That is a control loop, and it needs damping.

State. A hysteresis flag with two thresholds, a holdoff timer, and a dwell timer.

Cycle behaviour. The degraded condition is evaluated per error window (Chapter 7.6 §13). The holdoff reloads when a request is issued; the dwell reloads when a calibration commits.

Contract. The link FSM consumes recal_req. The link_operational term prevents a recalibration request from interfering with a bring-up already in progress.

Failure — three distinct ones, which is why all three mechanisms exist. Without two thresholds, the condition chatters on every window near the boundary. Without the holdoff, a genuinely degraded link requests recalibration continuously. Without the dwell, a recalibration that does not help is immediately followed by another — and if the underlying problem is margin rather than a stale setting, no number of recalibrations will help, so the dwell is what stops an unfixable problem from consuming all the link's time.

DV. Hold the error rate exactly at the entry threshold and confirm no oscillation. Sustain a high rate and confirm requests come at the holdoff interval. Commit a calibration that does not improve the error rate and confirm the dwell prevents an immediate repeat.

15. Who Owns Calibration State

Chapter 7.1 §16's rule, applied: the Adapter receives conclusions, not evidence.

LayerMay knowMust not know
Protocolthe link is usable, or is notanything below
Adapteroperational / recalibrating / faultedphase codes, vref codes, window widths
Physicaleverything in this chapter

The PHY owns the codes because only the PHY knows what they mean — a phase code is meaningful only against a specific delay line in a specific process. An Adapter that writes a raw code has taken a dependency on the PHY's implementation, and the next PHY generation breaks it.

What the Adapter legitimately needs is an abstraction: whether the link is currently usable, whether a recalibration is in progress (if the architecture exposes that at all), and whether calibration has failed. Diagnostics and telemetry get the wide view — window widths, chosen codes, failure causes — through the observability path, which per Chapter 5.4 must never become a functional dependency.

Calibration can fail to converge: a monitor that never returns a result, a settle that never completes, a sweep that runs but never finds a passing point.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — a bounded search, per phase.
property p_measure_phase_bounded;
  @(posedge cal_clk) disable iff (!rst_n)
    ((cal_state_q == CAL_MEASURE) && cal_phase_timeout)
      |=> (cal_state_q != CAL_MEASURE);
endproperty
 
// Illustrative — a failed calibration must not read as ready.
property p_fail_blocks_ready;
  @(posedge cal_clk) disable iff (!rst_n)
    (cal_state_q == CAL_FAIL) |-> !calibration_ready;
endproperty
 
// Illustrative — a recalibration request is not lost while it is deferred.
property p_recal_request_not_lost;
  @(posedge cal_clk) disable iff (!rst_n)
    (recal_req && !recal_accepted) |=> (recal_req || recal_accepted || !degraded_q);
endproperty

The first two are safety. The third is the interesting one: it is as close to a liveness claim as is honestly provable here. It does not say a recalibration eventually happens — that would be false if the link goes down, and unprovable in general. It says a pending request cannot be dropped: on the next cycle it is either still asserted, or accepted, or the underlying condition genuinely cleared. That is the strongest honest form, and Chapter 7.1 §18 made the same distinction for training timeouts.

17. Coverage

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative calibration coverage — not UCIe-defined.
covergroup cg_calibration @(posedge cal_clk iff cal_search_end);
 
  cp_reason : coverpoint cal_reason {          // why this search ran
    bins initial   = {CAL_REASON_BRINGUP};
    bins runtime   = {CAL_REASON_RUNTIME};
    bins requested = {CAL_REASON_SOFTWARE};
  }
  cp_outcome : coverpoint cal_outcome {
    bins committed = {CAL_OUT_COMMIT};
    bins failed    = {CAL_OUT_FAIL};
  }
  cp_cause  : coverpoint cal_fail_cause_q;
  cp_window : coverpoint window_width {
    bins none   = {0};
    bins narrow = {[1:3]};
    bins mid    = {[4:CODE_MAX/2]};
    bins wide   = {[CODE_MAX/2+1 : CODE_MAX]};
  }
  cp_where : coverpoint best_position_class;   // first / middle / last of range
 
  // Did a RUNTIME recalibration ever run, and what did it find?
  x_reason_by_outcome : cross cp_reason, cp_outcome;
  // Was a narrow window ever committed, and under which reason?
  x_window_by_reason  : cross cp_window, cp_reason;
 
endgroup

Why cp_where exists. It is the coverpoint that catches §8's overwrite bug. If the chosen point is always at the end of the swept range across every regression run, that is not a coincidence about the channel — it is the last-not-best bug, and nothing else in the coverage model would reveal it.

Why the reason cross. Bring-up calibration is exercised by every test that brings a link up. Runtime recalibration is exercised only by tests that deliberately degrade a running link, and if the model does not name it, it does not happen — which means the entire §13 and §14 apparatus ships unexercised.

18. Failure Signatures

No pass windowNarrow windowWindow moves with temperatureController stuckCalibrates but data wrong
Search completesyesyesyesnoyes
Result committednoyesyesnoyes
Recorded causeNO_WINDOWnonenoneTIMEOUT / MONITORnone
Errors afterlink never comes upappear later, under stressappear as the part warmslink never comes upimmediate and deterministic
Retry / recalibrate helpsnotemporarilyyes — this is what it is fornono
First moveChapter 7.6 — margin, channel, PDN, ratemargin budget; is this expected?nothing — expected behaviourmonitor handshake, settle time, FSMChapter 8.3 — map, identity, deskew

The two end columns are the most useful. No pass window is not a calibration bug — the controller swept the range and reported honestly that nothing works, which is a margin finding. And calibrates successfully but data is wrong points backwards: calibration optimised the sampling of a link whose lane map or deskew is incorrect, so it found the best way to sample the wrong thing. Deterministic corruption immediately after a successful calibration is a Chapter 8.3 problem wearing a Chapter 8.4 timestamp.

19. Debug Checklist

  1. Did training complete, and commit atomically? Calibration on an incorrectly mapped link optimises the wrong thing (§18).
  2. Did the calibration FSM start, and which state is it in?
  3. Is the candidate code advancing? A stuck candidate means the search is not stepping.
  4. Did the settle timer complete before each measurement? Measuring transients produces plausible nonsense (§6).
  5. Is the monitor returning valid results? A monitor that never asserts valid stalls the search rather than failing it — unless it is bounded (§16).
  6. Is sample accumulation advancing? Distinguish "measuring slowly" from "not measuring".
  7. Did the best-point state update more than once? If it updates on every candidate, that is §8's bug.
  8. Was a passing window found, and how wide? Width is the margin measurement (§10).
  9. Was the commit performed, and is the active code the committed value? Compare them directly.
  10. Is the active code the window centre, or an edge? An edge means either a split window or the wrong selection rule (§9).
  11. Did the link enter its operational state afterwards?
  12. Is runtime recalibration triggering repeatedly? Check the holdoff and dwell timers, not just the trigger (§14).
  13. Does the selected code move substantially with temperature? Expected in moderation; a large movement suggests thin margin.
  14. What failure cause is sticky? Four causes, four directions (§11).

Steps 1 to 4 resolve most cases and are readable from state. Step 1 is first for a reason: it is the cheapest, and it is the one that redirects the entire investigation to a different chapter.

20. Common Misconceptions

"Training and calibration are the same thing." Training establishes which discrete resources exist and how they are arranged; calibration searches a range for the best operating point. Their failures mean different things and are fixed in different places (§1, §2).

"The first passing code is good enough." It sits at the edge of the passing region, with margin on one side only. The centre tolerates drift in both directions (§9).

"Candidate settings can drive live traffic during the search." A search deliberately visits settings that do not work. If the datapath sees them, the search corrupts the data whose errors may have requested it (§5).

"One measurement per candidate is enough." The underlying quantity is a rate, so a code near the window edge passes sometimes — and the measured window moves between runs (§7).

"Calibration should choose the largest code." It should choose the centre of the passing window. A design that always lands at the top of the range is exhibiting the last-not-best bug (§8).

"Recalibrate on every error." That is a storm: throughput collapses while every mechanism behaves as designed. Policy needs two thresholds, a holdoff, and a dwell (§14).

"If calibration passes once it never needs to run again." It found the optimum for bring-up conditions. Temperature, voltage, and ageing move — which is why UCIe 3.0 adds runtime recalibration (§13).

"Eye monitor means a full on-die eye scanner." UCIe names centring sub-states; whether an implementation realises them with a full sweep and how richly it measures at each point varies, and the control architecture is the same either way (§3).

"Calibration belongs in the Adapter." The codes are meaningful only against specific analogue hardware. An Adapter that writes them has taken a dependency that the next PHY generation breaks (§15).

"A no-pass result is an RTL bug." Usually it is the search reporting honestly that no setting has margin — a Chapter 7.6 finding, not a controller defect (§18).

"Calibration can change the active setting without affecting accepted traffic." Changing the sampling instant or threshold under in-flight data changes how that data is interpreted. A commit needs a safe point, and accepted data must survive it (§13).

21. Understanding Check

22. Summary and What Comes Next

Training decides whether the link can work; calibration decides where it should operate inside the available margin. Training's answers are discrete facts about the assembly; calibration's answer is an optimum under conditions — which is why it is the only phase with a runtime counterpart.

UCIe embeds calibration work in MBTRAIN: reference-voltage training for the valid and data lanes, transmitter and receiver clock calibration, and centring sub-states that establish the data-to-clock relationship — realised as an eye sweep in at least one published interoperability demonstration, though the specification names the sub-states rather than prescribing the search. Either way it runs inside the training sequence, so training and calibration are conceptually separable mechanisms that the bring-up flow interleaves.

The control architecture: three copies of the setting, because a search must be free to try bad values, the datapath must never see one, and the winner must survive being followed by losers. Apply, settle, then measure, because measuring an unsettled analogue circuit yields a plausible answer derived from nothing. Accumulate samples per candidate, because the quantity is a rate and one observation is a coin flip. Track the best strictly, or the last code tested wins — a bug that hides whenever the passing region reaches the end of the range. Find the window and take its centre, with width-safe arithmetic, because edges have margin on one side only. And commit only on a validated window, failing visibly otherwise, with four failure causes pointing in four different directions.

At runtime the same rules hold with less room: the candidate still must not touch live traffic, the commit still needs a safe point, and accepted data must survive — a flush is acceptable, an unannounced flush is not. Policy needs two thresholds, a holdoff, and a dwell, because there are three separate ways for the loop to oscillate.

Two things to carry forward. The window's width is a margin measurement and is often more informative than the code chosen — a fleet that calibrates successfully with two-code windows is a fleet about to have a field problem. And a no-pass result is usually not a bug: it is the search reporting honestly that the margin is not there.

Four phases are now complete, each establishing something the next depends on: reset gave known local state, discovery found a peer, training qualified and aligned the physical resources, and calibration chose a robust operating point. What has not been examined is how they behave as one sequence — what happens when a later phase fails and an earlier one must run again, which state survives each restart, how the two dies stay in step through it, and how the whole thing composes into a link that reports itself operational:

Browse the full path on the UCIe tutorials index.