UCIe · Module 16
UCIe and CXL
Which responsibilities belong to CXL and which to UCIe — why native mapping and streaming are different relationships, the three subprotocols' three different boundary contracts, why a UCIe send is not a CXL completion, the destination decided once and never recomputed, CXL.mem traffic that starves CXL.cache into deadlock, what a UCIe recovery may and may not touch, why a degraded link is slower and not different, and a layer-attribution method for deciding which protocol to blame.
Chapter 16.4 put an existing coherent fabric at a UCIe boundary and found that UCIe carries it as a Streaming Protocol. CXL's relationship is different — official UCIe material maps it natively — and that difference reorganises the whole responsibility ledger.
1. The One-Sentence Model
CXL defines what a transaction means. UCIe determines how that traffic crosses the die boundary. Native mapping changes who provides the transport machinery — it does not move a single semantic responsibility across the line.
2. What This Chapter Owns — and What Module 11 Already Owns
Module 11 is five chapters deep on CXL over UCIe. This chapter does not rewrite any of them.
| Question | Where it is answered | Not repeated here |
|---|---|---|
| Why CXL exists; device types; the coherence contract | 11.1 — Why CXL Matters | ✓ |
| HDM/PDM, memory windows, interleaving, routing | 11.2 — Memory Expansion Over CXL | ✓ |
| Line metadata, transient states, probes, same-line conflict | 11.3 — Cache Coherency Over CXL | ✓ |
| Flit format, ARB/MUX, stacked retry, the delivery fence | 11.4 — CXL Transport on UCIe | ✓ |
| The three state planes, cross-plane validity, four tracking entries | 11.5 — CXL-over-UCIe Integration | ✓ |
What is new here, and could not have been written before 16.4:
The comparison. CXL is natively mapped; CHI in UCIe 1.0 was a Streaming Protocol in Raw Mode (§3, §4). Module 11 never had a second relationship to compare against, so it could not draw this distinction — and the distinction is what determines who provides the transport machinery for a coherent protocol over UCIe.
The subprotocol asymmetry. Module 11 treats CXL.io, CXL.cache and CXL.mem as three classes in a multiplexer. This chapter treats them as three different boundary contracts with different obligations, different failure modes and different starvation consequences (§7).
Degradation invariance. Modules 14 and 15 taught link degradation; Module 11 predates it in the curriculum. Composing the two — what a lane-width change may and may not do to a CXL semantic — is new (§28, §29).
And an attribution method. §30 is a procedure for deciding which of the two protocols to blame, which is the practical payoff of a responsibility ledger and is the thing engineers actually need at 2 a.m.
3. Sourcing
4. Native Mapping and Streaming Are Different Relationships
The axis 16.4 made visible.
| CXL over UCIe | CHI over UCIe | |
|---|---|---|
| How UCIe material describes it | "maps ... natively" (§3) | a Streaming Protocol (§3) |
| UCIe 1.0 | natively mapped | Raw Mode only |
| UCIe 1.1 change | — | may use the D2D Adapter over FDI |
| Where the protocol's own link layer sits | CXL has one, with its own CRC and retry (11.4 §12) | the fabric brings whatever it has |
| The design question that follows | which retry is in force — 11.4 §13's stacked-retry problem | whether any UCIe retry is in force at all (16.4 §6) |
Two readings, and they are opposite problems.
CXL's risk is redundancy. CXL has its own link-layer retry; UCIe's Adapter optionally has one. The failure mode is two reliability mechanisms stacked on one path, each retrying the other's retries — which is 11.4 §13, and it produces latency amplification and confusing failure attribution rather than data loss.
CHI's risk is absence. In Raw Mode there is no Adapter CRC or retry to reuse (inferred — 16.4 §3), so if the fabric does not bring its own, the link is unprotected.
Native mapping is a statement about ecosystem and machinery, not about semantics. It does not move ownership, coherence state, ordering or completion into UCIe. Every semantic responsibility stays exactly where it was — which is §6, and is the sentence this whole chapter defends.
5. The Layer Picture
The middle layer is the chapter. Above it, meaning. Below it, transport. It is the only place where a semantic lifetime and a transport lifetime are both visible, which is why it is where the mistakes are.
6. The Responsibility Ledger
| Responsibility | CXL | UCIe | Qualification |
|---|---|---|---|
| Memory semantic operation | owns | transports | "memory use cases are handled through CXL.Mem" (§3) |
| Cache ownership semantics | owns | transports | caching addressed through the caching protocol (§3) |
| Discovery and control semantics | owns | transports | addressed with PCIe/CXL.io (§3) |
| Address decode and destination | owns | must not change it in flight (§13) | mechanism in 11.2 |
| Semantic completion | owns | must not be substituted by transport completion (§11) | the retirement point is CXL's |
| Ordering obligations | owns | must be preserved by the mapping | no CXL ordering rule stated here |
| Flit framing and per-class credits | CXL's own link layer | Adapter framing where applicable | 11.4 |
| Link-layer retry | CXL has one | Adapter optionally has one | which is in force is the open question (11.4 §2) |
| Link state, parameter negotiation | — | UCIe | Adapter role (§3) |
| Lane training, repair, rate, width | — | UCIe | Modules 7, 8, 14 |
Three rows carry the chapter.
Row 5 — completion. UCIe signals transmitted and later delivered. CXL defines complete. §11 is the bug where the first is mistaken for the third, and it is the single most common CXL-integration defect.
Row 4 — destination. The address decode is CXL's, performed once. Anything that recomputes it later — after a retry, after a recovery, after a configuration change — has taken a CXL responsibility and executed it at the wrong time (§14).
Row 8 — retry. It is genuinely ambiguous, it is stated as ambiguous, and an implementer must resolve it from the revision they build rather than assume it. A wrong answer designs in either an unprotected link or two stacked mechanisms.
7. Three Subprotocols, Three Different Boundary Contracts
The section that most distinguishes this chapter from Module 11. Module 11 treats the three as classes in a multiplexer. At the responsibility boundary they are not interchangeable, and the differences drive different bugs.
| CXL.io | CXL.cache | CXL.mem | |
|---|---|---|---|
| What it carries | discovery, configuration, control | ownership and permission changes | memory reads and writes |
| Semantic obligation created | a control operation must complete | an ownership transition must resolve | a memory access must return or land |
| Cost of delaying it | usually bounded — control is bursty | can deadlock the system (§23) | throughput loss |
| Cost of duplicating it | depends on the operation's idempotence | a second ownership action — corruption | a second write — corruption |
| Destination stability matters? | at bring-up | per line | critically — §14 |
| Typical bandwidth share | small | small | dominant |
| Boundary risk | mistaken for readiness (§19) | starved by CXL.mem (§23) | starves the others (§23) |
Read the last three rows together, because they compose into one system failure.
CXL.mem is dominant in bandwidth and CXL.cache is critical to progress. A shared resource will be filled by the former and needed by the latter. §23 is that collision, and it deadlocks.
And the duplication row is why the three cannot share one duplicate-suppression policy either. Some control operations are naturally idempotent; an ownership transition is not, and neither is a write. A boundary that suppresses duplicates for one class and not another has to justify the asymmetry class by class.
8. What Native Mapping Does and Does Not Buy
| Native mapping does | Native mapping does not |
|---|---|
| let CXL reuse an existing ecosystem across a die boundary | move any semantic responsibility into UCIe |
| make the Adapter's arbitration available across protocol classes | make the Adapter aware of what a class means |
| put UCIe's link-state and negotiation machinery underneath CXL | give UCIe an opinion about coherence, ordering or completion |
| make the reliability question answerable per revision (§6) | answer it for you |
The tempting inference — "it's native, so UCIe handles it" — is exactly backwards. Native mapping means UCIe carries CXL well. It has never meant UCIe carries CXL's obligations.
9. Four Lifetimes, and the Use This Chapter Makes of Them
11.5 §12 established that one transaction is simultaneously represented in four structures — the semantic entry, the mapping queue entry, the replay entry and the PHY transmit state — and that their occupancies do not have to relate. That is not restated here.
What this chapter adds is the attribution use. Each lifetime, when violated, produces a characteristic failure, and the mapping from failure to lifetime is the debugging method in §30:
| Lifetime released too early | Characteristic symptom |
|---|---|
| semantic entry | a returning response matches nothing — a hang, far from the cause |
| mapping entry | an accepted object vanishes without ever being transmitted |
| replay entry | a recoverable transport error becomes unrecoverable |
| PHY state | truncated or interleaved transmission |
And released too late:
| Lifetime held too long | Characteristic symptom |
|---|---|
| semantic entry | outstanding-count exhaustion; the fabric throttles for no visible reason |
| mapping entry | table full; backpressure that looks like link congestion but is not |
| replay entry | replay-space exhaustion (14.3 §28) |
Two symptom families, four structures, and each cell is distinguishable from the others — which is what makes the taxonomy in §31 actionable rather than decorative.
10. The Integration Mapping Entry
// ILLUSTRATIVE integration state. NOT a CXL structure and NOT a UCIe structure,
// and no field corresponds to any specified field of either (Section 3).
typedef struct packed {
logic valid;
logic [SEM_ID_W-1:0] cxl_semantic_id; // CXL's identity for the operation
logic [CLASS_W-1:0] cxl_class; // io / cache / mem — Section 7
logic [TR_ID_W-1:0] ucie_transport_id; // UCIe's identity — recycled by UCIe
logic [DEST_W-1:0] destination; // captured ONCE, at acceptance
logic [EPOCH_W-1:0] config_epoch; // which configuration decided that
} cxl_ucie_map_t;
cxl_ucie_map_t map_q [MAP_DEPTH];Architecture. A binding between two namespaces, plus the two facts the boundary must remember about when the decision was made: the destination and the configuration epoch that produced it.
State. MAP_DEPTH entries — the maximum number of accepted-but-unresolved objects. The boundary must backpressure at full, never overwrite (§16's fourth property), because an overwrite silently discards a CXL obligation.
Cycle behaviour. Allocated at acceptance, freed when the operation's transport representation resolves. Not freed when the transport retires — those are different events and §11 is the bug that merges them.
Contract. ucie_transport_id belongs to UCIe and is recycled on UCIe's schedule; cxl_semantic_id belongs to CXL. They must never be treated as one identity, for the reasons 16.4 §12 sets out in detail.
Why config_epoch is not optional. It records which address-decode configuration produced this destination. Without it, a request outstanding across a configuration change is indistinguishable from one issued after it — which is §14, and which has no local symptom at all.
Failure. Omitting destination and recomputing it downstream (§14). Omitting config_epoch and assuming configuration is static (§14 again, one layer deeper).
DV. Assert destination immutability (§16) and that a stale-epoch response cannot resolve a re-driven entry.
11. Wrong RTL — a UCIe Send Treated as a CXL Completion
// WRONG — the CXL semantic obligation is retired when UCIe accepts the object.
always_ff @(posedge clk)
if (ucie_tx_fire)
cxl_txn_valid_q[id] <= 1'b0; // ← transport launch, not semantic completionTransport launch is not delivery. Delivery is not action. Action is not completion. This code collapses four distinct events into the earliest and most local one.
What happens, step by step:
1. The CXL semantic entry is freed the cycle UCIe accepts the object.
2. The object crosses. The remote side acts. A response is generated.
3. The response returns — correctly, intact, at the right time.
4. The boundary looks for the semantic entry: gone.
5. → the response is orphaned, and whatever CXL was waiting on never completes.Three properties.
Every layer below is blameless. UCIe transmitted, retried where needed, retired its object and delivered a response. The transport scoreboard is clean end to end.
And every layer above is blameless. CXL issued a legal operation and is waiting for a legal completion.
The bug is entirely in the one line that chose the wrong event. ucie_tx_fire is available in this module this cycle; the CXL completion is a round trip and several layers away. That asymmetry is why this bug is written, and it is the sixth time this curriculum has seen a semantic obligation released by a transport signal — 14.3 §15, 15.2 §15, 11.5 §14, 16.3 §13, 16.4 §26, and now this.
The correct form separates the two explicitly:
// ILLUSTRATIVE. Two events, two effects, and they are never the same signal.
always_ff @(posedge clk) begin
if (ucie_tx_fire)
map_q[id].transport_launched <= 1'b1; // transport progress only
if (cxl_semantic_completion_observed[id]) // the CXL-defined event
cxl_txn_valid_q[id] <= 1'b0; // and ONLY this frees it
end12. SVA — Semantic State Survives Transport Ownership
// MANDATORY. The property that makes Section 11 impossible.
property p_semantic_not_retired_on_transport_event;
@(posedge clk) disable iff (!rst_n)
(ucie_tx_fire && !cxl_semantic_completion_observed[ID])
|=> $stable(cxl_txn_valid_q[ID]);
endproperty
a_semantic_not_retired_on_transport_event:
assert property (p_semantic_not_retired_on_transport_event);
// Nor on transport RETIREMENT, which is a different and later transport event.
property p_semantic_not_retired_on_transport_retire;
@(posedge clk) disable iff (!rst_n)
(ucie_object_retired && !cxl_semantic_completion_observed[ID])
|=> $stable(cxl_txn_valid_q[ID]);
endproperty
a_semantic_not_retired_on_transport_retire:
assert property (p_semantic_not_retired_on_transport_retire);
// A returning response must find a live semantic entry.
property p_response_finds_live_semantic;
@(posedge clk) disable iff (!rst_n)
cxl_response_delivered |-> cxl_txn_valid_q[resp_id];
endproperty
a_response_finds_live_semantic: assert property (p_response_finds_live_semantic);Architecture. Three properties covering the two transport events that tempt an early retire, plus the consequence check.
Why the second property is not redundant with the first. A design that correctly avoids retiring on tx_fire very often retires on ucie_object_retired instead — which is a later, more plausible-looking transport event and is still the wrong one. The semantic obligation outlives both.
Why the third catches what the first two might miss. They are written per-ID and depend on choosing the right ID under test. The third is universal and fires the moment any response arrives for a dead entry — which is the observable form of the bug.
DV. Force an early retire in an error-injection build and confirm all three fire; then confirm they are silent in the clean build across the full regression.
13. CXL.mem at the Boundary
Not a retelling of 11.2. HDM windows, interleaving, the readiness vector and the memory request object are taught there. What the responsibility boundary adds is four obligations:
Address decode happens above the boundary, once. The boundary receives a destination; it does not compute one. A boundary that decodes is doing CXL's job at the wrong layer and at the wrong time (§14).
The destination must not change while the operation is in flight. Whatever the transport does — retry, recovery, re-arbitration — the recorded destination is the one the operation was accepted with (§16).
A transport recovery must not invalidate an accepted memory obligation. The device still owes a response, or the write still has to land. 11.2 §18 established that outstanding state survives UCIe recovery; the responsibility statement is that survival is CXL's requirement, not the transport's courtesy.
And a response must map back to its semantic request, exactly once. 11.2 §19 has the mechanism. The boundary obligation is that the identity used for that mapping is CXL's, never UCIe's (§10).
14. Wrong Integration — the Destination Recomputed After a Recovery
The strongest cross-module bug in the chapter, because it composes a Module 11 mechanism with a Module 14 event.
// WRONG — the destination is derived at TRANSMIT time from live configuration.
assign tx_destination = decode_hdm(map_q[id].address, hdm_config_q);
// ^^^^^^^^^^^^
// live configuration, read at an arbitrary later cycleThe sequence:
1. A CXL.mem write to address A is accepted. Configuration epoch 7 decodes
A to device X. The operation is now an obligation against device X.
2. UCIe enters recovery. The object has not yet been transmitted, or is replayed.
3. During or after recovery, host software reconfigures the memory map.
Configuration epoch 8 decodes A to device Y.
4. The object is transmitted. `decode_hdm` is evaluated NOW — epoch 8.
5. → the write lands in device Y.Four properties, and they make this the worst kind of bug.
There is no local symptom whatsoever. UCIe transported perfectly. The write was well-formed, accepted and acknowledged. Device Y stored exactly the bytes it was sent. Every layer reports success.
It is silent data corruption at two addresses. Device X's copy of A is stale; device Y has data that does not belong to it at that offset. Neither device can detect this, because neither has any way to know what the other holds.
The window is narrow and load-dependent, which means it will not appear in directed tests and may appear in the field.
And the detector cannot be local. It requires a model that predicts the destination independently, from the configuration in force at acceptance — 11.5 §20's point, and §32's Layer 1.
The correct form captures the decision, not the inputs to it:
// ILLUSTRATIVE. The destination is a DECISION, made once and recorded, along
// with the configuration epoch that produced it.
always_ff @(posedge clk)
if (cxl_accept_fire) begin
map_q[alloc_id].destination <= decode_hdm(accept_address, hdm_config_q);
map_q[alloc_id].config_epoch <= hdm_config_epoch_q;
end
// And transmission uses the recorded decision — never a fresh decode.
assign tx_destination = map_q[id].destination;15. Wrong Integration — a Replay Producing a Second Semantic Action
The CXL.cache version, and it is 11.3 §25's hazard stated as a responsibility.
// WRONG — the semantic layer is driven by physical arrival.
always_ff @(posedge clk)
if (ucie_object_arrived)
start_ownership_transition(arrived_line, arrived_requester);A retransmitted object arrives again, and this allocates a second ownership transition for one line. Both may probe; a single response may satisfy both; both may commit.
| Duplicated | Consequence |
|---|---|
| an ownership request | two transitions for one line; both may grant (11.3 §16) |
| a probe | the target may respond twice |
| a response | pending state cleared twice — a premature commit (16.2 §21) |
All three are coherence corruptions with a transport cause, and the transport did nothing wrong. The fix is the same construction as 11.4 §16's semantic delivery fence and 16.3 §11's gate: the semantic layer is reached by delivery, never by arrival. The invariant it enforces:
semantic allocations per CXL operation == 1
transport attempts per object >= 1
SEMANTIC DELIVERIES per object <= 116. SVA — Destination and Semantic Identity Are Immutable
// MANDATORY. The destination is decided once (Section 14).
property p_destination_immutable;
@(posedge clk) disable iff (!rst_n)
map_q[IDX].valid |-> $stable(map_q[IDX].destination);
endproperty
a_destination_immutable: assert property (p_destination_immutable);
// It survives a transport retry unchanged.
property p_destination_stable_across_retry;
@(posedge clk) disable iff (!rst_n)
(transport_retry_fire && map_q[IDX].valid) |=> $stable(map_q[IDX].destination);
endproperty
a_destination_stable_across_retry: assert property (p_destination_stable_across_retry);
// And across a recovery — the case Section 14 exploits.
property p_destination_stable_across_recovery;
@(posedge clk) disable iff (!rst_n)
(ucie_recovery_event && map_q[IDX].valid)
|=> ($stable(map_q[IDX].destination) && $stable(map_q[IDX].config_epoch));
endproperty
a_destination_stable_across_recovery:
assert property (p_destination_stable_across_recovery);
// Allocation backpressures; it never silently overwrites a live obligation.
property p_no_silent_overwrite;
@(posedge clk) disable iff (!rst_n)
map_alloc_fire |-> !map_q[alloc_idx].valid;
endproperty
a_no_silent_overwrite: assert property (p_no_silent_overwrite);
// A transport replay allocates no new semantic operation (Section 15).
property p_replay_allocates_nothing;
@(posedge clk) disable iff (!rst_n)
(ucie_object_arrived && duplicate_attempt) |=> $stable(semantic_alloc_count);
endproperty
a_replay_allocates_nothing: assert property (p_replay_allocates_nothing);Architecture. Immutability under three different events, plus overflow safety, plus the replay invariant.
Why three separate immutability properties rather than one. The first is the general invariant; the second and third name the two events that plausibly disturb it. When the general one fails, the specific ones tell you which event did it — which is the difference between a bug report and a debugging session.
Failure the fourth property catches. A full mapping table that overwrites rather than backpressures discards a live CXL obligation with no error anywhere — and the overwriting write is an entirely ordinary allocation, so nothing else will notice.
DV. The third property needs a recovery while an accepted operation is unresolved, and §35's third trace is that scenario.
17. CXL.cache at the Boundary
Not a retelling of 11.3. Line metadata, stable and transient states, probes, same-line conflict and the two completion points are taught there. The responsibility statements are:
Coherence state lives above the transport, always. No transport event — send, retire, retry, recovery — may create, destroy or alter a coherence state. The transport's entire relationship to coherence is delivery.
A UCIe retry cannot allocate another ownership transition (§15).
Pending semantic responses stay live across a UCIe recovery. The agents that owe them still owe them; a link event does not discharge an obligation (11.3 §26).
And a dirty-data obligation survives everything the transport does. Whoever holds the newest copy still holds it after a recovery, a retrain, a width change or a rate change. Losing that obligation is data loss, and it is the one failure in this chapter with no recovery path at all.
18. CXL.io at the Boundary
Concise, because 11.5 §7 owns the readiness vector. The responsibility statement is one sentence and it is worth its own section:
A UCIe link being up proves that bits can cross. It proves nothing about whether the CXL device has been discovered, configured, mapped, or made ready to serve.
Three distinct facts, routinely conflated:
| Fact | Established by | Proves |
|---|---|---|
| the link is operational | UCIe training and link state | bits cross reliably |
| the device is discovered and configured | CXL.io control operations | the device exists and has been set up |
| a region is usable | address decode plus device readiness | traffic to that range will be served |
Each is necessary and none implies another. §19 is the bug of assuming the first implies the third.
19. Wrong RTL — Readiness Derived From the Link
// WRONG — transport liveness treated as semantic readiness.
assign cxl_ready = ucie_link_active;Everything that can be true while ucie_link_active is asserted:
| Condition | Consequence of admitting traffic |
|---|---|
| discovery has not completed | requests to a device the host has not identified |
| the memory map is not programmed | requests to an address with no defined target |
| the device's memory is not initialised | reads return whatever the device happens to hold |
| the coherence agent is not ready | ownership requests against uninitialised metadata (11.1 §13) |
| a region is being reconfigured | §14's window, deliberately entered |
The correct form is a conjunction whose terms are separately owned:
// ILLUSTRATIVE. Each term is established by a different layer, and every one
// of them is necessary. Not all subprotocols need be enabled in every design —
// the conjunction is over what THIS design actually uses.
assign cxl_admit_allowed =
ucie_transport_ready_q // UCIe: link operational
&& cxl_config_valid_q // CXL.io: device discovered and configured
&& region_mapped_q // address decode: this range has a target
&& class_target_ready_q; // the target is ready for THIS classArchitecture. A named conjunction rather than an alias. Each term names the layer that owns it, so a failure to admit is immediately attributable.
Cycle behaviour, and this is the subtle part. The conjunction is evaluated at acceptance, and the result is bound to the accepted operation — it is not re-evaluated continuously underneath work already accepted. 11.5 §9 makes that argument in full; the responsibility version is that withdrawing readiness must not retroactively cancel an obligation the system already took on.
Failure. Any single-term shortcut. Also making class_target_ready_q a single bit shared by all three subprotocols, which asserts that they become ready together — and they do not (§7).
20. Class Resource Sharing — Three Structures
| One shared pool | Per-class pools | Shared + reservation | |
|---|---|---|---|
| Utilisation | highest | lowest | high |
| Head-of-line blocking across classes | yes | no | bounded |
| CXL.mem can starve CXL.cache | yes — §23 | no | no |
| State cost | smallest | NUM_CLASSES × depth | pool + a small slice |
| Suits | single-class traffic | strict isolation requirements | most real designs |
21. Progress Reservation
// ILLUSTRATIVE admission and arbitration state. A bounded reservation, not a
// full QoS scheme — what breaks Section 23's cycle is a bound, not fairness.
logic cache_progress_pending; // a response that RESOLVES an obligation
logic mem_bulk_pending;
logic [AGE_W-1:0] progress_wait_q; // saturating
logic progress_resource_available;
assign progress_resource_available =
(free_slots > RESERVED_FOR_PROGRESS) || cache_progress_pending;
always_comb begin
tx_select = TX_IDLE;
if (cache_progress_pending && (progress_wait_q >= PROGRESS_BOUND))
tx_select = TX_CACHE_PROGRESS; // bounded override
else if (mem_bulk_pending && progress_resource_available)
tx_select = TX_MEM_BULK;
else if (cache_progress_pending)
tx_select = TX_CACHE_PROGRESS;
end
always_ff @(posedge clk or negedge rst_n)
if (!rst_n)
progress_wait_q <= '0;
else if (cache_progress_pending && !(tx_fire && (tx_select == TX_CACHE_PROGRESS)))
progress_wait_q <= (progress_wait_q == AGE_MAX) ? AGE_MAX : progress_wait_q + 1'b1;
else if (tx_fire && (tx_select == TX_CACHE_PROGRESS))
progress_wait_q <= '0;Architecture. Two mechanisms working together: a reservation that stops bulk traffic from consuming the last slots, and a bounded override that guarantees progress traffic is served within PROGRESS_BOUND even if the reservation is somehow exhausted. Belt and braces, deliberately — the reservation is the common case and the override is the guarantee.
State. One saturating age counter and one comparison against free_slots. Saturating, because an age counter that wraps reports young exactly when the problem is worst (13.4 §13).
Cycle behaviour. tx_select is combinational. progress_wait_q advances only when progress traffic is pending and did not actually transfer — not merely when it was not selected, because a selected transfer that the downstream refused has not made progress either.
Contract. The coherence layer above depends on responses being delivered within a bound in order to guarantee its own liveness (11.3 §29). That dependency is invisible at this block's interface, which is precisely why §22 asserts it.
Failure. Setting RESERVED_FOR_PROGRESS to zero, which deletes the guarantee while leaving all the code that looks like it. Or classifying every CXL.cache message as progress-critical, which reserves so much that the reservation stops functioning as one.
DV. Saturate with CXL.mem traffic, trickle CXL.cache responses, and check progress_wait_q never reaches AGE_MAX. Then set RESERVED_FOR_PROGRESS to zero and confirm the design deadlocks in the predicted way — which validates that the mechanism is doing what the argument says.
22. SVA — Progress-Sensitive Traffic Is Served, Under Assumptions
// LIVENESS, bounded, with assumptions stated. An unqualified eventuality would
// be unprovable and useless (15.2 Section 36).
//
// A1: the transport eventually accepts when not in recovery
// A2: pending progress traffic remains pending until served
// A3: recovery terminates (14.2 Section 30)
assume property (@(posedge clk) disable iff (!rst_n)
(tx_valid && !recovery_active) |-> ##[1:TX_ACCEPT_BOUND] tx_ready);
assume property (@(posedge clk) disable iff (!rst_n)
(cache_progress_pending && !tx_fire) |=> cache_progress_pending);
property p_progress_served_within_bound;
@(posedge clk) disable iff (!rst_n)
cache_progress_pending
|-> ##[1:PROGRESS_SERVICE_BOUND] (tx_fire && (tx_select == TX_CACHE_PROGRESS));
endproperty
a_progress_served_within_bound: assert property (p_progress_served_within_bound);
// Bulk traffic is not starved either — fairness, a DIFFERENT claim.
property p_bulk_not_starved;
@(posedge clk) disable iff (!rst_n)
mem_bulk_pending |-> ##[1:BULK_FAIRNESS_BOUND] (tx_fire && (tx_select == TX_MEM_BULK));
endproperty
a_bulk_not_starved: assert property (p_bulk_not_starved);Architecture. Two bounded liveness properties with different bounds, plus three explicit assumptions.
Why the second matters as much as the first. A reservation that is too aggressive inverts the problem: bulk memory traffic starves instead, and the system is now slow rather than stopped. That is a better failure and it is still a failure, and only a property with its own bound will catch it.
Why A3 must be an assumption. Without it, the progress property fails on a design that is correct and merely stuck in a long recovery. Stating it converts "this hangs" into "this hangs because recovery did not terminate" — a diagnosis rather than a symptom.
DV. Prove both. Then disable A3 and confirm the first fails, verifying that the assumption is load-bearing rather than decorative.
23. Wrong Resource Sharing — CXL.mem Starves CXL.cache Into Deadlock
The system insight of the chapter, and it needs no unfairness, no bug in any single block, and no protocol violation.
1. A large CXL.mem streaming workload fills the shared transport staging.
2. A CXL.cache response — one that RESOLVES an ownership obligation — queues
behind that bulk traffic and is not scheduled.
3. The coherence agent waiting on that response cannot complete its transition.
4. The line stays in a transient state; further operations on it are serialised
behind the transition (11.3 §15).
5. The CXL.mem workload eventually issues an access to that line — or to a line
whose transition is queued behind the same resource.
6. → deadlock, in a system that was tuned for memory throughput.Four properties that make it genuinely hard.
Every participant is correct. CXL.mem is issuing legal requests. The scheduler is applying a defensible policy. The coherence agent is waiting for a response it is entitled to. No assertion about safety fires, because nothing incorrect happens — nothing happens at all.
It is delayed and load-dependent. Low and moderate load never reaches it. It appears at exactly the traffic levels a performance campaign is designed to produce, which is when the least verification attention is on functional correctness.
The class asymmetry is what creates it (§7). CXL.mem is the bandwidth-dominant class; CXL.cache carries the progress-critical messages. A shared resource will be filled by the first and needed by the second, and that is a structural property of the workload rather than a bug.
And the diagnosis is actively misleading. The link is busy, no errors are logged, and 15.5 §19's classifier reports high useful cycles right up to the stall. Everything reads healthy except that nothing completes.
The remedy is §21's, and the requirement is a bound, not fairness — because what has to be broken is a dependency cycle, and a cycle is broken by any guaranteed service, however small.
24. Recovery — Who Owns What
| State | Owner | What a UCIe recovery does to it |
|---|---|---|
| CXL semantic request outstanding | CXL | preserved — the obligation is unchanged |
| CXL cache line state, stable or transient | CXL | untouched — indexed by address, not by link |
| Dirty-data obligation | CXL | untouched — losing it is data loss (§17) |
| Recorded destination and config epoch | integration | preserved and not recomputed (§14, §16) |
| Mapping entry | integration | retained, re-qualified by epoch |
| Queued, unsent objects | integration | held, never dropped |
| Address-decode configuration | CXL / host software | unaffected by the link event — but it may change for other reasons (§14) |
| Replay entries | UCIe reliability | resolved by the reliability layer (14.3 §37) |
| Credits, link state, lane map | UCIe | rebuilt (14.2 §6) |
| In-flight physical attempts | UCIe | retransmitted or abandoned |
Read the top six rows. None of them is a link property, so none of them has any business changing when the link is re-established. A recovery re-establishes a link — nothing more (14.2 §4).
And read row 7 carefully, because it is the one that catches people. The link event does not change the memory map. But a recovery is often accompanied by software activity, and if that activity remaps a range while an operation is outstanding against it, §14's window is open — which is why the epoch record exists.
25. Lost Response Ambiguity
A timeout says no response arrived. It does not say the remote did nothing.
1. A CXL.mem write is transmitted and delivered.
2. The remote device performs the write. The data has landed.
3. The response is lost — or its transport acknowledgement is.
4. The local side times out.
5. It CANNOT conclude that the write did not happen.Three consequences, and the middle one is where designs go wrong.
Re-issuing is safe only if the operation is idempotent. A plain write to a fixed address usually is. An ownership transition is not, and neither is a read-modify-write (11.3 §27, 14.2 §22).
Rolling back is not available. The remote state has already moved and cannot be un-moved by a local decision. A design that "cleans up" by assuming the operation did not occur has made its model diverge from reality — and the divergence is silent.
And the semantic entry must stay live. It correctly records an unresolved obligation. The resolution is to determine what happened, not to assume — which is exactly what an exactly-once transport mechanism exists to make determinable (14.3 §20).
26. Degradation — Capacity Changes, Semantics Do Not
A link may recover narrower or slower (14.4).
| Changes | Does not change |
|---|---|
| bandwidth across the boundary | what any CXL operation means |
| latency of every transfer | any semantic identity |
| queue occupancy, backpressure, credit dynamics | any address's meaning or destination |
| replay-window pressure (14.3 §28) | any coherence state or obligation |
| how long an operation takes | whether it completes correctly |
A degraded link is slower. It is not different. A design in which a width change alters a CXL outcome has coupled a performance property to a correctness property, and the coupling is the bug — whichever outcome it happens to produce.
Note the third row on the right, because it is this chapter's specific hazard. 11.2 established that a memory address's destination is architectural state. A width change must not touch it — and §27 is the mechanism by which it accidentally does.
27. Wrong RTL — a Bound Derived From a Fixed Cycle Count
// WRONG — the timeout assumes full-width, full-rate transport, forever.
localparam int TIMEOUT_CYCLES = 4096; // measured on an x16 link at full rate
always_ff @(posedge clk)
if (op_outstanding[id] && (age_q[id] >= TIMEOUT_CYCLES))
declare_operation_failed(id); // ← fires on a healthy, degraded linkWhat happens after a recovery to half width:
| Link | Typical completion | TIMEOUT_CYCLES | Verdict |
|---|---|---|---|
| x16, full rate | ~2,100 cycles | 4096 | fine |
| x8, full rate | ~3,900 cycles | 4096 | marginal |
| x4, reduced rate | ~7,600 cycles | 4096 | spurious failure on every operation |
Three consequences.
A capacity change is reported as a fault. The transport is working; the operation would have completed. The timeout manufactures a failure that did not exist.
And the response to that fault is usually worse than the fault. Declaring an operation failed may trigger a recovery, a retry storm, or an escalation (14.5) — on a link that had just successfully recovered, potentially producing a recovery loop.
The fix is that bounds which must survive degradation are derived from the active configuration, not from a constant measured once:
// ILLUSTRATIVE. The bound scales with the ACTIVE configuration, and the
// configuration is the committed one (14.4's requested-vs-active distinction).
logic [TO_W-1:0] timeout_cycles_q;
always_ff @(posedge clk)
if (config_commit_fire)
timeout_cycles_q <= scale_timeout(BASE_TIMEOUT, active_width_q, active_rate_q);And the same reasoning applies to every depth, credit and window sized against a peak the link may no longer be delivering. If the response to a full structure is anything other than backpressure, degradation becomes loss (§16).
28. The Attribution Method
The practical payoff of a responsibility ledger. Given a failure, decide which of the two protocols to investigate — in a fixed order, because the cheap questions eliminate the most cases.
| Step | Question | If yes | If no |
|---|---|---|---|
| 1 | Did every object cross intact and exactly once? | continue | UCIe transport — integrity, retry, recovery |
| 2 | Did each semantic operation get exactly one delivery? | continue | the boundary — the delivery gate (§15) |
| 3 | Did each response resolve to a live semantic entry? | continue | the boundary — lifetime (§11) |
| 4 | Was every destination the one captured at acceptance? | continue | the boundary — destination handling (§14) |
| 5 | Did any progress class exceed its service bound? | — | continue |
| 6 | If none of the above, the failure is semantic | CXL — coherence, ordering, memory semantics |
Three notes on using it.
Steps 1 to 4 are answerable from the boundary's own instrumentation, cheaply and without a semantic model. Most integration failures are eliminated in those four steps, and reaching step 6 with all four clean is genuine evidence that the problem is above the boundary.
Step 5 is deliberately not a pass/fail gate. A starved progress class is a contributing condition, not usually the whole story — but knowing it occurred reframes everything below it.
And a "yes" at every step is not proof of a CXL bug. It is proof that this boundary is not the cause, which is a smaller and much more defensible claim — and it is the one that ends the "is it your layer or mine" conversation with evidence instead of assertion.
29. Three Scoreboard Layers
// Verification-only. THREE models, three identities, three failure classes.
class cxl_ucie_scoreboard;
// ---- Layer 1: CXL SEMANTIC model. What was promised, and to whom.
typedef struct {
int cxl_class; // io / cache / mem
bit live;
int predicted_destination; // computed INDEPENDENTLY, from the config in
// force at ACCEPTANCE — this is what catches §14
int accept_config_epoch;
int semantic_deliveries; // MUST be <= 1
int allocations; // MUST be 1
bit completed;
} semantic_op_t;
// ---- Layer 2: INTEGRATION model. Which mapping represents it, for how long.
typedef struct {
int semantic_id;
int transport_id;
int recorded_destination;
int recorded_config_epoch;
bit bound;
bit survived_recovery;
} integration_t;
// ---- Layer 3: UCIe TRANSPORT model. What physically happened.
typedef struct {
int attempts;
bit retired;
bit crossed_recovery;
} transport_obj_t;
semantic_op_t semantic [int];
integration_t integration [int];
transport_obj_t transport [int];
// ---- The check that catches Section 14, and nothing else can.
function void check_destination(int sid, int idx);
if (integration[idx].recorded_destination != semantic[sid].predicted_destination)
$error("DESTINATION DIVERGED op %0d: recorded %0d, predicted %0d "
, "(accept epoch %0d, recorded epoch %0d) — Section 14",
sid, integration[idx].recorded_destination,
semantic[sid].predicted_destination,
semantic[sid].accept_config_epoch,
integration[idx].recorded_config_epoch);
endfunction
// ---- The check that catches Section 11.
function void check_lifetime(int sid);
if (!semantic[sid].live && !semantic[sid].completed)
$error("SEMANTIC OP %0d retired without completion — Section 11", sid);
endfunction
// ---- Cross-layer conservation (Section 15).
function void check_conservation(int sid);
if (semantic[sid].allocations != 1)
$error("OP %0d allocated %0d times (must be 1)", sid, semantic[sid].allocations);
if (semantic[sid].semantic_deliveries > 1)
$error("OP %0d delivered %0d times (must be <= 1)",
sid, semantic[sid].semantic_deliveries);
endfunction
// ---- Recovery survival (Section 24).
function void check_recovery_survival(int idx);
if (integration[idx].survived_recovery && !integration[idx].bound)
$error("BINDING %0d lost across recovery — Section 24", idx);
endfunction
endclassArchitecture. Three models keyed by semantic identity, mapping index and transport object.
The field that matters most is predicted_destination. It must be computed by the model, from the configuration in force at acceptance — never read back from the design. A model that reads the design's recorded destination and compares it to the design's transmitted destination is comparing the design to itself, and §14 passes.
And accept_config_epoch alongside it is what makes a divergence explicable rather than merely detected: the error message names which configuration each side used.
30. Why This Differs From Module 11's Integration Scoreboard
11.5 §20's three-model scoreboard is organised by state plane — memory mapping, coherence, transport — because that chapter is about three planes being simultaneously valid.
This one is organised by responsibility owner — CXL, the boundary, UCIe — because this chapter is about attribution.
| 11.5's model | This chapter's model | |
|---|---|---|
| Keyed by | plane | owner |
| Question it answers | is each plane internally consistent? | whose responsibility failed? |
| Distinguishes | memory vs coherence vs transport state | CXL vs boundary vs UCIe |
| Feeds | cross-plane validity checks | §28's attribution procedure |
They are complements, not alternatives. A serious environment builds both, and the reason is visible in one example: a wrong destination is a memory-plane failure in 11.5's model and a boundary failure in this one — and both statements are true, because the memory plane's state was corrupted by the boundary using it at the wrong time. The first tells you what is broken; the second tells you who broke it.
31. Flagship Trace 1 — a CXL.mem Request Across UCIe
Illustrative. One memory read, accepted at configuration epoch 7.
| Cyc | CXL semantic op | Recorded dest / epoch | Mapping entry | UCIe object | Link | Note |
|---|---|---|---|---|---|---|
| 0 | — | — | — | — | operational | — |
| 1 | accepted | dev X / epoch 7 | — | — | operational | destination decided ONCE |
| 2 | live | X / 7 | bound | — | operational | — |
| 3 | live | X / 7 | bound | formed, retained | operational | — |
| 4 | live | X / 7 | bound | attempt 1 | transmitting | uses the recorded dest |
| 6 | live | X / 7 | bound | in flight | operational | — |
| 7 | live | X / 7 | bound | arrived | operational | integrity checked |
| 8 | live | X / 7 | bound | delivered | operational | semantic delivery ×1 |
| 9 | live | X / 7 | bound | acknowledged | operational | — |
| 10 | live | X / 7 | bound | retired | operational | semantic op still live |
| 14 | live | X / 7 | bound | — | operational | device X reads |
| 18 | live | X / 7 | bound | — | operational | response in flight |
| 20 | live | X / 7 | bound | — | operational | response arrives |
| 21 | live | X / 7 | bound | — | operational | matched to semantic id |
| 22 | live | X / 7 | released | — | operational | mapping freed |
| 23 | live | — | — | — | operational | data returned to CXL |
| 24 | completed | — | — | — | operational | semantic completion |
Four readings.
Cycle 1 records the destination and the epoch, and neither is touched again. Everything downstream uses the record, never a fresh decode. §14's bug is re-deriving it at cycle 4.
Cycle 10: the UCIe object retires while the semantic operation is live — fourteen cycles before completion. §11's bug is freeing the semantic entry here.
Cycle 22 releases the mapping when the response resolves, not when the transport retired. Two structures, two lifetimes.
And cycle 24 is the completion — not cycle 4, not cycle 10, not cycle 20. Four candidate events, one correct answer, and the correct one is the last and least local.
32. Flagship Trace 2 — a CXL.cache Ownership Transition With a Replay
| Cyc | Semantic transition | Pending responses | UCIe attempts | Semantic deliveries | Correct? |
|---|---|---|---|---|---|
| 1 | allocated (once) | — | 0 | 0 | ✓ |
| 3 | live | {B} | attempt 1 | 0 | — |
| 6 | live | {B} | arrived, integrity FAIL | 0 | ✓ gate blocks |
| 10 | live | {B} | attempt 2, same object | 0 | ✓ replay |
| 13 | live | {B} | arrived, OK | 1 | ✓ first delivery |
| 14 | live | {B} | — | 1 | B invalidates |
| 16 | live | {B} | — | 1 | B's response sent |
| 18 | live | {B} | — | 1 | response lost |
| 24 | live | {B} | attempt 3 — lost-ACK replay | 1 | — |
| 26 | live | {B} | arrived, OK | 1 | ✓ duplicate suppressed |
| 27 | live | {B} | — | 1 | ✓ ACK re-sent anyway |
| 30 | live | {} | — | 1 | B's re-sent response lands |
| 31 | committed | — | — | 1 | ✓ one transition |
Four readings.
Three physical attempts, one semantic delivery, one ownership transition. §15's invariant holding under replay.
Cycle 1 allocates once and nothing re-allocates. §16's fifth property is exactly this.
Cycle 26 and 27 are the two halves that pull opposite ways. The effect is suppressed — B does not invalidate twice and no second transition appears. The reply is not — because a silent duplicate leaves the pending set non-empty forever (14.3 §22).
And the CXL layer above saw one clean transition. Slower than it would have been. Not different.
33. Flagship Trace 3 — Degradation With CXL Traffic Outstanding
| Cyc | Semantic ops outstanding | Recorded dests | Link | Active config | What must be true |
|---|---|---|---|---|---|
| 0 | 6 | X, X, Y, X, Y, Y | operational | x16, full rate | steady |
| 5 | 6 | unchanged | error detected | x16 | — |
| 6 | 6 | unchanged | recovery entered | x16 | no semantic op is cancelled |
| 7 | 6 | unchanged | quiescing | x16 | queued objects held |
| 12 | 6 | unchanged | retraining | — | — |
| 18 | 6 | unchanged | recovered | x4, reduced rate | destinations untouched |
| 19 | 6 | unchanged | operational | x4 | timeouts rescaled (§27) |
| 20 | 6 | unchanged | operational | x4 | transmission resumes |
| 40 | 4 | unchanged | operational | x4 | draining — slower |
| 90 | 0 | — | operational | x4 | all six completed correctly |
Five readings, and this is the chapter's clearest statement of its thesis.
Cycle 6: six operations outstanding, six preserved. Not one is cancelled, failed or re-issued. The link event has no semantic content.
Cycle 18: the link returns at a quarter of the width and a lower rate. Roughly an order of magnitude less capacity. Every recorded destination is byte-identical to cycle 0.
Cycle 19 is the step most designs omit. Bounds derived from the previous configuration are now wrong, and if they are not rescaled the very next cycle produces §27's spurious failures — on a link that just recovered successfully, potentially looping.
Cycles 20 to 90: the drain is slow. Ten times the cycles for the same six operations. That is the correct outcome, and a performance report should say so rather than a fault report.
And every operation completed correctly. Capacity changed; correctness did not. If any of the six had completed differently, the design would have coupled the two.
34. Error Injection
| # | Injection | Targets |
|---|---|---|
| 1 | retry on an outbound CXL.mem object | §15 — duplicate delivery |
| 2 | retry on a CXL.cache response | §32's cycle 24 — the duplicate that must still be answered |
| 3 | lost response after the remote acted | §25 — timeout ambiguity |
| 4 | recovery with operations outstanding | §24, §33 |
| 5 | memory remap while an operation is outstanding | §14 — the silent-corruption case |
| 6 | recovery returning a narrower link | §26, §27, §33 |
| 7 | mapping table full | §16's fourth property |
| 8 | sustained CXL.mem load with CXL.cache responses trickling | §22, §23 |
| 9 | ucie_link_active before CXL.io configuration completes | §19 |
| 10 | a region withdrawn after acceptance but before transmission | §19's binding-at-acceptance rule |
| 11 | transport identity reused while the semantic op is live | §10's contract |
| 12 | dirty owner probed, then the link degrades | §17 — obligation survival |
| 13 | all three classes offered simultaneously at full rate | §7, §20 |
Two notes.
Row 5 is the highest-value injection in the chapter and the one most environments cannot produce, because it needs configuration change coordinated with in-flight traffic. It is the only test of §14, and §14 is silent data corruption.
Row 2 is not the same test as row 1. A retried request and a retried response break different things (§34's first two rows are two different failures) — and an environment that injects only on the request path tests half the boundary.
35. Coverage
covergroup cg_cxl_ucie_boundary @(posedge clk);
option.per_instance = 1;
// --- The class dimension (Section 7).
cp_class : coverpoint active_cxl_class {
bins cxl_io = {0};
bins cxl_cache = {1};
bins cxl_mem = {2};
}
cp_class_mix : coverpoint num_classes_active {
bins one = {1}; bins two = {2}; bins all_three = {3}; // Section 20
}
// --- Lifetime (Sections 11, 12).
cp_retire_event : coverpoint semantic_retire_trigger {
bins on_completion = {0}; // the only correct one
bins on_tx_fire = {1}; // must never occur
bins on_retire = {2}; // must never occur
}
// --- Destination stability (Sections 14, 16).
cp_config_change : coverpoint config_change_relative_to_op {
bins none = {0};
bins before_accept = {1};
bins after_accept_before_tx = {2}; // THE case — Section 14
bins after_tx = {3};
}
cp_dest_epoch_mismatch : coverpoint dest_epoch_mismatch_observed;
// --- Transport interaction (Section 15).
cp_retry_point : coverpoint retry_occurred_on {
bins none = {0}; bins request = {1}; bins response = {2};
}
cp_duplicate_gated : coverpoint duplicate_suppressed_at_gate;
// --- Recovery and degradation (Sections 24, 26, 27).
cp_recovery_point : coverpoint recovery_during {
bins none = {0};
bins ops_outstanding = {1};
bins objects_queued = {2};
}
cp_width_after_rec : coverpoint width_after_recovery {
bins same = {0}; bins narrower = {1}; // Section 33
}
cp_timeout_rescaled : coverpoint timeout_rescaled_after_config_commit;
// --- Readiness (Sections 18, 19).
cp_readiness : coverpoint admit_gate_terms_false {
bins none = {0};
bins transport = {1};
bins cxl_config = {2};
bins region_map = {3};
bins target_ready = {4}; // each term must gate ALONE
}
// --- Liveness (Sections 21-23).
cp_progress_wait : coverpoint progress_wait_q {
bins none = {0};
bins some = {[1:PROGRESS_BOUND-1]};
bins at_bound = {PROGRESS_BOUND}; // the override firing
}
// --- Crosses that carry the information.
x_config_recovery : cross cp_config_change, cp_recovery_point; // Section 14
x_class_progress : cross cp_class, cp_progress_wait; // Section 23
x_recovery_width : cross cp_recovery_point, cp_width_after_rec; // Section 33
x_retry_class : cross cp_retry_point, cp_class;
endcovergroupSix bins whose value is in whether they are reachable at all:
cp_retire_event.on_tx_fire and .on_retire must stay at zero. They are the only bins in this curriculum whose coverage goal is never to be hit — they exist as detectors, and hitting either is §11.
cp_config_change.after_accept_before_tx. §14's exact window. It requires configuration change coordinated with in-flight traffic and is the bin most environments lack.
cp_readiness — every term, alone. Each admission term must be shown to gate by itself; a design where one term is redundant has a readiness check that is smaller than it looks (§19).
cp_width_after_rec.narrower. §26 and §33. A recovery that returns the same width tests nothing about degradation.
cp_timeout_rescaled. §27's fix, exercised. A design that never rescales will pass every test that never degrades.
And cp_class_mix.all_three. All three subprotocols active simultaneously — §7's asymmetry and §23's starvation both need it, and neither appears with one class active.
36. Debug Taxonomy
| Signature | Most likely cause | First instrument |
|---|---|---|
| UCIe packets correct, stale or wrong data | CXL semantics or §14 — not transport | Layer-1 predicted destination vs recorded |
| A CXL operation disappears after a recovery | §24 — the boundary released semantic state on a transport event | what was cleared at the recovery cycle |
| A response arrives and matches nothing | §11 — semantic entry retired early | which event freed it: tx_fire, retire, or completion? |
| Duplicate ownership action after a retry | §15 — semantic layer driven by arrival, not delivery | is there a delivery gate, and is it combinational? |
| Link active, CXL request rejected | §19 — higher-layer readiness or configuration incomplete | which admit-gate term is false |
| Heavy CXL.mem load hangs CXL.cache traffic | §23 — no progress reservation | progress_wait_q under load; is anything reserved? |
| Operations fail immediately after a successful recovery | §27 — a bound not rescaled to the active configuration | which timeouts derive from active width and rate |
| A degraded link changes an address's destination | §14 + §26 — decode coupled to live configuration | is the destination recorded at acceptance? |
| Writes land, reads return stale, no errors anywhere | §14 — two devices each holding part of one address | predicted vs recorded destination, per operation |
| Transfers vanish under load with no error | §16's fourth property — mapping table overwritten | occupancy vs MAP_DEPTH; is there an overflow assertion? |
| Retries never stop on one object | §32's cycle 27 — the duplicate suppressed the reply too | is the acknowledgement re-sent on a recognised duplicate? |
Row 9 deserves emphasis. Writes land, reads return stale, and nothing anywhere reports an error is the signature of §14, and it is the hardest failure in this chapter. Every layer is functioning; the operations simply went to two different places.
37. Debug Checklist
- Which CXL subprotocol — io, cache or mem? (§7)
- Which semantic operation, and what identity does it carry?
- What destination was recorded, and at which configuration epoch? (§10)
- Does that match what an independent model predicts from the accept-time configuration? (§29)
- Which mapping entry represents it, and to which UCIe transport identity?
- How many transport attempts occurred?
- How many semantic deliveries occurred? Must be ≤ 1. (§15)
- Did a retry occur — on the request, on the response, or both? (§34)
- Did a UCIe recovery occur while the operation was outstanding? (§24)
- Did the link return at a different width or rate? (§26)
- Were bounds rescaled to the active configuration afterwards? (§27)
- Did the memory map change at any point during the operation's life? (§14)
- Is the semantic entry still live — and if not, which event freed it? (§11)
- Did the response resolve to a live semantic entry? (§12)
- Which admit-gate terms were true at acceptance? (§19)
- Was CXL.cache progress traffic starved by CXL.mem? (§23)
- Is a dirty-data obligation involved, and where is it? (§17)
- Run §28's attribution steps in order — which step first answered "no"?
- Which scoreboard layer diverged first? (§29, §30)
38. Common Misconceptions
"UCIe owns CXL coherence." It does not. Official UCIe material says UCIe maps CXL natively and that caching requirements are addressed through the caching protocol. Native mapping is a statement about ecosystem and machinery. Not one semantic responsibility crosses the line (§3, §4, §8).
"CXL and UCIe transaction lifetimes are the same." One CXL operation is simultaneously represented in several structures with different lifetimes, and the transport's representation typically retires while the semantic obligation is still outstanding (11.5 §12, §9).
"A UCIe link being active means CXL is ready." The link being up proves bits can cross. It proves nothing about discovery, configuration, address mapping, or a target being ready to serve — and each of those is necessary and none implies another (§18, §19).
"A UCIe retry should regenerate the CXL operation." A retry re-transmits the same object. Regenerating the operation produces a second semantic action — a second ownership transition, or a second write — from a mechanism that was working correctly (§15).
"CXL.mem and CXL.cache can always share one queue safely." CXL.mem is bandwidth-dominant and CXL.cache carries progress-critical responses. A shared resource will be filled by the first and needed by the second, and the resulting deadlock is safety-correct, load-dependent, and arrives at exactly the traffic levels a performance campaign produces (§7, §23).
"UCIe recovery invalidates CXL semantic state." It does not. A recovery re-establishes a link. Outstanding requests, line states, dirty-data obligations, recorded destinations and queued objects all survive — and the recorded destination in particular must not be recomputed afterwards (§14, §24).
"A clean UCIe transport scoreboard proves CXL correctness." Every object can cross once with clean integrity, every replay entry can retire, and a write can still land in the wrong device. Transport correctness is a strictly weaker claim (§14, §29).
"Link degradation only changes physical bits, so there is no visible impact." Degradation changes latency, bandwidth, queue pressure and replay dynamics — which is very visible in performance. What it must not change is any semantic outcome, and a design with fixed-cycle timeouts converts a capacity change into a manufactured fault (§26, §27).
"Module 11 and this chapter are the same topic." Module 11 teaches the mechanisms — flit framing, HDM windows, coherence states, the three state planes. This chapter teaches the responsibility ledger and an attribution method, and it is only possible after 16.4 because the CXL/CHI contrast is what makes native mapping meaningful as a category (§2, §4, §30).
39. Understanding Check
40. Module 16 Complete — and What Comes Next
Module 16 in five chapters:
| 16.1 | Shared memory | what it takes for several dies to agree that address X names one location |
| 16.2 | Cache coherency | the state that makes N-agent coherence correct — ownership, sharers, transients |
| 16.3 | Coherency across UCIe | semantic operations against messages against attempts, generically |
| 16.4 | The CHI boundary | a Streaming Protocol; reliability ownership depends on mode and revision |
| 16.5 | The CXL boundary | natively mapped; the responsibility ledger and how to attribute a failure |
And one sentence held across all five: transport carries meaning without owning it. Every failure in the module is a design that let a transport event decide something semantic — a pending bit cleared on transmit, a binding freed on retirement, a semantic entry retired on a send, a destination recomputed after a recovery, a bound derived from a width that no longer exists.
Two protocols, two relationships, one ledger. CXL is natively mapped and CHI was a Streaming Protocol in Raw Mode; the machinery differs and not one semantic responsibility moves in either case.
Three subprotocols, three contracts. CXL.io is mistaken for readiness, CXL.cache is starved, CXL.mem starves — and each has its own duplication cost.
And an attribution method that answers "whose layer" with evidence: exactly-once crossing, exactly-one delivery, a live entry for every response, a destination unchanged since acceptance, and a progress class within its bound.
Module 16 has treated memory as something coherence operates on. Module 17 turns to the memory itself — the memory chiplet as a manufactured, standalone die with its own timing, its own maintenance obligations, and its own reasons for existing, reached across exactly the boundary this module has spent five chapters characterising.
- 17.1 — Memory Chiplets — DRAM and HBM as standalone chiplets.
Browse the full path on the UCIe tutorials index.