Skip to content

UCIe · Module 8

UCIe Reset

Reset as a distributed protocol — asynchronous assert and synchronous deassert, per-domain synchronisers, what must and must not be reset, cross-layer state lifetime, reset-domain crossings, partial-reset hazards, observability, and why reset release is never link readiness.

Module 7 built a PHY that can move bits: levels, lanes, width, a timing reference, and the means to know whether margin remains. Every one of those was described in its operating state. The calibration codes had to be arrived at. The lane map had to be built and agreed with a peer. The active width had to be negotiated. The clocks had to become stable and be qualified.

Before any of that can start, both dies must be in a known state. And that turns out to be a much harder problem than "clear the registers", because a UCIe link is not one block with one clock. It is two dies, several clock domains per die, three protocol layers, an analogue front end, and a set of state machines that depend on each other in a specific order — and every one of them has to arrive at a valid starting condition without creating metastability, without half-initialising, and without silently losing state that something else is still counting on.

This chapter is about doing that correctly. Much of it is not UCIe-specific: reset methodology is a foundational RTL discipline, and the reasoning here transfers to any multi-clock, multi-layer design you will ever build.

1. The One-Sentence Model

Reset is not one event. It is a distributed protocol for establishing valid state across independently clocked blocks.

The word to argue with is event. An event happens at a moment; reset does not. Assertion may be near-instantaneous, but release is a sequence — it happens at different moments in different clock domains, it has prerequisites, it can fail to complete, and what it leaves behind must be consistent across boundaries that nothing in the design supervises.

Treat it as an event and you get the entire family of bugs in this chapter: state released before its clock exists, one layer restarting while another keeps counting, transactions that survive in one place and vanish in another, and post-reset failures that reproduce one boot in fifty.

2. Five Milestones That Are Not the Same

The single most common conceptual error in link bring-up is collapsing these into one:

MilestoneWhat it meansWhat it does not mean
Reset deassertedlogic in this domain may begin leaving resetthat it has, or that anything else has
Clock stablethis domain can operate at allthat its logic is initialised
PHY readyphysical resources are usablethat a peer exists or agrees
Adapter readytransport-level state is establishedthat traffic may flow
Link operationaltraffic may progress

They occur in that order, they are separated by real time, and each has its own prerequisites that can fail. A design with one ready signal covering more than one of them has thrown away the information needed to diagnose which prerequisite is missing — which, per §22, is the first question you will ask when it does not come up.

3. What UCIe Actually Requires to Leave Reset

Concrete, and worth reading closely because it validates everything above.

UCIe's PHY holds the RESET state for a predetermined minimum duration — reported in link-training material as on the order of milliseconds — explicitly to allow circuitry including PLLs to stabilise. The state is exited when a specific conjunction holds:

  • power supplies are stable;
  • a sideband clock is available and running;
  • mainband and die-to-die-adapter clocks are stable and available;
  • the mainband clock is set to the slowest supported IO data rate — cited as 2 GHz for 4 GT/s, which is Chapter 7.5's DDR relationship exactly;
  • and a link-training trigger has occurred.

Four observations, each of which the rest of the chapter develops:

Reset exit is a conjunction, not a timer. The minimum duration is a floor, not the condition. Something can satisfy the time and still not satisfy the prerequisites.

Clocks are prerequisites, not consequences. Three separate clock requirements appear — sideband, mainband, adapter. §12 explains why they cannot be one.

The sideband clock is called out separately, because it is the domain that must be alive first in order to sequence everything else. That is the resolution of Chapter 7.5 §15's circularity, in the specification's own terms.

Bring-up begins at the slowest rate. The link is brought up where timing margin is largest and only then increases rate. This is a general principle worth stealing: start where the design is easiest to make work, then tighten.

What follows reset exit is sideband initialisation — detect, repair where applicable, exchange an out-of-reset message — and then mainband initialisation. Those belong to Chapters 8.2 and 8.3; this chapter stops at the point where a die is ready to begin them.

4. Assert Asynchronously, Deassert Synchronously

The foundational pattern, and the reason for each half is different.

Assertion is asynchronous because reset must work when nothing else does. If the clock is stopped, glitching, or has never started, a synchronous reset cannot take effect — and those are exactly the conditions in which you most need a known state. Asynchronous assertion reaches every flop regardless.

Deassertion is synchronous because of what happens at the end of reset. A flop has recovery and removal timing requirements — the reset input must be stable for a setup-like window before the clock edge, and held after it. Release reset asynchronously and it lands at an arbitrary point relative to each flop's clock edge. Then:

  • a flop can go metastable on the release itself, resolving unpredictably;
  • different flops in the same domain can leave reset on different clock edges, because the reset tree has skew and each flop's timing differs;
  • an FSM can therefore start from a state that is a mixture of reset and post-reset values — a state that is not in its state diagram at all.

Asynchronous deassertion is the more dangerous half of reset, and it is the half people forget, because assertion is the one that looks like it needs care.

The pattern's name says what it does: asynchronous assert, synchronous deassert. Get the known state immediately; leave it in an orderly, single-edge, domain-wide fashion.

5. The Reset Synchroniser

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset RTL — generic methodology, not UCIe normative naming.
// Asynchronous assert, synchronous deassert, for ONE clock domain.
module reset_sync #(
  parameter int STAGES = 2
) (
  input  logic clk,        // the destination domain's clock
  input  logic arst_n,     // asynchronous, active-low, from anywhere
  output logic srst_n      // synchronous-deassert reset for THIS domain
);
 
  logic [STAGES-1:0] rst_sync_q;
 
  always_ff @(posedge clk or negedge arst_n) begin
    if (!arst_n)
      rst_sync_q <= '0;                          // async assert: immediate
    else
      rst_sync_q <= {rst_sync_q[STAGES-2:0], 1'b1};  // sync deassert: shift in
  end
 
  assign srst_n = rst_sync_q[STAGES-1];
 
endmodule

Architecture. An asynchronous input must become a domain-local reset that asserts without a clock and releases on a clock edge, with any metastability on the release absorbed before it reaches real logic.

State. STAGES flops. Two is the common minimum; more are used where the domain's clock is slow relative to the metastability settling requirement, or where the reset tree is deep. The number is an implementation decision, not a universal constant.

Cycle behaviour — the part worth walking through precisely:

CycleEventrst_sync_qsrst_n
arst_n falls00 immediately, no clock needed0
reset held, clock may or may not run000
0arst_n rises (asynchronously, any time)00 — nothing happens yet0
1first clock edge after release01 — stage 0 takes the 10
2second clock edge11 — stage 1 takes it1

The first stage is the one exposed to the asynchronous release, so it is the one that may go metastable — and it is given a full clock period to settle before stage 1 samples it. Stage 1's output is clean, and because it is a single flop feeding the domain's reset tree, every flop in the domain leaves reset on the same edge.

Contract. Every sequential element in this clock domain uses srst_n — as an asynchronous reset input, which is the usual style, or synchronously. What matters is that this domain has one release edge.

Failure. Distribute arst_n directly to the domain's flops and you get §4's failure: metastable releases, flops leaving reset on different edges, FSMs starting in states that do not exist.

DV. Simulation can check that release is single-edge and that state is at its reset value until then. It cannot prove the first stage resolves metastability — that is RDC and CDC structural analysis, the same limitation Chapter 7.1 §15 established, applied to reset.

6. One Synchroniser Per Domain — Not One Total

The most damaging reset architecture mistake:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — one synchroniser's output reused across unrelated clock domains.
reset_sync u_rs (.clk(core_clk), .arst_n(por_n), .srst_n(srst_n));
 
// ... and then srst_n is used in the phy_clk domain too:
always_ff @(posedge phy_clk or negedge srst_n) begin ... end

srst_n is synchronous to core_clk and asynchronous to everything else. Using it in phy_clk reintroduces, in full, exactly the problem the synchroniser was built to solve — with the added insult that it now looks solved.

The failure is worth being specific about because it is so easy to miss in review. srst_n rises on a core_clk edge. That edge has no defined relationship to phy_clk, so from phy_clk's perspective the release is asynchronous and can land in its setup or hold window. Flops in the phy_clk domain then leave reset on different edges, and a phy_clk FSM can start from a mixed state.

A reset synchroniser output is valid only in the domain whose clock generated it. Every clock domain needs its own instance driven from the same asynchronous source.

Note what is shared and what is not: the asynchronous assertion source is common — that is the point, it is what makes the reset global. The release is per-domain, and it happens at a different moment in each. Which raises the question §12 answers: what about the logic that spans two domains whose releases are at different moments?

A single global asynchronous reset fans out to three separate reset synchronisers, one per clock domain: protocol, adapter, and PHY. Each produces its own local synchronous-deassert reset. Above them sits a layered readiness chain in which PHY readiness enables adapter readiness, which enables protocol traffic.Global async resetasserted immediately, with no clock requiredProtocol reset syncprotocol_clk domainAdapter reset syncadapter_clk domainPHY reset syncphy_clk domainLayered readinessPHY ready enables Adapter ready enables Protocol traffic12
Figure 1 — reset release is per-domain; operational readiness is layered. One asynchronous source reaches every domain immediately, without needing any clock — that is what makes it a reliable known-state mechanism. But each clock domain releases through its own synchroniser, on its own clock, at its own moment. Above that sits a completely separate ladder: physical readiness enables transport readiness, which enables protocol traffic. Reset release is the bottom of that ladder, not the top, and the two must never be collapsed into one signal.

7. Reset Release Is Not Readiness

Chapter 7.1 §9 killed phy_ready = rst_n for the PHY. The same error at link level is worse, because more depends on it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — reset release does not mean the link is usable.
assign link_ready = rst_n;

Reset release means this domain's logic may begin operating. It says nothing about whether the peer die is powered, whether clocks are stable, whether the PHY has trained, whether the Adapter has negotiated, or whether the two ends agree about anything at all.

The failure sequence, precisely:

  • Reset lifts. link_ready asserts immediately.
  • The Protocol Layer, seeing a ready link, accepts a transaction from its client and issues it downward.
  • The Adapter accepts it — it has no independent reason to refuse — and hands it to the PHY.
  • The PHY has not trained. There is no usable channel. The transport unit is driven into nothing.
  • No layer reports an error, because every layer's local view was consistent. The client's transaction never completes, and the eventual timeout points at whatever was unlucky enough to be holding it.

The correct architecture makes readiness an explicit, layered conclusion:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — readiness is a conclusion with prerequisites at every layer.
assign phy_ready_local      = phy_local_rst_released && clock_ready && phy_trained;
assign adapter_ready_local  = adapter_local_rst_released && phy_ready_sync && params_exchanged;
assign link_traffic_enable  = adapter_ready_sync && protocol_local_rst_released;

Note the _sync suffixes. Each layer's readiness is generated in its own clock domain and consumed in another, so it is a status crossing that must be synchronised — Chapter 7.1 §15's rule appearing here as a direct consequence of §6's per-domain release.

8. What Must Be Reset, and What Must Not

A reset that clears everything is not safer than one that clears the right things — it is more expensive and often harder to close timing on. The discipline is to classify.

Must be reset — anything that makes stale data look valid:

  • FSM state, so machines start in their defined initial state.
  • Valid bits and pending flags, so nothing that was in flight appears to still be.
  • FIFO pointers and occupancy counters, so a buffer reads as empty.
  • Credit and outstanding-transaction counters, so accounting starts from its advertised value.
  • Enable and configuration masks that gate behaviour, so nothing is enabled by accident.

Need not be reset — anything whose validity is guarded by something in the first list:

  • FIFO memory arrays. If occupancy is zero, no entry can be read, so the contents are unreachable. Resetting them costs nothing useful and can cost a great deal — a wide array under reset may fail to infer as block RAM, and it adds enormous load to the reset tree.
  • Datapath pipeline registers whose accompanying valid bit is reset.
  • Wide capture and telemetry registers whose validity is separately indicated.

Invalid data need not be known. If the design cannot observe a value, its value does not matter — and reset is expensive precisely where data is widest.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — clearing the storage array as well as the control state.
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    wr_ptr_q <= '0;
    rd_ptr_q <= '0;
    count_q  <= '0;
    for (int i = 0; i < DEPTH; i++)
      mem_q[i] <= '0;          // huge reset fan-out; blocks RAM inference
  end else begin
    ...
  end
end
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset RTL — reset the control state, not the storage.
// The invariant that makes this safe: an entry can only be READ if the
// pointers and count say it exists, and those are reset.
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    wr_ptr_q <= '0;
    rd_ptr_q <= '0;
    count_q  <= '0;
  end else begin
    if (push && !pop)      count_q <= count_q + 1'b1;
    else if (pop && !push) count_q <= count_q - 1'b1;
    if (push) wr_ptr_q <= wr_ptr_q + 1'b1;
    if (pop)  rd_ptr_q <= rd_ptr_q + 1'b1;
  end
end
 
// Storage is written unconditionally on push and never reset.
always_ff @(posedge clk)
  if (push) mem_q[wr_ptr_q] <= wr_data;

Architecture. A buffer's state is its pointers and count; its contents are only meaningful relative to that state.

State. Two pointers and a count, all reset. An array, not reset.

Cycle behaviour. Pointers and count move on push and pop; the array is written on push only. After reset, count_q == 0, so pop cannot be asserted and no stale entry can be observed.

Contract. Whatever reads the FIFO must respect the empty indication. That is the invariant the entire simplification rests on, and it should be asserted (§16) rather than assumed.

Failure. If a consumer can read the array without checking emptiness — a debug port, a speculative read, a bypass path — the invariant breaks and stale data becomes visible. The exception is where this optimisation goes wrong, and it is worth checking for explicitly rather than assuming no such path exists.

9. Reset Defines State Lifetime

The subtle part, and the one that produces cross-layer bugs.

What happens to a transaction that was accepted immediately before reset? It exists in someone's state. Reset destroys some of that state and not other parts of it, and if the surviving parts and the destroyed parts belong to different layers, the layers now disagree about reality.

The canonical failure:

  • The Protocol Layer issued a transaction and is tracking it — an outstanding-transaction tag, a completion timer, a reserved buffer.
  • The Adapter accepted it, and it is sitting in a buffer.
  • A reset clears the Adapter's buffers and credit counters.
  • The Protocol Layer is not reset, or is reset from a different source, or is reset later.
  • After reset, the Protocol Layer is still waiting for a completion that no longer exists anywhere. The tag is never freed. The timer eventually fires, or — if there is no timer — the resource is leaked permanently.

A reset boundary must align with a state-ownership boundary. If a reset destroys state that another layer is still tracking, the two are inconsistent, and nothing in either layer can detect it — because each is behaving correctly according to its own view.

There is direct precedent for treating this explicitly. UCIe/LPIF material describes credit counters being reassigned to their initially advertised values whenever RDI states transition away from Active, and describes a retimer draining or dumping its receiver buffer before re-entering Active. Both are the same discipline: when a transition invalidates in-flight state, the accounting is reset on both sides together, so nobody is left tracking something that no longer exists. LPIF also defines a Stall Req/Ack handshake by which the physical layer can interrupt packet transfers — a mechanism for reaching a clean boundary before the disruption rather than in the middle of it.

Three architecturally sound answers exist, and the product must pick one:

  1. Reset aborts everything in flight, and the layer above is told so it can clean up and retry. Requires the notification to exist and to be reliable.
  2. Reset is preceded by a drain. Quiesce the interface, let outstanding work complete, then assert. Cleanest, and requires a stall or quiesce handshake to reach the boundary.
  3. Reset is scoped so that in-flight state is not split — the reset domain covers the tracking state and the buffered state together.

What is never acceptable is the fourth: state destroyed on one side of a boundary while the other side keeps tracking it, with no notification.

10. Reset-Domain Crossings

A reset is an asynchronous control signal entering a clock domain, so it has crossing hazards of its own. They are close cousins of CDC hazards and are not covered by CDC analysis, which is why they have their own name and their own tools: reset-domain crossing, or RDC.

An RDC hazard exists wherever a path leaves logic reset by one reset and enters logic reset by another — including logic reset by the same asynchronous source but released at a different moment, which §6 established is the normal case across clock domains.

Four concrete hazards:

One block out of reset drives another still in reset. The receiving flop is held at its reset value while its input changes. Harmless in itself — until the receiver releases, at which point it samples whatever is there. If the driver's output was mid-transition, the sample can be metastable; if it was a pulse, it may be missed entirely.

A combinational path spans two reset domains. Both ends momentarily hold different views of the world — one reset, one not — and the combinational function of those two views may be a state that never occurs in normal operation. If that state is latched downstream, the design has entered a condition its logic never anticipated.

A handshake is split across a reset boundary. §11 develops this; it is the most damaging of the four.

A stale level is read as a new event after reset. A signal that was legitimately high before reset is still high after the receiver releases, and the receiver — having just initialised — interprets it as a fresh assertion. It acts on an event that happened before it existed.

What RDC tools do: enumerate every path between differently-reset logic, classify it, and flag those without protection — an isolation gate, a synchroniser on the receiving side, or a documented assertion that the two resets are always coincident. Like CDC analysis, it is structural: it finds hazards regardless of whether any test happened to expose them, which matters enormously because these bugs reproduce rarely.

11. The Split Handshake

The RDC failure worth walking through in full, because it is common and it is invisible until it is not.

Two blocks exchange data with a valid/ready handshake. The producer is in reset domain A; the consumer in domain B. A reset asserts on A only — a local reset, an error recovery, a partial reset (§17).

TimeProducer (domain A)Consumer (domain B)
beforevalid high, item presented, awaiting readyready low, buffer full, sequence counter at 7
reset Adrops valid, clears its sequence counter to 0unaffected — still full, still at 7
afterexpects a fresh conversation from sequence 0expects the pending item, continues from 7

The producer now believes it is starting a new exchange; the consumer believes it is mid-exchange. Depending on the details, the outcome is a lost item, a duplicated item, an interface deadlock where each waits for the other, or — worst — data delivered under the wrong sequence number, which is corruption rather than failure.

Three architectural remedies, in increasing cost and increasing robustness:

A joint reset contract. The two blocks are always reset together. Simplest, and it means partial reset is not supported — which is a legitimate and often correct decision, provided it is stated rather than assumed.

A reinitialisation handshake. After a local reset, the interface performs an explicit re-establishment before carrying traffic, with both sides returning to a defined state.

A generation identifier. Each side tags its traffic with an epoch that increments on reset, so stale traffic is recognisable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset RTL — an epoch distinguishes before-reset from after.
// This is an architecture technique, not a UCIe-defined field.
logic [EPOCH_W-1:0] reset_epoch_q;
 
always_ff @(posedge clk or negedge global_rst_n) begin
  if (!global_rst_n)          reset_epoch_q <= '0;
  else if (local_reset_done)  reset_epoch_q <= reset_epoch_q + 1'b1;
end
 
// A response tagged with a stale epoch belongs to a conversation that ended.
assign response_is_stale = (rsp_epoch != reset_epoch_q);

Architecture. After a reset on one side, in-flight items from before the reset may still arrive. Without a way to tell them apart they are indistinguishable from valid new traffic.

State. A small counter, incremented once per local reset completion — and note it is reset by the global reset, not the local one, so it survives exactly the events it must distinguish.

Cycle behaviour. Increments once, on the cycle local reset completes. Compared combinationally against incoming traffic's tag.

Contract. Both ends must agree on the epoch's width and its wrap behaviour. A width of one bit is often enough — you only need to distinguish this conversation from the immediately previous one.

Failure. Without it, a response issued before the reset arrives after it and is matched against a freshly allocated tag that happens to have the same value. That is silent misrouting of data, and it is the reason "the transaction completed but the data was wrong" appears in post-mortems.

Do not over-apply this. If the architecture resets both sides together, an epoch is complexity with no purpose. It earns its place exactly where partial reset is genuinely supported.

12. Configuration After Reset: Four Categories

Reset invalidates state, and what has to happen next depends entirely on where that state came from. Classifying it is one of the most useful things a bring-up architect can do:

CategoryExampleAfter reset
Strap / fuse derivedpackage class, maximum width, die IDreloads automatically — the source is physical and persistent
Software programmedthresholds, policies, feature enablesmust be reprogrammed — nothing reloads it
Negotiated with the peeragreed width, agreed rate, lane mapmust be renegotiated — the peer's state may also have changed
Calibration resulttermination codes, receiver offset, deskewmust be recalibrated — the measurement is invalidated

The distinction is not academic. The most common post-reset bug in a real system is software assuming its configuration survived. A driver that programmed a threshold before reset and does not reprogram it after leaves the hardware at its default, and the symptom — a policy behaving differently than intended — points nowhere near the reset that caused it.

The corresponding hardware rule: anything that must be re-established after reset should read as invalid, not as stale. A negotiated width register that retains its old value looks correct and is not. One that resets to a defined "not yet negotiated" encoding forces the question.

13. The Reset-State Matrix

Making the policy explicit, per state class, is worth doing on paper before it is worth doing in RTL:

StateReset?Recovered fromNote
FSM current stateyesits defined initial statenon-negotiable
Valid and pending flagsyesclearedprevents stale-looks-valid
FIFO pointers and countyeszero§8
FIFO memory contentsnonot neededguarded by the count
Credit countersyesinitial advertised value§9 — must match the peer
Lane mapdependstraining / configurationmust not read as valid if stale
Calibration resultsdependsrecalibrationinvalidate if conditions changed
Negotiated capabilityyesdiscovery and negotiationmust read invalid, not stale
Sticky error statepolicyexplicit clear§18 — often deliberately preserved
Reset countersno (broader domain)never§18 — the subtle one

The three "depends" and "policy" rows are the interesting ones, and each has a defensible answer in both directions. What is not defensible is not having decided — the failure mode of an undecided policy is that different blocks implement different assumptions, and the disagreement is discovered in silicon.

14. Sequencing the Release

Reset deassertion is itself a sequence with prerequisites, so it deserves a controller.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset RTL — not a UCIe normative state machine.
typedef enum logic [2:0] {
  RST_HELD       = 3'd0,   // asynchronous reset asserted; nothing assumed
  RST_WAIT_CLOCK = 3'd1,   // reset lifted, waiting for a qualified clock
  RST_RELEASE    = 3'd2,   // local synchronous reset shifting out
  RST_PHY_INIT   = 3'd3,   // local logic running; PHY initialising
  RST_READY      = 3'd4    // this domain is initialised and usable
} rst_state_t;
 
module reset_release_ctrl (
  input  logic       clk,
  input  logic       arst_n,             // asynchronous, active low
  input  logic       local_srst_n,       // from this domain's reset_sync
  input  logic       clock_ready,        // qualified — Chapter 7.5 §11
  input  logic       phy_init_done,
  output rst_state_t rst_state_q,
  output logic       local_enable
);
 
  rst_state_t rst_state_d;
 
  always_comb begin
    rst_state_d = rst_state_q;                   // explicit default: hold
    unique case (rst_state_q)
      RST_HELD       : if (local_srst_n)   rst_state_d = RST_WAIT_CLOCK;
      RST_WAIT_CLOCK : if (clock_ready)    rst_state_d = RST_RELEASE;
      RST_RELEASE    : rst_state_d = RST_PHY_INIT;
      RST_PHY_INIT   : if (phy_init_done)  rst_state_d = RST_READY;
      RST_READY      : ;                          // leave only via async reset
      default        : rst_state_d = RST_HELD;    // illegal encoding recovers
    endcase
  end
 
  // Asynchronous assert returns this machine to RST_HELD from ANY state.
  always_ff @(posedge clk or negedge arst_n) begin
    if (!arst_n) rst_state_q <= RST_HELD;
    else         rst_state_q <= rst_state_d;
  end
 
  assign local_enable = (rst_state_q == RST_READY);
 
endmodule

Architecture. Leaving reset has ordered prerequisites — a synchronised local release, a qualified clock, PHY initialisation — and each can fail or be slow. Encoding them as states makes the ordering structural and, crucially, makes which prerequisite is outstanding visible to a debugger.

State. A five-state register. Note it is clocked by clk with asynchronous reset to RST_HELD, so assertion returns it to a known state from anywhere regardless of clocking — the property §4 argued for, applied to the controller itself.

Cycle behaviour. One transition per clock. RST_HELD → RST_WAIT_CLOCK happens when the domain's own reset synchroniser has released, which is why the controller and the synchroniser are separate blocks: the synchroniser makes the domain safe to run, and only then can a state machine sequence anything.

Contract. local_enable gates this domain's functional logic. Chapter 7.1's PHY FSM, the Adapter's initialisation, and the Protocol Layer's traffic enable all sit downstream.

Failure. Without RST_WAIT_CLOCK, the machine advances on a clock that has not been qualified — Chapter 7.5 §11's chattering-PLL bug, arriving through a different door. Without the default branch, an illegal encoding from a single-event upset has no recovery path.

DV. Every transition taken; each prerequisite individually withheld to confirm the machine waits in the right state; asynchronous reset asserted from every state to confirm return to RST_HELD.

An illustrative reset-release controller. From RESET HELD the machine advances to WAIT CLOCK when the local synchronous reset releases, to RELEASE when the clock is qualified, to PHY INIT as the local release takes effect, and to READY when PHY initialisation completes.RESETHELDWAITCLOCKRELEASEPHY INITREADYlocal reset outlocal reset outclock qualifiedclock qualifiedlogic runninglogic runningPHY init donePHY init done
Figure 2 — the illustrative reset-release controller. The chain is what matters: a domain-local synchronous release, then a qualified clock, then the local release taking effect, then PHY initialisation, and only then a usable domain. Note what is deliberately not drawn — an arrow from every state back to RESET HELD. Asynchronous assertion applies from all five states at once, which is precisely the property that makes it trustworthy, and drawing it as one edge from one state would misrepresent it.

15. No Clock, No Release — and That Is Correct

A corner case that looks like a bug and is a feature.

If a domain's clock is stopped, its reset synchroniser cannot shift. srst_n stays low. The domain stays in reset indefinitely, no matter how long ago the asynchronous input was released.

That is exactly right:

  • A domain with no clock cannot operate anyway, so being held in reset costs nothing.
  • If reset were released while the clock was stopped, then whenever the clock returned, flops would leave reset at unpredictable moments relative to one another — precisely §4's failure.
  • Holding reset until the clock arrives means the release, when it comes, is clean and domain-wide.

The temptation to avoid is bypassing the synchroniser "so reset can release without a clock". That trades a benign, diagnosable stall for metastability across an entire domain. Do not do it.

And the diagnostic value: if a domain is stuck in reset, the question is not "why is reset not releasing?" but "why is this domain's clock not running?" — which is a much more productive question, and it is why §22's checklist puts the clock check second.

This is Chapter 7.5 §15's circularity resolved: something must be clocked first. UCIe's own RESET-exit conditions name the sideband clock available and running as a prerequisite, which is the specification identifying the domain that bootstraps the rest.

16. Assertions

Reset assertions are cheap and catch structural errors that are otherwise found in silicon.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — nothing reports ready while reset is asserted.
property p_no_ready_during_reset;
  @(posedge clk) !local_srst_n |-> !local_enable;
endproperty

Note the deliberate absence of disable iff (!rst_n) — this property is about reset, so disabling it during reset would disable it exactly when it matters. Getting the disable iff right is most of the skill in writing reset assertions, and the default habit of adding it everywhere is wrong here.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative — no interface activity while this domain is in reset.
property p_no_transfer_while_reset;
  @(posedge clk) !local_srst_n |-> (!tx_valid && !rx_valid);
endproperty
 
// Illustrative — the FSM is in its initial state throughout reset.
property p_fsm_in_reset_state;
  @(posedge clk) !local_srst_n |-> (rst_state_q == RST_HELD);
endproperty
 
// Illustrative — the buffer really is empty on the first cycle after release.
// $rose on the reset signal gives the exact cycle release takes effect.
property p_fifo_empty_after_reset;
  @(posedge clk) $rose(local_srst_n) |-> (count_q == '0) && !rx_valid;
endproperty
 
// Illustrative — functional logic uses only the SYNCHRONISED reset.
// Structural, and best checked by RDC tooling; the assertion documents intent.
property p_uses_synchronised_reset;
  @(posedge clk) $rose(local_srst_n) |-> $stable(local_srst_n)[*1];
endproperty

What these catch. p_no_ready_during_reset catches the §7 bug directly. p_no_transfer_while_reset catches a block that begins driving an interface before its own reset released — an RDC hazard's symptom. p_fifo_empty_after_reset catches an incorrectly-scoped reset that missed the count. And p_fsm_in_reset_state is trivial to write and catches an FSM whose reset value was changed by someone refactoring the enum.

What they cannot catch, and it must be said clearly: assertions cannot prove metastability safety on reset release. They can prove that logic consumes the synchronised reset rather than the raw one, that state stays reset until release, and that release is single-edge in simulation. Whether the synchroniser's first stage actually resolves in time is an analogue question for RDC and CDC structural analysis plus library timing — exactly the boundary Chapter 7.1 §15 drew for CDC, applied to reset.

17. Partial Reset Is Not Local

Resetting one layer while others keep running is sometimes necessary — error recovery, a stuck PHY, a software-requested reinitialisation. It is also where the worst reset bugs live.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG — a local reset silently clears state a neighbour is still tracking.
always_ff @(posedge adapter_clk or negedge adapter_rst_n) begin
  if (!adapter_rst_n) begin
    credits_q       <= INITIAL_CREDITS;   // Protocol still thinks it spent them
    retry_buffer_vq <= '0;                // discards items already accepted
  end
  ...
end

Two distinct failures in three lines.

Credit accounting diverges. The Adapter restores its credit counter to the advertised initial value. The Protocol Layer, unreset, still believes it has consumed some. Now the two disagree about how much buffering exists — and the disagreement is silent, because neither can see the other's counter. If the Protocol Layer is the more conservative one, throughput quietly drops; if the Adapter is, it accepts more than it can hold and overflows. UCIe/LPIF's rule that credits are reassigned to their initially advertised value on transitions away from Active exists precisely to make this a defined, symmetric event rather than an accident.

Accepted items are discarded. The retry buffer held transport units the Protocol Layer had already been told were accepted. Clearing it destroys them with no notification — Chapter 7.1 §12's accepted-data rule, violated by a reset instead of by a fault.

A local reset is local in its wiring, never in its effects. Any reset that clears state a neighbour tracks needs an explicit recovery contract: notify, or renegotiate, or reset together.

Whether UCIe supports resetting one layer independently, and with what semantics, is a specification question to answer for your revision — not something to infer. What is universal is the design rule: before scoping a reset, enumerate every piece of state it clears and ask who else has an opinion about that state.

18. Reset Observability

Reset is a destructive operation, so if you learn nothing from it, you have lost the evidence.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset RTL — not UCIe normative registers.
typedef enum logic [2:0] {
  RESET_CAUSE_NONE       = 3'd0,
  RESET_CAUSE_POR        = 3'd1,   // power-on
  RESET_CAUSE_SW         = 3'd2,   // software requested
  RESET_CAUSE_LINK_ERROR = 3'd3,   // triggered by a link fault
  RESET_CAUSE_TIMEOUT    = 3'd4    // a watchdog fired
} reset_cause_t;
 
// These observability registers live in a BROADER reset domain than the block
// they observe: cleared only by power-on, so they survive the resets they count.
reset_cause_t          reset_cause_q;
rst_state_t            pre_reset_state_q;
logic [RST_CNT_W-1:0]  reset_count_q;      // saturating
 
always_ff @(posedge always_on_clk or negedge por_n) begin
  if (!por_n) begin
    reset_cause_q     <= RESET_CAUSE_NONE;
    pre_reset_state_q <= RST_HELD;
    reset_count_q     <= '0;
  end else if (local_reset_trigger) begin
    reset_cause_q     <= reset_cause_in;      // why
    pre_reset_state_q <= rst_state_q;         // what we were doing
    if (!(&reset_count_q))
      reset_count_q <= reset_count_q + 1'b1;  // how often — saturating
  end
end

Architecture. After an unexplained reset, three questions matter: why, what was happening, and how often. Hardware must capture them at the moment of reset, because afterwards the evidence is gone by construction.

State. A cause enum, a snapshot of the pre-reset state, and a saturating counter.

Cycle behaviour. All three are captured on the cycle the reset is triggered — before the reset takes effect on the observed block.

Contract. Firmware reads them after bring-up completes. Nothing functional depends on them.

Failure — and this is the subtle, excellent one. If these registers are reset by the same reset they observe, they clear on every occurrence and can never record history. A link that is flapping — resetting repeatedly — reads as a link that has reset exactly once, every time you look. The counter that would have revealed the flap is destroyed by the flap.

Observability state must live in a broader reset domain than the state it observes. A counter reset by the event it counts is a counter that counts to one.

The same reasoning applies to sticky error state (Chapter 7.6 §11): if a fault indication is cleared by the recovery it triggers, the recovery erases the evidence for itself. These decisions are policy, and the policy should be written down.

19. Verifying Reset

Stress the scenarios, not just the nominal path:

  • Reset at idle — the easy case, and the one most regressions stop at.
  • Reset with traffic in flight — items accepted and not yet delivered, which is where §9's ownership question becomes concrete.
  • Reset during PHY training — a partially established link, with configuration half-applied.
  • Reset while the clock is unstable — confirming §15's behaviour is the intended stall and not a hang.
  • Reset of one domain before another — the RDC exposure of §10, driven deliberately.
  • Rapid repeated resets — a reset asserted before the previous release completed. Every state machine must handle it, and the observability counter must record every one.
  • Reset during error recovery — two disruptive mechanisms interacting, which is where the least-travelled code lives.

Only generate what the architecture permits: a reset combination the hardware cannot produce is coverage spent on a state that will never occur.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Illustrative reset coverage — not UCIe-defined.
covergroup cg_reset @(posedge always_on_clk iff local_reset_trigger);
 
  cp_cause      : coverpoint reset_cause_q;
  cp_prev_state : coverpoint pre_reset_state_q;
  cp_fifo       : coverpoint (count_q != '0);        // was anything in flight?
  cp_link_up    : coverpoint link_operational;       // was the link carrying?
  cp_clock      : coverpoint clock_ready;            // was the clock qualified?
 
  // The valuable one: which causes have been seen from which states?
  x_cause_by_state : cross cp_cause, cp_prev_state;
 
  // And: has a reset ever been taken with work genuinely in flight?
  x_cause_by_inflight : cross cp_cause, cp_fifo;
 
endgroup

Why the crosses. Resetting from the idle state is easy and every regression does it. Resetting from RST_PHY_INIT because a timeout fired, with a FIFO non-empty and the link operational, is where §9's state-lifetime question is actually exercised — and it is the point a regression reaches only if the coverage model names it.

20. Debugging: Stuck After Reset

Work down the prerequisite chain. Each step is answerable from state you can read, and each eliminates everything below it.

  1. Did the asynchronous reset actually deassert? Observe it at the block, not at its source. Reset trees have their own faults.
  2. Is the destination clock running? If not, §15 says reset correctly will not release, and the investigation is now about the clock.
  3. Did the local reset synchroniser release? Read srst_n in the affected domain. If it is low with a running clock, the synchroniser's input or wiring is at fault.
  4. Is the FSM still in its reset state? If yes, some prerequisite is unmet, and the state itself names which one.
  5. Which prerequisite is blocking the next transition? Read the transition condition's inputs individually — this is why §14 makes each a named signal rather than a compound expression.
  6. Are FIFO counts and valid bits actually cleared? A missed reset on control state produces a buffer that looks non-empty forever.
  7. Is negotiated or configured state correctly invalidated? Stale state that reads as valid causes the design to skip a step it needed to redo (§12).
  8. Is another layer still in reset? Readiness is layered, so one domain waiting on another can look like the wrong domain being stuck.
  9. Are the status crossings between domains synchronised and progressing? A readiness bit that never crosses looks identical to a readiness bit that never asserted.
  10. Is software expecting configuration to have survived? The most common non-hardware cause, and the last one anyone checks (§12).

21. Debugging: Intermittent Post-Reset Failures

A different problem with a different suspect list. If a design comes up correctly most of the time, the reset is racing something:

  • Asynchronous deassertion somewhere. A domain whose reset does not go through a synchroniser will work whenever the release happens to miss the critical window — which is most of the time (§4, §5).
  • A shared synchroniser across domains. §6's bug has exactly this signature: fine when the two clocks' phases happen to be favourable, broken when they drift.
  • A reset-domain crossing without protection. §10 and §11 — and note that these produce failures that correlate with nothing environmental, so temperature and voltage sweeps report nothing.
  • Partial reset with state left behind. §17. Symptom: works on a cold boot, fails after a recovery event, because a cold boot resets everything and a recovery does not.
  • Configuration not reloaded. §12. Symptom: works on the first bring-up after software configures it, fails on any subsequent reset that software does not follow with reconfiguration.
  • A clock-readiness race. The FSM advanced on an unqualified clock (Chapter 7.5 §11), so behaviour depends on where the PLL happened to be.

The signature that points at reset rather than at anything else: it depends on the boot, not on the workload. A failure that varies from power-up to power-up while doing identical work afterwards is a reset or initialisation race, not a functional bug.

22. Common Misconceptions

"Reset is one wire." Assertion may be one source; release happens per domain, at different moments, with prerequisites, and can fail to complete (§1, §6).

"Asynchronous deassertion is safe because assertion is asynchronous." They are opposite cases. Assertion needs to work without a clock; deassertion needs to be aligned to one, or flops leave reset at different edges and metastably (§4).

"One reset synchroniser can serve every clock domain." Its output is synchronous only to the clock that generated it. Everywhere else it is an unsynchronised asynchronous release (§6).

"Reset deasserted means the link is ready." Reset release is the bottom of a five-milestone ladder that ends with link operational (§2, §7).

"Every register must be reset." Only state whose staleness could be mistaken for validity. Everything else costs reset-tree load and inference problems for nothing (§8).

"FIFO memory contents must be cleared." If the count is reset, no entry can be read. Invalid data need not be known — provided no bypass path can read it (§8).

"Partial reset only affects the block being reset." It affects everything that tracks state the reset clears — credits, outstanding tags, buffered items (§17).

"Reset can release while the destination clock is stopped." Synchronous deassertion needs edges. That it cannot release is correct behaviour, and bypassing it trades a benign stall for domain-wide metastability (§15).

"SVA proves reset metastability safety." It proves logic consumes the synchronised reset and that state stays reset until release. Metastability resolution is RDC and CDC structural analysis (§16).

"Random reset testing proves reset correctness." Without epoch boundaries, cancellation expectations, and post-reset state comparison, it is a liveness test wearing a correctness test's clothes (§19).

"Reset history counters should clear on every reset." Then they count to one, and a flapping link is indistinguishable from a link that reset once. Observability belongs in a broader reset domain (§18).

"Configuration survives reset." Straps and fuses reload; software-programmed, negotiated, and calibrated state do not (§12).

23. Understanding Check

24. Summary and What Comes Next

Reset is a distributed protocol, not an event. Assertion is asynchronous so a known state is reached without needing a clock; deassertion is synchronous so an entire domain leaves reset on one edge, because asynchronous release causes metastability and flops leaving reset at different edges — which starts FSMs in states that do not exist. That means one reset synchroniser per clock domain, sharing only the asynchronous source.

Reset release is not readiness. Five milestones — reset deasserted, clock stable, PHY ready, Adapter ready, link operational — occur in order with their own prerequisites, and collapsing them discards the information you need to debug. UCIe's own RESET-exit conditions bear this out: stable supplies, a running sideband clock, stable mainband and adapter clocks, the mainband clock at the slowest rate, and a training trigger — a conjunction, not a timer.

Classify before resetting. Reset anything whose staleness could look valid: FSM state, valid bits, pointers, counts, credits, enable masks. Leave alone what is guarded by those — FIFO memory need not be cleared if the count is. And classify configuration by origin: straps reload, software-programmed state must be reprogrammed, negotiated state must be renegotiated, calibration must be redone — and anything in the last three should read invalid rather than stale.

Reset defines state lifetime, so a reset boundary must align with a state-ownership boundary. A local reset is local in its wiring, never in its effects — credits, outstanding tags, and accepted items all belong to someone else too. Reset-domain crossings are a distinct hazard class from CDC with their own tooling, and the split handshake is their most damaging form.

Finally, learn from every reset: capture cause, pre-reset state, and a saturating count — in a broader reset domain, because a counter cleared by the event it counts counts to one. And verify reset properly: random injection without epoch boundaries, cancellation expectations, and post-reset state comparison is a liveness test, not a correctness test.

Reset establishes known local state. It does not establish a peer — nothing so far has proved another die is even there. Once both ends have safely left reset, the next task is to find out:

  • 8.2 — Link Discovery — the sideband channel and the handshake by which two dies detect each other, exchange out-of-reset messages, and establish a link partner.

Browse the full path on the UCIe tutorials index.