CXL · Module 31
“CXL.cache and CXL.mem Are Identical”
Same payload, same link, same diagram — and not substitutable. Nine tests: the initiator, the per-line state a device must carry, the obligation to answer, the storage arithmetic, opposite failure signatures, the substitution test, two obligations, two critical paths, and the four conditions.
"CXL.cache and CXL.mem are basically the same thing" is the belief that survives the longest, because the two really do appear side by side in every diagram, carry payloads of the same shape, and run over the same link. Nothing a newcomer observes contradicts it.
The question this chapter turns on:
Substitute one for the other. What goes unserved?
The test is cheap, decisive, and almost never run. Two things that are interchangeable produce the same count when swapped; two things that are not produce a different one. Everything else in this chapter is a way of predicting that count before you run it.
1. Same Shape Is Not Same Thing
The belief is assembled from observations that are individually correct.
| Observation | True? | Does it imply identity? |
|---|---|---|
| Both carry cache-line-sized payloads | yes | no — that is a payload size |
| Both run over the same link | yes | no — so do all of them |
| Both appear in the same diagrams | yes | no — that is a diagram |
| Therefore they are interchangeable | — | does not follow |
Identity is a claim about substitutability, and none of the premises is about substituting anything. The two differ on four axes that a diagram does not show: who starts the transaction, what state the device must carry, what its failure looks like, and what its critical path waits for.
Throughout this chapter the two are called protocol A and protocol B — A is the device-initiated, ownership-bearing one; B is the host-initiated, memory-serving one. No model names either, and the naming happens only in prose.
2. How To Use This Chapter
Each of the nine dimensions below is a working test of the claim, and every one answers the same seven questions:
| Facet | What it settles |
|---|---|
| The claim under test | the specific form of "identical" being examined |
| What identity would require | the condition that would have to hold |
| The measurement | what the model computes, and from what |
| What the shortcut build reports | the reasoning the misconception uses |
| Why the belief is reasonable | the true observation it is built on |
| What it costs to hold | the engineering decision it leads to |
| What to say instead | the one-sentence correction |
3. The One-Sentence Model
One protocol is started by the device and obliges it to carry per-line ownership state and answer interrogations about it; the other is started by the host and obliges it to carry per-request state and serve accesses — so they differ in initiator, in device storage, in failure signature and in what their critical path waits for, and a substitution leaves requests unserved.
4. What This Chapter Owns
| Ground | Owner |
|---|---|
| The three protocols and what each carries | Module 3 |
| Device types, and which protocols each uses | Module 4 |
| Reviewing coherency invariants across agents | 30.4 |
| Why "replaces" is the wrong verb | 31.1 |
| Why "only for memory" is a sampling error | 31.2 |
| Why the two sub-protocols are not interchangeable | this chapter |
The boundary with 31.2 is worth stating. That chapter argues that the family has more than one member. This one argues that two specific members are not the same member — which is the next question somebody asks, and it needs completely different evidence: a substitution rather than a census.
5. Teaching-Model Boundary And Source Discipline
Every model in this chapter is a teaching model, and each computes a property of a CLAIM rather than of a protocol.
Nothing in this chapter states a normative detail of any specification. No opcode, channel, packet layout, bit position, field width, coherence state, state name, transition, snoop type, response encoding, register definition, timing guarantee or specification revision appears anywhere — checked by a scan over the finished page as well as by writing the models that way.
| Claim class | How it is marked |
|---|---|
| General architectural reasoning | stated plainly, at the level of initiator and state |
| Teaching abstraction | declared in the model header |
| Illustrative parameter | every concrete figure in a model or table |
| Simulator-derived result | quoted from a run and asserted |
| Derived arithmetic | shown with its inputs |
One modelling choice needs stating up front. The ownership model counts held lines and nothing else. It does not model coherence states, transitions or messages, because the property under review is how much state the device is obliged to carry — and that is a count, not a state machine. The abstraction is declared in the model header, and it is what keeps the chapter clear of the specification.
6. Test 1 — Who Starts The Transaction?
The claim under test. That both are the same kind of exchange.
What identity would require. That the initiator were the same on both.
The failure. The initiator is the one fact about an interface you cannot change without changing everything downstream — who arbitrates, who backpressures, who times out, and who retries.
// RTL 1 - who starts the transaction?
//
// The first axis on which two sub-protocols differ is the INITIATOR. One is
// started by the device reaching into host memory; the other is started by the
// host reaching into memory the device presents. "Identical" is the assertion
// that the initiator is the same on both, and the initiator is the one fact
// about an interface you cannot change without changing everything downstream
// - who arbitrates, who backpressures, who times out, and who retries.
//
// BAD : "they both move cache lines, so they are the same"
// GOOD : name the initiator on each, and notice they are opposite
//
// TEACHING MODEL. Two illustrative initiator flags. It is not a model of CXL
// or of any protocol, and it contains no opcode, channel, packet layout, field
// width, encoding, register definition or timing guarantee from any published
// standard. Protocol A is the device-initiated, ownership-bearing one;
// protocol B is the host-initiated, memory-serving one.
//
// INITIALIZATION CONTRACT (combinational + two counters):
// power-on/reset : counters zero; initiator_id is a pure function
// re-initialise : not applicable to the decode
// telemetry : initiator_id is published per protocol so the two can
// be compared without reading either specification
module initiator_side #(parameter int SAME_MOVER_SAME_PROTOCOL = 0) (
input logic clk, rst_n,
input logic assess,
input logic a_device_initiates, b_device_initiates,
output logic [7:0] initiator_a, initiator_b, n_comparisons, n_conflated,
output logic same_initiator, agrees_protocol,
output logic init_err
);
// Initiator 1 is the device; initiator 2 is the host. Zero is neither, which
// is a legal state for a link with no traffic on that protocol at all.
assign initiator_a = a_device_initiates ? 8'd1 : 8'd2;
assign initiator_b = b_device_initiates ? 8'd1 : 8'd2;
// The truth: two protocols share an initiator only when their flags agree.
assign same_initiator = (initiator_a == initiator_b);
// The whole review point: what the reader concludes from both protocols
// carrying cache-line-sized payloads.
assign agrees_protocol = (SAME_MOVER_SAME_PROTOCOL != 0) ? 1'b1 : same_initiator;
// SAFETY-OF-CLAIM VIOLATION: two protocols were called the same while their
// initiators are opposite.
assign init_err = assess && agrees_protocol && !same_initiator;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_comparisons <= 8'd0; n_conflated <= 8'd0;
end else if (assess) begin
n_comparisons <= n_comparisons + 8'd1;
if (init_err) n_conflated <= n_conflated + 8'd1;
end
end
endmoduleThe measurement. A device-initiated A against a host-initiated B:
A device-initiated, B host-initiated : init_a=1 init_b=2 same=0 same_mover_says=1Opposite initiators, and the same-mover build calls them the same on the grounds that both carry cache-line-sized payloads. The run drives all four combinations — device/host, device/device, host/host, host/device — and the weak build conflates on two of the four.
Why the belief is reasonable. From a diagram, an arrow is an arrow. The initiator is a property of the arrow's tail, and tails are the part of a diagram nobody reads.
What it costs to hold. An arbitration and backpressure design sized for one direction, and a bring-up that discovers the other one needs its own.
What to say instead. "Name the initiator on each. They are opposite, and everything downstream of the initiator differs with it."
7. Test 2 — What State Does The Device Have To Carry?
The claim under test. That the device-side implementations are comparable.
What identity would require. That neither obliged state the other did not.
The failure. A device that holds copies of host memory must know, per line, whether it still holds that line and on what terms. A device that serves memory to the host holds no copies and needs no such record. That is a structural difference, not a stylistic one.
// RTL 2 - the state one of them obliges the device to carry.
//
// A device that holds copies of host memory must know, per line, whether it
// still holds that line and on what terms. A device that serves memory to the
// host holds no copies and needs no such record. That is a STRUCTURAL
// difference, not a stylistic one: one of the two obliges the device to
// implement per-line tracking storage and the state machine that maintains it,
// and the other does not.
//
// BAD : "the device just needs a buffer either way"
// GOOD : count the lines the device HOLDS, and say what record each one needs
//
// TEACHING MODEL. Sequential.
// State remembered : how many lines this device currently holds.
// Safety : the tracking obligation is never reported absent while
// the device holds a line.
// No coherence state, state name, encoding or transition from any
// specification appears; the model counts held lines and nothing else.
//
// INITIALIZATION CONTRACT:
// power-on/reset : lines_held zero - a device out of reset holds nothing
// initialisation : `take_line` and `drop_line` are the only movers
// re-initialise : `flush_all` returns the count to zero and is legal at
// any time, including while lines are held; it is the
// recovery action, it is idempotent, and it DOMINATES a
// simultaneous take, because a flush that a fresh take
// could survive would not be a flush
// telemetry : lines_held is published, so tracking_needed is checkable
module ownership_tracking #(parameter int A_BUFFER_IS_ENOUGH = 0) (
input logic clk, rst_n,
input logic take_line, drop_line, flush_all, assess,
output logic [7:0] lines_held, n_assessments, n_untracked,
output logic tracking_needed, tracks_it,
output logic own_err
);
logic [7:0] held_q;
assign lines_held = held_q;
// The truth: a device that holds anything owes a per-line record.
assign tracking_needed = (held_q != 8'd0);
// The whole review point: whether the device implements that record.
assign tracks_it = (A_BUFFER_IS_ENOUGH != 0) ? 1'b0 : tracking_needed;
// SAFETY VIOLATION: lines are held and nothing tracks them.
assign own_err = assess && tracking_needed && !tracks_it;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
held_q <= 8'd0; n_assessments <= 8'd0; n_untracked <= 8'd0;
end else begin
// ONE assignment, priority written down. A take and a drop in the same
// cycle net to no change, which is the case two independent statements
// would get wrong by discarding the earlier one.
if (flush_all) held_q <= 8'd0;
else if (take_line && !drop_line) held_q <= (held_q == 8'hFF) ? held_q : held_q + 8'd1;
else if (drop_line && !take_line) held_q <= (held_q == 8'd0) ? held_q : held_q - 8'd1;
else held_q <= held_q;
if (assess) begin
n_assessments <= n_assessments + 8'd1;
if (own_err) n_untracked <= n_untracked + 8'd1;
end
end
end
endmoduleThe measurement. One line held:
one line held : lines=1 tracking_needed=1 buffer_is_enough_tracks=0Holding anything at all creates the obligation. The buffer-is-enough build carries no per-line record and is in violation from the first line.
The model's initialisation contract is explicit and the run exercises all of it. A take and a drop in the same cycle net to no change; a drop at zero saturates rather than wrapping to 255; a flush dominates a simultaneous take; and a repeated flush is idempotent. A counter that wraps from 0 to 255 turns "the device holds nothing" into "the device holds everything", which is why the saturation is driven rather than assumed.
Why the belief is reasonable. Both devices have a buffer. The difference is not the buffer, it is the record of what the buffer's contents mean — and that record is invisible in a block diagram.
What it costs to hold. A device area estimate missing its per-line tracking structure entirely, discovered when the state machine that maintains it has to be designed.
What to say instead. "Count the lines the device HOLDS. Each one needs a record, and serving memory holds none."
8. Test 3 — Can This Device Answer At All?
The claim under test. That the obligations are symmetric.
What identity would require. That both protocols could interrogate the device, or neither.
The failure. A device that holds a copy of host memory can be asked about it, and must answer — every time, within the window the interface allows. A device that only serves memory it presents is never asked, because it holds nothing to be asked about. A device built for the serving protocol and deployed on the holding one has no answering path at all. It does not answer slowly; it does not answer.
// RTL 3 - the obligation to answer.
//
// A device that holds a copy of host memory can be asked about it, and it must
// answer - every time, within the window the interface allows. A device that
// only serves memory it presents is never asked, because it holds nothing to
// be asked about. The obligation is therefore asymmetric, and a device built
// for the serving protocol and deployed on the holding one has no answering
// path at all. It does not answer slowly; it does not answer.
//
// BAD : "we will add the response later"
// GOOD : ask whether this protocol can interrogate the device, and if it
// can, build the answering path before anything else
//
// TEACHING MODEL. Sequential. The interrogation is an abstract request; no
// snoop type, encoding, response code, channel or timing from any
// specification appears.
// Safety : a device that holds a line always answers an interrogation.
//
// INITIALIZATION CONTRACT:
// power-on/reset : holds nothing, owes nothing, outstanding count zero
// initialisation : `take_line` gives it something to be asked about
// re-initialise : `flush_all` drops everything and CANCELS an outstanding
// obligation, because a device that no longer holds the
// line has nothing to answer about - this is the one
// place the recovery action changes an in-flight
// obligation, and it is stated rather than implied
// telemetry : owed_answers must read zero whenever the device is idle
module snoop_obligation #(parameter int SERVING_NEEDS_NO_ANSWER = 0) (
input logic clk, rst_n,
input logic take_line, flush_all, interrogated, answer_now, assess,
output logic [7:0] owed_answers, n_interrogations, n_unanswered,
output logic holds_line, must_answer, can_answer,
output logic snoop_err
);
logic held_q;
logic [7:0] owed_q;
assign holds_line = held_q;
assign owed_answers = owed_q;
// The truth: holding a line creates the obligation to answer about it.
assign must_answer = held_q && (owed_q != 8'd0);
// The whole review point: whether the device has an answering path at all.
assign can_answer = (SERVING_NEEDS_NO_ANSWER != 0) ? 1'b0 : 1'b1;
// SAFETY VIOLATION: an answer is owed and the device cannot produce one.
assign snoop_err = assess && must_answer && !can_answer;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
held_q <= 1'b0; owed_q <= 8'd0;
n_interrogations <= 8'd0; n_unanswered <= 8'd0;
end else begin
// ONE assignment each, priority written down.
if (flush_all) held_q <= 1'b0;
else if (take_line) held_q <= 1'b1;
else held_q <= held_q;
// A flush cancels the outstanding obligation: the device no longer holds
// the line, so there is nothing left to answer about.
if (flush_all) owed_q <= 8'd0;
else if (interrogated && !answer_now) owed_q <= (owed_q == 8'hFF) ? owed_q : owed_q + 8'd1;
else if (answer_now && !interrogated) owed_q <= (owed_q == 8'd0) ? owed_q : owed_q - 8'd1;
else owed_q <= owed_q;
if (interrogated) n_interrogations <= n_interrogations + 8'd1;
if (assess && snoop_err) n_unanswered <= n_unanswered + 8'd1;
end
end
endmoduleThe measurement. A device holding a line and interrogated about it:
holds a line, interrogated : owed=1 must_answer=1 can_answer=1 serving_build_can=0The serving build has no answering path, and the obligation is outstanding. The run drives an interrogation while the device holds nothing — an answer is recorded as owed and yet nothing must be answered, because there is no line to answer about. That case is the only one that isolates the held term of the obligation, and a mutation found it missing.
The flush case is the one worth keeping. A flush cancels an outstanding obligation, because a device that no longer holds the line has nothing to answer about. That is the one place in the model where a recovery action changes an in-flight obligation, and it is stated in the contract rather than implied by the code.
Why the belief is reasonable. Nobody designs the answering path first, and a device that has not been interrogated yet looks complete.
What it costs to hold. A device that passes every test until the first interrogation, and a debug session in which nothing is corrupted and nothing arrives.
What to say instead. "Can this protocol interrogate the device? If it can, build the answering path before anything else."
9. Test 4 — Count The Flops Each Obligation Costs
The claim under test. That the storage is comparable.
What identity would require. That the two products were the same.
// RTL 4 - what each protocol costs the device in flops.
//
// The two protocols place different storage obligations on the device, and the
// difference is arithmetic rather than rhetorical. Holding lines costs state
// PER LINE HELD. Serving memory costs state per outstanding REQUEST, which is
// a much smaller number and does not scale with capacity. A claim that the two
// are the same is a claim that these two products are the same.
//
// BAD : "the storage is comparable"
// GOOD : lines x bits-per-line against outstanding x bits-per-request, in
// the same units, with both numbers stated
//
// TEACHING MODEL. All widths and depths are illustrative parameters. None is a
// CXL figure, and no register layout or field width from any specification
// appears.
//
// INITIALIZATION CONTRACT (combinational + counters):
// power-on/reset : counters zero
// re-initialise : not applicable
// telemetry : both products are published so a reader can divide
module state_per_line #(parameter int STORAGE_IS_COMPARABLE = 0) (
input logic clk, rst_n,
input logic assess,
input logic [7:0] cached_lines, bits_per_line,
input logic [7:0] outstanding, bits_per_request,
output logic [15:0] holding_bits, serving_bits, ratio_x10,
output logic [7:0] n_assessments, n_understated,
output logic holding_costs_more, reported_comparable,
output logic store_err
);
logic [31:0] h_q, s_q, r_q;
// WIDTH INVARIANT, written down rather than guarded. Both products are
// 8-bit by 8-bit, so each reaches at most 255 x 255 = 65,025, and a 16-bit
// destination holds 65,535. Neither can overflow, and a clamp here would be
// code with no reachable input that takes its true branch. A mutation
// campaign found exactly that and the clamps were deleted - 30.5 section 20's
// rule, applied to a bound that comes from operand WIDTH rather than from an
// enclosing guard, which is why `domcheck` reported zero while they existed.
//
// Widening either input beyond 8 bits invalidates this invariant and needs a
// clamp added back with a driven case that reaches it.
assign h_q = {24'd0, cached_lines} * {24'd0, bits_per_line};
assign s_q = {24'd0, outstanding} * {24'd0, bits_per_request};
assign holding_bits = h_q[15:0];
assign serving_bits = s_q[15:0];
// The ratio, scaled by ten so one decimal survives integer division. A
// serving cost of zero is reported as the maximum rather than dividing.
//
// THIS clamp is not dead. holding_bits x 10 reaches 650,250 at a serving cost
// of one bit, an order of magnitude past the 16-bit destination, and the
// stimulus drives that corner.
assign r_q = (serving_bits == 16'd0) ? 32'd65535
: (({16'd0, holding_bits} * 32'd10) / {16'd0, serving_bits});
assign ratio_x10 = (r_q > 32'd65535) ? 16'd65535 : r_q[15:0];
// The truth: the holding obligation is the larger one whenever it is larger.
assign holding_costs_more = (holding_bits > serving_bits);
// The whole review point: a reader who calls the two costs comparable.
assign reported_comparable = (STORAGE_IS_COMPARABLE != 0) ? 1'b1 : !holding_costs_more;
// SAFETY-OF-CLAIM VIOLATION: the costs were called comparable while one is
// strictly larger than the other.
assign store_err = assess && reported_comparable && holding_costs_more;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_assessments <= 8'd0; n_understated <= 8'd0;
end else if (assess) begin
n_assessments <= n_assessments + 8'd1;
if (store_err) n_understated <= n_understated + 8'd1;
end
end
endmoduleThe measurement. Sixty-four held lines at four bits each, against eight outstanding requests at six bits:
64 lines x 4 bits vs 8 outstanding x 6 bits : holding=256 serving=48 ratio=5.3Holding costs state per LINE HELD. Serving costs state per outstanding REQUEST — a much smaller number, and one that does not scale with capacity. A claim that the two are the same is a claim that these two products are the same, and at these illustrative parameters they differ by a factor of 5.3.
All widths and depths here are illustrative parameters. None is a CXL figure and no register layout from any specification appears. The shape of the result is the durable part: one obligation scales with what the device holds and the other with what is in flight.
The run drives the equal case too — twelve lines of four bits is exactly 48 — and the honest build correctly reports that comparable is the right word there. A weak build that is wrong everywhere teaches nothing; this one is wrong in exactly the state the claim is about.
Why the belief is reasonable. Both numbers are "some flops in the device", and neither appears on a data sheet.
What it costs to hold. An area budget short by the difference, agreed before anybody multiplied.
What to say instead. "Lines times bits-per-line against outstanding times bits-per-request, in the same units, with both numbers stated."
Figure 1 — the only box the two paths share is the one the misconception is built on. Everything to the left of it differs, and none of it is visible in a diagram that draws only the payload.
10. Test 5 — Was There Data, Or Was There None?
The claim under test. That the failures are the same failure.
What identity would require. That the signatures were indistinguishable.
The failure. When either protocol is wrong the application sees "bad data" or "a hang", which is why they get conflated. The signatures are opposite. A holding protocol fails by returning a value that was correct once and is not correct now — a stale copy, data present and wrong. A serving protocol fails by not returning anything — an unserved access, no data at all.
// RTL 5 - two different failures that look the same from the top.
//
// When either protocol is wrong the application sees "bad data" or "a hang",
// which is why they get conflated. The SIGNATURES are opposite. A holding
// protocol fails by returning a value that was correct once and is not correct
// now - a stale copy, with data present and wrong. A serving protocol fails by
// not returning anything - an unserved access, with no data at all. Present-
// and-wrong and absent are the two ends of the diagnosis, and treating them as
// one symptom sends the investigation to the wrong layer.
//
// BAD : "the link is returning bad data"
// GOOD : was there data? then it is a staleness problem. was there none?
// then it is a service problem. They are different sub-protocols.
//
// TEACHING MODEL. Two illustrative failure flags; no error code, status bit or
// reporting register from any specification appears.
//
// INITIALIZATION CONTRACT (combinational + counters):
// power-on/reset : counters zero; signature_id is a pure function
// re-initialise : not applicable
// telemetry : signature_id is the field a triage script reads first
module failure_signature #(parameter int BAD_DATA_IS_BAD_DATA = 0) (
input logic clk, rst_n,
input logic assess,
input logic stale_copy, unserved_access,
output logic [7:0] signature_id, n_failures, n_misrouted,
output logic signatures_differ, contradictory, agrees_signature,
output logic sig_err
);
// 0 none, 1 stale copy, 2 unserved access, 3 both at once.
assign signature_id = {6'd0, unserved_access, stale_copy};
// The truth: the two signatures are distinguishable whenever exactly one of
// them is present.
assign signatures_differ = stale_copy ^ unserved_access;
// Both at once is CONTRADICTORY evidence and isolates nothing - the same
// shape 30.7 section 15 found in layer triage. It is published separately
// rather than folded into the test above, because a reviewer needs to tell
// "no signature yet" from "two signatures, so something is wrong with the
// measurement".
assign contradictory = stale_copy && unserved_access;
// The whole review point: a reader who routes both to one investigation.
assign agrees_signature = (BAD_DATA_IS_BAD_DATA != 0) ? 1'b1 : !signatures_differ;
// SAFETY-OF-CLAIM VIOLATION: two distinguishable signatures were treated as
// one, which sends the investigation to a single layer.
assign sig_err = assess && agrees_signature && signatures_differ;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_failures <= 8'd0; n_misrouted <= 8'd0;
end else if (assess) begin
n_failures <= n_failures + 8'd1;
if (sig_err) n_misrouted <= n_misrouted + 8'd1;
end
end
endmoduleThe measurement. A stale copy:
a stale copy : signature=1 differ=1 contradictory=0 bad_data_is_bad_data_says=1Present-and-wrong and absent are the two ends of the diagnosis, and treating them as one symptom sends the investigation to the wrong layer on half the failures.
Both signatures at once is contradictory evidence
The run drives that case, and the honest model reports it as not distinguishable rather than as two findings. A failure that is simultaneously "data present and wrong" and "no data at all" is a measurement problem, and it is the same shape 30.7 section 15 found in layer triage.
The model published that fact twice at first, under two names, and section 18 records how the redundancy was found and what replaced it.
Why the belief is reasonable. From the application's point of view both are "the link is broken", and the application is where the report comes from.
What it costs to hold. A triage script with one branch, and an investigation that starts in the wrong place on half the incidents.
What to say instead. "Was there data? Then it is a staleness problem. Was there none? Then it is a service problem. Different sub-protocols."
11. Test 6 — Run The Substitution
The claim under test. Identity itself.
What identity would require. That a swap changed nothing.
The direct test. Give a workload that needs A the interface that implements B, and count what is served. If the two were interchangeable the count would be unchanged.
// RTL 6 - the substitution test.
//
// The direct test of "A and B are identical" is substitution: give a workload
// that needs A the interface that implements B, and count what is served. If
// the two were interchangeable the count would be unchanged. It is not. The
// test is cheap, it is decisive, and it is the one people skip because the two
// protocols appear side by side in every diagram.
//
// BAD : compare the two descriptions
// GOOD : substitute one for the other and count the unserved requests
//
// TEACHING MODEL. Sequential.
// State remembered : how many requests have been served and how many have not.
// Safety : a request that the provided protocol cannot serve is
// counted as unserved rather than silently dropped.
//
// INITIALIZATION CONTRACT:
// power-on/reset : both counters zero
// initialisation : the counters accumulate over one substitution trial
// re-initialise : `fresh_trial` zeroes both and is legal at any time; it
// is how a second substitution is measured without
// carrying the first one's totals, and it DOMINATES a
// simultaneous request, because a trial boundary that a
// request could cross would mix two measurements
// telemetry : unserved must read zero for a substitution to be sound
module substitution_test #(parameter int EITHER_WILL_DO = 0) (
input logic clk, rst_n,
input logic request, needs_a, provides_a, fresh_trial, assess,
output logic [7:0] served, unserved, n_trials,
output logic would_serve, reported_served,
output logic sub_err
);
logic [7:0] srv_q, uns_q, trial_q;
assign served = srv_q;
assign unserved = uns_q;
assign n_trials = trial_q;
// The truth: a request is served when the protocol provided is the protocol
// the request needs. Needing B and being given B serves too - the test is
// about a MATCH, not about which protocol is better.
assign would_serve = (needs_a == provides_a);
// The whole review point: a reader for whom either protocol serves anything.
assign reported_served = (EITHER_WILL_DO != 0) ? 1'b1 : would_serve;
// SAFETY VIOLATION: a request was reported served by a protocol that cannot
// serve it.
assign sub_err = assess && reported_served && !would_serve;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
srv_q <= 8'd0; uns_q <= 8'd0; trial_q <= 8'd0;
end else begin
// ONE assignment each, priority written down: a trial boundary dominates
// a request arriving in the same cycle.
if (fresh_trial) srv_q <= 8'd0;
else if (request && would_serve) srv_q <= (srv_q == 8'hFF) ? srv_q : srv_q + 8'd1;
else srv_q <= srv_q;
if (fresh_trial) uns_q <= 8'd0;
else if (request && !would_serve) uns_q <= (uns_q == 8'hFF) ? uns_q : uns_q + 8'd1;
else uns_q <= uns_q;
if (fresh_trial) trial_q <= (trial_q == 8'hFF) ? trial_q : trial_q + 8'd1;
end
end
endmoduleThe measurement. A request that needs A, given B:
needs A, given B : served=0 unserved=1 either_will_do_says=1Nothing served, one unserved, and the either-will-do build reports it served. The run drives the mirror substitution too — needing B and being given A — and it fails the same way, which matters: the test is about a MATCH, not about which protocol is better. Needing B and being given B serves.
The trial boundary is explicit. A fresh trial dominates a request arriving in the same cycle, because a boundary a request could cross would mix two measurements into one number.
Why the belief is reasonable. The test is cheap and nobody runs it, because the two protocols appear side by side in every diagram and the diagram is the thing everybody has.
What it costs to hold. A device selected on the wrong protocol, and a substitution nobody measured until integration.
What to say instead. "Substitute one for the other and count the unserved requests. It takes an afternoon."
12. Test 7 — Using Both Is Two Obligations
The claim under test. That a device using both is using one thing twice.
What identity would require. That the obligation count did not move.
// RTL 7 - a device that uses both, and what that costs it.
//
// The cleanest refutation of "they are identical" is that a device can use
// BOTH, and that doing so gives it BOTH sets of obligations rather than one.
// If the two were the same protocol, using both would be using one twice and
// the obligation count would not move. It moves.
//
// BAD : "it supports both, so it is the same thing twice"
// GOOD : count the obligations each one creates, and add them
//
// TEACHING MODEL. Two illustrative usage flags and an obligation count.
//
// INITIALIZATION CONTRACT (combinational + counters):
// power-on/reset : counters zero
// re-initialise : not applicable
// telemetry : obligations_count is published, so a device that
// declares both and implements one is visible
module both_at_once #(parameter int BOTH_IS_ONE_TWICE = 0) (
input logic clk, rst_n,
input logic assess,
input logic uses_a, uses_b, implements_a, implements_b,
output logic [7:0] obligations_count, implemented_count, n_devices, n_short,
output logic both_obligations, fully_implemented, reported_ready,
output logic both_err
);
// Each protocol in use creates its own obligation. If they were one protocol
// this sum would be a maximum rather than an addition, which is exactly the
// weak build's reading.
assign obligations_count = (BOTH_IS_ONE_TWICE != 0)
? ((uses_a || uses_b) ? 8'd1 : 8'd0)
: ({7'd0, uses_a} + {7'd0, uses_b});
assign implemented_count = {7'd0, (uses_a && implements_a)}
+ {7'd0, (uses_b && implements_b)};
assign both_obligations = uses_a && uses_b;
// The truth: a device is ready when it implements every protocol it uses.
assign fully_implemented = (!uses_a || implements_a) && (!uses_b || implements_b);
// The whole review point: the weak build counts one obligation, so a device
// implementing one of two looks complete.
assign reported_ready = (BOTH_IS_ONE_TWICE != 0)
? (implemented_count >= obligations_count)
: fully_implemented;
// SAFETY VIOLATION: a device was called ready with an obligation unmet.
assign both_err = assess && reported_ready && !fully_implemented;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_devices <= 8'd0; n_short <= 8'd0;
end else if (assess) begin
n_devices <= n_devices + 8'd1;
if (both_err) n_short <= n_short + 8'd1;
end
end
endmoduleThe measurement. A device that uses both and implements one:
uses both, implements one : obligations=2 implemented=1 one_twice_says=1Two obligations, one met. The one-twice build counts a single obligation, sees one implementation, and calls the device ready. If the two were the same protocol, using both would be using one twice and the count would not move. It moves.
The run drives the case that isolates the uses term: a protocol implemented and not used. Counting implementations without it would credit a device for a protocol it does not run — and a mutation found that exact gap.
Why the belief is reasonable. "Supports both" appears on a feature list as one line, and a feature list has no column for obligations.
What it costs to hold. A readiness assessment that scores a device complete on half its obligations.
What to say instead. "Count the obligations each protocol creates, and add them. Two protocols in use is two obligations."
13. Test 8 — What Does Each Path Wait For?
The claim under test. That the latency is about the same.
What identity would require. That both paths waited for the same thing.
// RTL 8 - two different critical paths.
//
// The two protocols wait for different things. One waits for an ownership
// question to be resolved somewhere else in the system; the other waits for a
// memory access to complete on the device. Those are different numbers with
// different scaling behaviour, and a design that budgets one for the other is
// wrong by their difference on every transaction.
//
// BAD : "the latency is about the same"
// GOOD : name what each one waits FOR, then compare the two numbers
//
// TEACHING MODEL. All latencies are illustrative integers in arbitrary units.
// None is a CXL or PCIe figure and none is attributed to any product.
//
// INITIALIZATION CONTRACT (combinational + counters):
// power-on/reset : counters zero
// re-initialise : not applicable
// telemetry : both path latencies are published separately, which is
// what makes a budget reviewable
module latency_path #(parameter int ONE_BUDGET_FITS_BOTH = 0) (
input logic clk, rst_n,
input logic evaluate,
input logic [7:0] ownership_lat, memory_lat, budget,
output logic [15:0] path_a, path_b, gap,
output logic [7:0] n_evals, n_missed,
output logic both_fit, reported_fit,
output logic lat_err
);
// Path A waits for the ownership question; path B waits for the memory
// access. Each is its own number, and the gap between them is the amount by
// which a single budget is wrong.
assign path_a = {8'd0, ownership_lat};
assign path_b = {8'd0, memory_lat};
assign gap = (path_a > path_b) ? (path_a - path_b) : (path_b - path_a);
// The truth: a single budget holds only when BOTH paths fit inside it.
assign both_fit = (path_a <= {8'd0, budget}) && (path_b <= {8'd0, budget});
// The whole review point: budgeting the shorter path and assuming the other
// one is the same.
assign reported_fit = (ONE_BUDGET_FITS_BOTH != 0)
? (path_b <= {8'd0, budget}) : both_fit;
// SAFETY-OF-CLAIM VIOLATION: a budget was accepted with a path outside it.
assign lat_err = evaluate && reported_fit && !both_fit;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_evals <= 8'd0; n_missed <= 8'd0;
end else if (evaluate) begin
n_evals <= n_evals + 8'd1;
if (lat_err) n_missed <= n_missed + 8'd1;
end
end
endmoduleThe measurement. An ownership path of 90 against a memory path of 30, with a budget of 40:
ownership 90, memory 30, budget 40 : path_a=90 path_b=30 gap=60 one_budget_says=1One path waits for an ownership question to be resolved somewhere else in the system; the other waits for a memory access to complete on the device. Those are different numbers with different scaling behaviour, and a design that budgets one for the other is wrong by their difference on every transaction — here, by 60.
All latencies are illustrative integers in arbitrary units. None is a CXL or PCIe figure and none is attributed to any product.
The run drives both boundary cases — a budget exactly equal to the longer path, which holds, and one unit under, which does not — and then swaps the paths, where the one-budget build happens to measure the longer one and is right by accident. It budgets one path, not the worst one, and that distinction is the difference between a rule and a coincidence.
Why the belief is reasonable. Both numbers are "the latency of the link", and only one of them is usually measured.
What it costs to hold. A latency budget wrong by the gap, on every transaction of one of the two protocols.
What to say instead. "Name what each path waits FOR, then compare the two numbers. A single budget has to hold the worse one."
14. Test 9 — Write Down What Would Have To Be True
The claim under test. All of them, at once.
What identity would require. Four conditions, and all four.
| Condition | Would have to be true |
|---|---|
| the initiator is the same | both are started by the same side of the link |
| neither obliges state the other does not | the two device-side storage products are equal |
| the failure signatures are the same | present-and-wrong and absent are indistinguishable |
| a substitution serves every request | swapping them changes no count |
// RTL 9 - what would have to be true for "identical" to hold?
//
// Same discipline, third chapter running. Write the conditions, check them,
// count. Four are enough here: the initiator is the same on both, neither
// obliges state the other does not, the failure signatures are the same, and
// a substitution serves every request. Any one of the four failing settles it.
//
// BAD : argue about whether they are the same
// GOOD : list what would have to be true, and count how many are
//
// TEACHING MODEL. Four illustrative booleans.
//
// INITIALIZATION CONTRACT (combinational + counters):
// power-on/reset : counters zero
// re-initialise : not applicable
// telemetry : met_pct beside the conjunction, so 75 percent is
// visibly still false
module identical_conditions #(parameter int MOSTLY_THE_SAME = 0) (
input logic clk, rst_n,
input logic assess,
input logic same_initiator, same_device_state,
input logic same_failure_signature, substitution_serves_all,
output logic [7:0] conditions_met, n_assessments, n_overclaims,
output logic [15:0] met_pct,
output logic would_hold, claimed_holds,
output logic ident_err
);
logic [31:0] m_q;
assign conditions_met = {7'd0, same_initiator} + {7'd0, same_device_state}
+ {7'd0, same_failure_signature} + {7'd0, substitution_serves_all};
// No clamp: four one-bit values over four cannot exceed a hundred.
assign m_q = ({24'd0, conditions_met} * 32'd100) / 32'd4;
assign met_pct = m_q[15:0];
assign would_hold = (conditions_met == 8'd4);
assign claimed_holds = (MOSTLY_THE_SAME != 0) ? (conditions_met >= 8'd3) : would_hold;
assign ident_err = assess && claimed_holds && !would_hold;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_assessments <= 8'd0; n_overclaims <= 8'd0;
end else if (assess) begin
n_assessments <= n_assessments + 8'd1;
if (ident_err) n_overclaims <= n_overclaims + 8'd1;
end
end
endmoduleThe measurement. Three of four met:
3 of 4 conditions : met=75% would_hold=0 mostly_the_same_says=1Seventy-five percent, and a conjunction has no partial credit. Any one of the four failing settles it, and in practice all four fail.
What to say instead. "Here are the four things that would have to be true. None of them is, and the substitution test alone settles it."
15. The Misconception Assembled
Nine tests, one summary.
// RTL 10 - the misconception examined. Nine tests, one summary.
// "They carry the same payload" is bit 0: a true statement about a wire, and
// one sixth of an argument about two protocols.
module ident_review_signoff #(parameter int SAME_PAYLOAD_IS_PROOF = 0) (
input logic clk, rst_n,
input logic review,
input logic same_payload, initiator_named, device_state_counted,
input logic signatures_separated, substitution_run, conditions_checked,
output logic [5:0] fail_mask,
output logic [15:0] conditions_met, sound_pct,
output logic sound,
output logic [7:0] n_reviews, n_sound, n_claimed,
output logic mis_err
);
logic [31:0] s_q;
logic truly_sound, claimed;
assign fail_mask[0] = ~same_payload;
assign fail_mask[1] = ~initiator_named;
assign fail_mask[2] = ~device_state_counted;
assign fail_mask[3] = ~signatures_separated;
assign fail_mask[4] = ~substitution_run;
assign fail_mask[5] = ~conditions_checked;
assign conditions_met = {15'd0, same_payload} + {15'd0, initiator_named}
+ {15'd0, device_state_counted} + {15'd0, signatures_separated}
+ {15'd0, substitution_run} + {15'd0, conditions_checked};
assign s_q = ({16'd0, conditions_met} * 32'd100) / 32'd6;
// No clamp: six one-bit values over six cannot exceed a hundred.
assign sound_pct = s_q[15:0];
assign truly_sound = (fail_mask == 6'd0);
assign claimed = (SAME_PAYLOAD_IS_PROOF != 0) ? same_payload : truly_sound;
assign sound = claimed;
assign mis_err = review && !truly_sound && claimed;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_reviews <= 8'd0; n_sound <= 8'd0; n_claimed <= 8'd0;
end else if (review) begin
n_reviews <= n_reviews + 8'd1;
if (truly_sound) n_sound <= n_sound + 8'd1;
if (claimed) n_claimed <= n_claimed + 8'd1;
end
end
endmoduleThe measurement. Two views of the same argument:
the substitution was never run : mask=010000 met=5 sound=83%
they carry the same payload : mask=111110 met=1 sound=16%The first line is a serious argument with one condition unmet — bit 4, the substitution was never run. And that single unmet condition is the decisive one, which is the point worth taking: the cheapest test in the chapter is the one that was skipped.
The second line is the misconception itself. Bit 0 is clear — they do carry the same payload — and nothing else was checked. Sixteen percent of an argument, from a true statement about a wire.
Figure 3 — bit 4 is the cheapest of the six and the one most often unmet. A substitution takes an afternoon and settles the question on its own.
16. Quantitative Reasoning
Every figure here is a teaching parameter or a value derived from one and asserted by the testbench. None is a measurement of a real system, and none is a figure from any specification.
Initiators. Two protocols and two possible initiators give four combinations, of which two have the initiators agreeing and two have them opposite. The run drives all four; the same-mover build conflates on the two that differ — 50 percent wrong by construction, and wrong on exactly the pair the chapter is about.
Device state, derived. Sixty-four held lines at four bits each is 256 bits. Eight outstanding requests at six bits each is 48 bits. The ratio is 256 × 10 / 48 = 53, which is 5.3 with one decimal preserved by the scaling. The general form is the point: holding cost is lines × bits-per-line and scales with capacity; serving cost is outstanding × bits-per-request and scales with depth. Two products with different multiplicands do not become the same product because both are small.
The equal case, derived. Twelve lines of four bits is 48, exactly the serving cost, and the ratio is 1.0. That case is driven deliberately so that the honest build can be seen agreeing with the weak one — a model that disagrees everywhere is not teaching a distinction, it is asserting one.
The 16-bit corner. 255 lines × 255 bits = 65,025, the largest product two 8-bit operands can make, and a 16-bit destination holds 65,535. The case is driven and asserted, and section 18 records why the clamp that used to guard it was deleted.
Failure signatures. Two boolean failure flags give four states: none, stale only, unserved only, and both. The signatures are distinguishable in two of the four and contradictory in one. A triage script with a single branch is therefore right on the two distinguishable cases and wrong about which of them it is — which is the worst kind of wrong, because it produces a confident answer.
Substitution, derived. Four requests driven — needs-A given-B, needs-A given-A, needs-B given-B, needs-B given-A — produce two served and two unserved. If the protocols were interchangeable the unserved count would be zero, and it is not, which is the entire test in one number.
Obligations, derived. Two protocols in use is 1 + 1 = 2 obligations. The one-twice build computes max(uses_a, uses_b) = 1. A device implementing one of two then looks complete at 1 ≥ 1, and it is short by exactly the obligation nobody counted.
Latency, derived. An ownership path of 90 and a memory path of 30 give a gap of 60, and a budget of 40 holds the second and not the first. A single budget is wrong by the gap on every transaction of the protocol it did not measure. The boundary is a budget of exactly 90, which holds, and 89, which does not.
Conditions, derived. Four conditions with three met is 3 × 100 / 4 = 75 percent, and identity requires four. In practice all four fail, so the claim is not a near miss — it is wrong on every axis it is tested on.
The sign-off arithmetic. Six conditions; five met is 5 × 100 / 6 = 83 percent under integer division, and one met is 100 / 6 = 16 percent.
17. Verification Method
Order of work
names.txt→ ten models, each compiled alone → model-expressiveness review → boolean-tautology review → width review → structural gates → testbench → legal baseline → PASS → mutation campaign → re-baseline after every change → MDX assembled from the verified sources
A mutation campaign on a failing baseline is invalid, and both campaigns in this chapter ran against a green one.
Independent oracles
| Model | Oracle |
|---|---|
| initiator side | device-initiated A, host-initiated B → 1 and 2, not the same |
| ownership tracking | one line held → tracking owed; take+drop → no change; drop at zero → saturates |
| snoop obligation | holds and interrogated → owed 1, must answer; flush → cancels |
| state per line | 64 × 4 = 256; 8 × 6 = 48; ratio 5.3 |
| failure signature | stale only → signature 1; unserved only → 2; both → 3, contradictory |
| substitution | needs A given B → 0 served, 1 unserved; needs B given B → served |
| both at once | uses both, implements one → 2 obligations, 1 met, not ready |
| latency path | 90 and 30 against 40 → gap 60, budget does not hold both |
| conditions | 3 of 4 → 75 percent, does not hold |
| sign-off | five of six → 83 percent; one of six → 16 percent |
chkv prints got against expected, which is what lets an oracle be wrong out loud. In this chapter it caught one, mine, recorded in section 18.
X and Z rejected explicitly
chk(c, …) tests c !== 1'b1, so an X-valued condition fails rather than passing. chkv(got, exp, …) reduces the result and reports an explicit X/Z failure before comparing. The scripted output-connectivity gate this batch introduced returns zero on all ten models.
Pulses are latched, never sampled
Every evidence output — init_err, own_err, snoop_err, store_err, sig_err, sub_err, both_err, lat_err, ident_err, mis_err — is caught by a continuous always @(posedge clk) monitor into a sticky bit, asserted outside any conditional, in a window containing a clock edge.
Stimulus never lands on the active edge, and reset is released after it
step_clk is @(posedge clk); #1;, and reset release lands one delta after the edge.
Initialisation contracts are stated, not implied
Three models in this chapter carry real sequential state, and each one's header answers the same questions explicitly: what the power-on state is, what the initialising event is, whether re-initialisation is legal while live, what it does to state that is already there, and what telemetry proves it happened.
The ownership model's flush dominates a simultaneous take. The obligation model's flush cancels an outstanding answer — the one place a recovery action changes something in flight. The substitution model's trial boundary dominates a request. All three priorities are written into the source and all three are driven by the run.
Both builds are always instantiated
Every model has both its honest and its shortcut build wired to the same stimulus, and the testbench asserts the internal figures on both. In nine of the ten, the shortcut build computes the honest figure internally and reports a different conclusion from it.
Safety, liveness and performance kept apart
Safety — a device that holds a line always answers an interrogation. A request that the provided protocol cannot serve is counted as unserved rather than silently dropped. A device is never called ready with an obligation unmet. None requires an assumption.
Liveness — nothing in this chapter is a liveness claim.
Performance — one model is explicitly a performance claim, and it is marked as one: the latency comparison is about budgets, not about correctness, and a path outside its budget is slow rather than broken.
18. Baseline Defects Found Before Mutation
RTL defects — one, found by the pre-simulation boolean review
A redundant term dressed as a second test. The failure-signature model published both signatures_differ = stale ^ unserved and distinguishable = signatures_differ — the second an exact rename of the first. It was found before any simulation ran, by the §11 review asking what input makes the two differ. Nothing does.
The replacement is a better model as well as a correct one. contradictory = stale && unserved is genuinely independent: it separates "no signature yet" from "two signatures at once, so something is wrong with the measurement" — the same shape 30.7 section 15 found in layer triage, and a case the rename could never have expressed.
Testbench defects — none. Wrong oracles — one, mine.
A check that inherited its inputs from the line above it. The degenerate-device case read two of the four inputs and relied on the other two still being zero from an earlier case. A case inserted between them changed one, and the expected obligation count of zero became one.
It is the same class as every wrong oracle in batches 030 and 031 — a when error rather than an arithmetic one — but about source order rather than clock edges. A check that does not set every input it depends on is a check about the line above it. Every input in that case is now written explicitly.
Coverage gaps found by the structural gates
| Gate | Finding | Closed by |
|---|---|---|
outscan | 6 unasserted output nets, all on the weak build | value assertions on all six |
banned, excheck, splitcheck, domcheck, displaycheck, simwrite, xscan | none | — |
Two dead clamps that domcheck could not see
The campaign found the holding-cost clamp unkillable, and the reason is a width bound: an 8-bit by 8-bit product reaches 255 × 255 = 65,025 and the 16-bit destination holds 65,535, so the clamp's true branch has no reachable input. The identical clamp on the serving cost is dead for the same reason.
Both were deleted and the invariant written into the source, which is 30.5 section 20's rule. The model's own comment had defended them as future-proofing against a widened parameter — a production argument, not a teaching one — and that comment is now a stated precondition instead.
domcheck reported zero on this chapter while both were present. It models domination between a guard and an enclosing condition; this unreachability comes from operand width, which is outside its model. Eighth distinct instance in this track of a structural tool reporting a confident zero on something it could not see.
The third clamp in the same model is live and was kept. The ratio reaches 65,025 × 10 = 650,250, an order of magnitude past its 16-bit destination, and the stimulus now drives that corner and asserts the saturated value. A clamp is dead or live as a function of its own operand widths, and each one has to be computed rather than assumed.
Compiler-warning findings
Under -Wall the ten models produce no truncation, select-width, signedness, cast, multiplication-width, shift-width or loop-width warnings.
Five width results were reasoned rather than trusted.
cached_lines × bits_per_lineandoutstanding × bits_per_request: 8-bit by 8-bit in a 32-bit context, maximum 65,025, assigned to 16-bit nets that hold 65,535. No clamp needed, and the campaign proved it by making one unkillable.holding_bits × 10 / serving_bits: the numerator reaches 650,250, which is why this one does need its clamp.held_q + 1andowed_q + 1saturate at8'hFFexplicitly;held_q − 1andowed_q − 1saturate at zero. All four saturations are driven and asserted, because an 8-bit counter that wraps from 0 to 255 is the difference between "nothing held" and "everything held".srv_q + 1anduns_q + 1saturate identically.conditions_met × 100 / 4and/ 6cannot exceed 100: a sum of one-bit values over its own denominator.
Simulator constraints
Icarus Verilog 13.0 rejects ref task arguments, carried forward.
19. Mutation Testing
73 mutations attempted, 73 non-equivalent, 73 killed. Zero unexplained survivors, zero equivalents withdrawn.
| Reported separately | Count |
|---|---|
| Mutants attempted | 73 |
| Withdrawn as equivalent | 0 |
| Non-equivalent mutants | 73 |
| Killed | 73 |
| Unexplained survivors | 0 |
| Model | Dimension | Muts |
|---|---|---|
| m1 | initiator side | 6 |
| m2 | ownership tracking | 7 |
| m3 | snoop obligation | 8 |
| m4 | state per line | 8 |
| m5 | failure signature | 6 |
| m6 | substitution test | 8 |
| m7 | both at once | 7 |
| m8 | latency path | 7 |
| m9 | conditions | 6 |
| m10 | review sign-off | 10 |
Three survivors across the first runs, every one classified before anything was changed.
One was dead code, and the response was to delete it
The holding-cost clamp's mutation could not be killed because the clamp can never fire. Deleted, with the bound written down — not withdrawn, and not asserted around. The mutation attacking it was retargeted to the live clamp in the same model, where it dies.
Two were the conjunction family
| Term isolated | Driven, and not driven |
|---|---|
| the held term of the answer obligation | driven: every interrogation followed a line being taken. Not driven: an interrogation with nothing held |
| the uses term of the implementation count | driven: four devices with implements zero wherever uses was zero. Not driven: a protocol implemented and not used |
Both are one term of a conjunction driven false while the others are true — the case that isolates the term the mutation removes.
For every conjunction, drive each term false with the others true.
That rule has now produced findings in every chapter of batches 031 and 032. It is the highest-yield stimulus rule the track has.
The classification rule
Never add an assertion for a survivor before classifying it.
| Class | Means, and what to do |
|---|---|
| Equivalent | no input tells the two apart — withdraw it, never count a kill |
| Stimulus gap | the case is never driven — extend the stimulus |
| Missing checker | the case is driven and nothing looks — add the checker |
| Vacuous checker | the check cannot fail — fix the check, not the design |
| Model cannot express it | the decisive experiment has no representation — rebuild the model |
| Model ambiguity | the model has not decided what it means — decide, then re-mutate |
| Dead code | the guard has no reachable input — delete it and write the invariant down |
| Unreachable | its guard never holds — fix the guard |
| Masked | another mechanism hides it — expose it, or say why you cannot |
| Coincidental | the arithmetic happens to agree — change the stimulus |
| Missing config | the build that differs is never built — instantiate it |
| Other | anything else — state it precisely |
20. Synthesis And Implementation Reality
These models are not meant to be synthesised. What follows is the honest reading of what the structures they abstract would cost, because the storage difference is the chapter's central quantitative claim and it deserves real numbers.
Per-line ownership tracking is the expensive obligation, and it scales with capacity. Every line the device may hold needs a record. At 64 lines and 4 bits of state each that is 256 flops; at 1,024 lines it is 4,096, and the structure is an associative lookup rather than a flat array if the device has to find a line by address. The lookup is the part that costs more than the storage.
Per-request serving state is the cheap obligation, and it scales with depth rather than capacity. Eight outstanding requests at 6 bits is 48 flops, and it does not grow when the device presents more memory. A device can present a great deal of memory with a very small serving structure, which is exactly why the two products are not comparable.
The answering path is a structure, not a signal. A device that can be interrogated needs a path from the interrogation to the tracking structure, a lookup, and a response generator with its own flow control. It is the single largest thing the misconception hides, because a block diagram drawn for the serving protocol has nowhere to put it.
The two critical paths are physically different. One waits on a lookup plus a round trip to wherever ownership is resolved; the other waits on a memory access on the device. They will not close at the same frequency and they do not pipeline the same way, which is the implementation form of section 13.
A saturating counter costs a comparator and a mux. Every counter in this chapter saturates explicitly rather than wrapping, and the campaign kills every mutation that removes one. Two gates against the difference between "holds nothing" and "holds everything" is not a trade anybody should think about.
No area, frequency or power figures appear in this chapter, because none was measured.
21. Silicon Observability
| Telemetry | What it would settle |
|---|---|
| lines currently held, and a high-water mark | whether the tracking structure is sized for what the device actually holds |
| outstanding answers owed, and a high-water mark | an interrogation path that is falling behind, before it times out |
| answers owed at the moment of a flush | how often a recovery cancels an obligation rather than discharging it |
| stale-copy and unserved-access counts, separately | which of the two failure signatures this incident is |
| served and unserved request counts per protocol | a substitution's result, measurable in production rather than in a lab |
| obligations declared against obligations implemented | a device that advertises both protocols and implements one |
| ownership-path and memory-path latency, separately | which of the two a budget was written against |
The fourth is the one that changes a debug session. Present-and-wrong and absent are opposite diagnoses, and a single "errors" counter makes them indistinguishable — which is the observability form of section 10.
The third is the one nobody has. An obligation cancelled by a flush is not a failure, but a device that cancels many of them is a device whose recovery is being used as flow control, and there is no other way to see it.
Two counters here must read permanently zero — unserved requests on a matched protocol, and answers owed while the device holds nothing. Each costs a comparator, and each catches a class of defect that otherwise presents as an application-level mystery.
22. DebugLabs
These labs debug decisions made from the misconception. The symptom is always a project that went wrong, and the root cause is always two things treated as one because their payloads are the same shape.
Lab 1 — A device works until the host stops driving
Symptom. A device passes every test and fails at a customer the first time it initiates a transaction of its own.
Evidence. Every test in the regression is host-driven. The device's own initiation path has never been exercised under load.
Hypothesis. The design was built around one initiator.
Investigation. Read the arbitration and backpressure design. Both are sized for host-initiated traffic; the device-initiated path shares the structures and was never budgeted.
Root cause. Two protocols with opposite initiators implemented as one, because their payloads are the same shape.
Fix. Separate arbitration and flow control per initiator, and size each.
Prevention. Name the initiator first. Everything downstream — arbitration, backpressure, timeout, retry — is a property of the tail of the arrow.
Observability. Traffic counters per initiator. Zero in one column before the customer finds it.
Lab 2 — An area estimate is short by a structure nobody drew
Symptom. A device's area comes in well over budget late in implementation.
Evidence. The estimate was built from a block diagram. The diagram has a buffer and no tracking structure.
Hypothesis. The per-line record was never counted.
Investigation. Count the lines the device may hold and the bits each needs. 1,024 lines at 4 bits is 4,096 flops plus the lookup.
Root cause. A device that holds lines estimated with the storage model of a device that serves them.
Fix. Re-estimate from the obligation rather than from the diagram.
Prevention. Ask what the device holds, not what it buffers. A buffer is bytes; a record is what the bytes mean.
Observability. Lines held and a high-water mark. The estimate becomes a measurement.
Lab 3 — A device does not answer, and nothing is corrupted
Symptom. A requester waits forever. No data is wrong anywhere. No protocol error is reported.
Evidence. The device holds lines. An interrogation was issued. No response was generated.
Hypothesis. There is no answering path.
Investigation. Trace the interrogation into the device. It arrives and terminates: nothing is connected to it.
Root cause. A device built for the serving protocol and deployed on the holding one. It does not answer slowly; it does not answer.
Fix. Build the path: interrogation to lookup to response, with its own flow control.
Prevention. Ask whether this protocol can interrogate the device, and build the answering path first. It is the largest structure the misconception hides.
Observability. Answers owed, with a high-water mark. A count that only rises is the whole diagnosis.
Lab 4 — Two incidents with one triage branch
Symptom. Half the incidents in a class are misdiagnosed. The other half are diagnosed quickly.
Evidence. The triage script has one branch for "bad data from the link". Both failure modes enter it.
Hypothesis. Two opposite signatures are being routed to one investigation.
Investigation. Separate the two counters. Present-and-wrong and absent are both non-zero, in different incidents.
Root cause. A stale copy and an unserved access treated as one symptom, because the application reports both as "the link is broken".
Fix. Two counters, two branches. Was there data, or was there none?
Prevention. The signatures are opposite and the reports are identical. Instrument the difference the application cannot see.
Observability. Stale-copy and unserved-access counts, separately. One field decides the branch.
Lab 5 — A device was selected on the wrong protocol
Symptom. A device chosen for a workload cannot serve it, and nobody noticed until integration.
Evidence. The selection compared descriptions. No substitution was run.
Hypothesis. The protocols were assumed interchangeable.
Investigation. Run the substitution: give the workload the protocol the device implements, and count. Half the requests are unserved.
Root cause. A comparison of documents rather than a test of substitutability.
Fix. Select against the substitution result.
Prevention. The test takes an afternoon and settles the question on its own. It is skipped because the two appear side by side in every diagram.
Observability. Served and unserved per protocol. The test becomes a production metric rather than a lab exercise.
Lab 6 — A device advertises both and implements one
Symptom. A device declares support for both protocols. One of them does not work.
Evidence. The readiness assessment counted one obligation and found one implementation.
Hypothesis. Using both was scored as using one thing twice.
Investigation. Count the obligations each protocol creates and add them. Two. Count the implementations that match a protocol in use. One.
Root cause. A feature list with one line for "supports both" and a readiness check that counted lines.
Fix. Score obligations, not features, and count only implementations of protocols actually in use.
Prevention. Two protocols in use is two obligations. If they were the same protocol the count would not move.
Observability. Obligations declared against obligations implemented. A device that is short is visible before it ships.
Lab 7 — A latency budget holds for one protocol and not the other
Symptom. A design meets its latency budget in one traffic class and misses it by a wide margin in the other.
Evidence. One budget was written. One path was measured.
Hypothesis. The budget was set against the shorter path.
Investigation. Measure both. The ownership path is three times the memory path.
Root cause. A single budget applied to two paths that wait for different things.
Fix. Two budgets, or one budget that holds the worse path.
Prevention. Name what each path waits FOR. A budget that measured one path is a coincidence when it holds and a defect when it does not.
Observability. The two path latencies, published separately. A budget becomes reviewable.
Lab 8 — A claim survived because nobody wrote it down
Symptom. An organisation treats the two protocols as interchangeable in planning documents, and each counter-example is handled as a special case.
Evidence. Nobody has written the identity claim as a proposition with conditions.
Hypothesis. A sentence is being defended rather than a claim being tested.
Investigation. Write the four conditions. Check them. All four fail.
Root cause. A claim that was never made checkable, so each contradiction was absorbed instead of counting against it.
Fix. The list, in the first meeting.
Prevention. For any identity claim, the substitution test alone settles it. The list is what gets you to run the test.
Observability. The condition list. A claim with no condition list is 30.8's unbounded claim at organisational scale.
23. Coverage Reasoning
Coverage of an argument has the same failure mode as coverage of a design: it measures what was considered, not whether anything was checked.
Four coverage models are worth keeping over any identity claim:
Initiator-combination coverage. Two protocols, two initiators, four cells. The cells that matter are the two where the initiators differ, and a regression built around one traffic direction fills neither.
Substitution coverage. A cross of needs against provides, four cells, of which two are mismatches. A test plan that only drives matched pairs fills half the table and cannot detect a substitution failure at all.
Signature coverage. Two failure flags, four states, including both at once. The contradictory cell is the one a single-branch triage script can never distinguish, and populating it is what proves the branch is insufficient.
Obligation coverage. A cross of uses against implements, per protocol. The cell that isolates the uses term is implemented-and-not-used, and a mutation found it missing from this chapter's own stimulus.
The bin the shortcut build cannot hit is the most valuable bin in any model. In section 7 it is "a line held with no record". In section 8 it is "an answer owed and no path to produce one". In section 11 it is "a request unserved by the protocol provided". Each is unreachable in the shortcut build and trivial in the honest one.
24. How This Appears In Real Engineering
Diagrams draw payloads, not initiators. An arrow between two boxes shows what moves; the thing that decides everything downstream is which end started it, and that is the part of the arrow nobody reads.
Block diagrams have a box for a buffer and no box for a record. The per-line tracking structure is the largest thing the misconception hides, and it is invisible in exactly the artefact most area estimates are built from.
Answering paths are built last, because nothing exercises them until something interrogates the device, and nothing interrogates it until integration.
Triage scripts have one branch for "link errors" because the application's report has one category for them, and the script is written from the report.
Substitution tests are skipped because the two protocols appear together everywhere. Familiarity reads as equivalence, and the test that would settle it looks redundant.
Feature lists have one line for "supports both", and readiness checks are written against feature lists.
Latency budgets are written once and measured once, and the path that gets measured is the one that was easiest to instrument.
And the most durable form: two things with the same payload shape, in the same diagram, on the same link, look like one thing with two names — and nothing in a newcomer's experience interrupts that, because the differences are all in what the device must build rather than in what the link carries.
25. Where The Misconception Comes From
Every observation is true. Both really do carry cache-line-sized payloads, run over the same link, and appear in the same diagrams. There is no false premise to correct.
The differences are all on the device side. What differs is what the device must implement — a record, a lookup, an answering path, a serving path — and none of that is visible from the link, which is where the introductory material looks.
The names are similar and the diagrams are shared. Two boxes side by side with arrows of the same thickness read as two instances of one thing.
The substitution test is cheap and looks redundant. Nobody runs an experiment to confirm something a diagram already shows, and the diagram is exactly what is wrong.
The failure modes report identically. The application says "the link is broken" for both, so even the failures do not separate them for the person debugging.
And one direction of the error is invisible. A device built for the holding protocol deployed on the serving one is over-engineered and works. Only the other direction fails — which means half the mistakes this belief produces never generate evidence against it.
26. Common Misconceptions
"They are basically the same thing." Substitute one for the other and count the unserved requests. The test takes an afternoon.
"They carry the same payload." True, and that is a payload size. Identity is a claim about substitutability.
"They are both on the same link." So are all of them. A link is a shared resource, not an identity.
"An arrow is an arrow." The initiator is the tail. Everything downstream of the initiator differs with it.
"The device just needs a buffer either way." A buffer is bytes. A record of what those bytes mean is what one protocol obliges and the other does not.
"We will add the response path later." There is nothing to add it to if the tracking structure does not exist. A device without an answering path does not answer slowly; it does not answer.
"The storage is comparable." Lines times bits-per-line against outstanding times bits-per-request. Two products with different multiplicands.
"The link is returning bad data." Was there data, or was there none? Those are opposite diagnoses.
"It supports both, so it is the same thing twice." Two protocols in use is two obligations. If they were one, the count would not move.
"The latency is about the same." One waits for an ownership question resolved elsewhere; the other for a memory access on the device. Name what each waits FOR.
"Mostly the same." A conjunction has no partial credit, and all four conditions fail, not three.
"They carry the same payload." That is bit 0, and it is worth one sixth of an argument about two protocols.
27. Interview And Design-Review Questions
The claim and its test
1. What kind of claim is "these two are identical"? A claim about substitutability. None of the usual evidence — same payload, same link, same diagram — is about substituting anything.
2. What is the direct test? Give a workload that needs one the interface that implements the other, and count what goes unserved. Interchangeable things give the same count when swapped.
3. Why is that test almost never run? Because the two appear side by side in every diagram, so confirming a difference feels like confirming something already shown. The diagram is exactly what is wrong.
4. Four requests are driven across two needs and two provisions. How many are served? Two. The two matched pairs serve and the two mismatches do not, which is the whole test in one number.
5. Why does the test have to run in both directions? Because the question is about a match, not about which protocol is more capable. Needing the serving protocol and being given the holding one fails too.
Initiator, state and obligation
6. Name the first axis on which they differ. The initiator. One is started by the device reaching into host memory, the other by the host reaching into device memory.
7. Why does the initiator decide so much? Because arbitration, backpressure, timeout and retry are all properties of the side that starts. Change the initiator and every one of them moves.
8. What state does the holding protocol oblige the device to carry? A per-line record of whether it still holds the line and on what terms, plus the state machine that maintains it.
9. What does the serving protocol oblige instead? State per outstanding request. It does not scale with how much memory the device presents.
10. Sixty-four lines at four bits against eight outstanding at six bits. Give both numbers and the ratio. 256 bits and 48 bits, a ratio of 5.3. Two products with different multiplicands.
11. Which of the two scales with capacity? The holding cost. Presenting more memory does not enlarge the serving structure; holding more lines does enlarge the tracking one.
12. What is the largest structure the misconception hides? The answering path — interrogation to lookup to response, with its own flow control. A diagram drawn for the serving protocol has nowhere to put it.
13. A device that holds a line is interrogated and does not respond. How fast is it? It is not slow. It has no answering path, so no response will ever be generated, and the requester waits forever.
14. When does a flush cancel an obligation rather than discharge it? When the device no longer holds the line the interrogation was about. It is the one place a recovery action changes something in flight, and it belongs in the initialisation contract rather than in the code alone.
Failures, budgets and obligations
15. Give the two failure signatures. A stale copy — data present and wrong. An unserved access — no data at all.
16. Why do they get conflated? Because the application reports both as "the link is broken", and the triage script is written from the application's report.
17. What does a triage script with one branch get right? Half the incidents, by accident, with no way to know which half. A confident answer on a coin flip.
18. Both signatures are present at once. What have you isolated? Nothing. No single failure produces both, so the evidence is contradictory and the measurement is what needs attention.
19. A device uses both protocols and implements one. Is it ready? No. Two protocols in use is two obligations, and it meets one.
20. What does the one-twice reading compute? A maximum instead of a sum. One obligation, one implementation, and a device that looks complete at one greater than or equal to one.
21. What case isolates the uses term in that count? A protocol implemented and not used. Without it, counting implementations credits a device for a protocol it does not run.
22. Name what each critical path waits for. One waits for an ownership question resolved elsewhere in the system; the other for a memory access to complete on the device.
23. Ownership 90, memory 30, budget 40. What holds? Only the memory path. The gap is 60, and a single budget is wrong by that much on every transaction of the other protocol.
24. Where is the boundary in that comparison? A budget exactly equal to the longer path, which holds, and one unit under, which does not.
25. A one-budget design happens to measure the longer path. Is it correct? It is right by accident. It budgets one path, not the worst one, and it fails the moment the two swap.
Method and evidence
26. State the disciplined way to handle an identity claim. Write the conditions under which it would be true, then check them — and for identity, run the substitution, which settles it alone.
27. Give the four conditions. Same initiator, neither obliges state the other does not, the same failure signature, and a substitution that serves every request.
28. Three of four hold. What follows? Nothing is established. In this case all four fail, so the claim is not a near miss.
29. What makes a mutation campaign invalid? A failing baseline. Every mutation then fails for the reason the baseline does.
30. A clamp's mutation cannot be killed. What is the first thing to compute? The maximum value the expression can reach, against its destination width. If it cannot exceed the destination, the clamp is dead code.
31. What is the correct response to dead code found by a campaign? Delete it and write the invariant down. Not an assertion, and not a withdrawal.
32. Why did domcheck report zero while two dead clamps existed? It models domination between a guard and an enclosing condition. These are unreachable because of operand width, which is outside its model.
33. This chapter kept one clamp and deleted two. What distinguishes them? The kept one guards a value reaching 650,250 against a 16-bit destination; the deleted ones guard values that cannot exceed 65,025. A clamp is dead or live as a function of its own operand widths.
34. Name the pre-simulation review that found this chapter's RTL defect. The boolean-tautology review: two published outputs where the second was an exact rename of the first. It was found by asking what input makes them differ.
35. What replaced it, and why is that better? A genuinely independent term — both signatures at once — which separates "no signature yet" from "a contradictory measurement". The rename could not express that case.
36. Why must every counter saturate rather than wrap? Because an 8-bit counter that wraps from 0 to 255 turns "the device holds nothing" into "the device holds everything". Two gates against that inversion is not a trade.
37. What does an initialisation contract have to state? Power-on state, the initialising event, whether re-initialisation is legal while live, what it does to state already present, and the telemetry that proves it happened.
38. Give the three priorities this chapter's sequential models state. A flush dominates a simultaneous take; a flush cancels an outstanding answer; a trial boundary dominates a request.
39. Why does a trial boundary need to dominate? Because a boundary a request could cross mixes two measurements into one number, and the number then describes neither.
40. Your oracle expected zero and got one. Name two causes that are not arithmetic. The check read a value before its edge, or it inherited an input from a line above it that a later insertion changed.
41. How do you stop the second one? Set every input a check depends on, explicitly, in the case that check belongs to.
Saying it well
42. What would you say to a colleague who states the misconception? Give the four axes — initiator, device state, failure signature, critical path — and offer the substitution test. The structure is more interesting than the correction.
43. What is the cheapest sentence that shows you understand the difference? "One is started by the device and obliges it to carry per-line state; the other is started by the host and obliges per-request state."
44. Why is half of this belief's damage invisible? Because a device built for the holding protocol and deployed on the serving one is over-engineered and works. Only one direction of the mistake generates evidence.
45. What does this chapter share with 31.2? Both refute a claim whose premises are all true. One is a sampling error and one is a substitutability error, and neither is fixed by correcting a fact.
46. State the single question this chapter turns on. Substitute one for the other — what goes unserved?
28. Exercises
1 — Verification · Intermediate. Builds: designing the test that settles an identity claim. Specify the substitution experiment for two interfaces you believe are interchangeable. Bounded scope: define the request stream, the two provisions, the counter you would read, and the result that would confirm identity. Hint: state the result that would CONFIRM it, not just the one that would refute it.
2 — Architecture · Intermediate. Builds: reading an interface for its initiator. Take a block diagram of a device attached over a link. Bounded scope: for each arrow, name the initiator, and list what changes downstream — arbitration, backpressure, timeout, retry — when the initiator changes. Hint: the initiator is the tail of the arrow, which is the part diagrams draw smallest.
3 — Design · Advanced. Builds: sizing an obligation instead of a buffer. A device may hold up to 1,024 lines and may have up to 16 requests outstanding. Bounded scope: compute both storage obligations with stated bits-per-entry, say which scales with capacity, and identify the structure that costs more than the storage. Hint: finding a line by address is not a flat array.
4 — Design · Advanced. Builds: specifying the path the misconception hides. Specify the answering path for a device that holds lines. Bounded scope: name every stage from interrogation to response, state where its flow control lives, and say what the device does when the path is backed up. Hint: "it answers slowly" and "it does not answer" are different designs.
5 — Debug · Advanced. Builds: separating two opposite signatures. A class of incidents is reported as "bad data from the link". Bounded scope: give the two signatures, the counter that distinguishes them, the triage branch for each, and what a simultaneous appearance of both would mean. Hint: one of the three outcomes is a statement about the measurement rather than about the link.
6 — Design review · Advanced. Builds: scoring obligations rather than features. Review a device that declares support for both protocols. Bounded scope: write the obligation count, the implemented count, the readiness expression, and the input combination on which a feature-list reading is wrong. Hint: the wrong reading computes a maximum where the right one computes a sum.
7 — Quantitative · Advanced. Builds: budgeting two paths that wait for different things. An ownership path measures 200 units and a memory path 60, against a budget of 100. Bounded scope: state which holds, compute the gap, say what a single budget costs per transaction on the failing path, and give the two budgets you would write instead. Hint: also state what happens to your answer if the two paths swap.
8 — Verification · Expert. Builds: testing a conjunction the way a campaign does. Take a safety condition from your own design written as a conjunction of two or more terms. Bounded scope: for each term, construct the input that drives it false with the others true; identify any term for which no such input is reachable; and say what that unreachability means about the model rather than about the term. Hint: this chapter found exactly that, twice, and the answer was to extend the stimulus — but 31.4 found a case where it was the model that had to change.
29. Summary
Identity is a claim about substitutability, and same payload, same link and same diagram are not about substituting anything.
Run the substitution. Four requests across two needs and two provisions serve two and leave two unserved — and interchangeable things would leave none.
Name the initiator first. One protocol is started by the device and the other by the host, and arbitration, backpressure, timeout and retry all move with it.
Count the state the device is obliged to carry. Holding lines costs state per line and scales with capacity; serving costs state per outstanding request and does not.
A device that holds a line can be asked about it and must answer. A device with no answering path does not answer slowly — it does not answer.
Was there data, or was there none? Present-and-wrong and absent are opposite diagnoses reported identically, and a triage script with one branch is right half the time with no way to know which half.
Two protocols in use is two obligations. If they were one protocol the count would not move.
Name what each critical path waits FOR. One waits on an ownership question resolved elsewhere, the other on a memory access on the device, and a single budget is wrong by their gap.
All four identity conditions fail, so the claim is not a near miss — it is wrong on every axis it is tested on.
Half this belief's damage is invisible, because the over-engineered direction of the mistake works and generates no evidence.
Six conditions, and "they carry the same payload" is one of them. A true fact about a wire, checked correctly, is 16 percent of an argument about two protocols.
Continue learning
Related tutorials
- Related topic
“UCIe Automatically Provides Coherency”
Two dies joined by a perfect zero-error link, each with a cache, are incoherent within one cycle — so the link was never the mechanism. What coherence actually requires, why carrying a coherent protocol is necessary and not sufficient, and the bridge RTL that hands write permission to two agents at once.
- Related topic
“CXL Automatically Solves Coherency”
A mechanism is not a discipline. Nine tests: the division of labour, coherence against consistency, the agent outside the domain, the flag that arrived first, a line that is not a critical section, the recovery path nobody wrote, per-region coverage, the per-access cost, and the four conditions.
- Related topic
Why CXL Matters
Why PCIe transaction semantics are insufficient for coherent memory and accelerator attach — CXL.io, CXL.cache and CXL.mem as the CXL Consortium defines them, device types, why coherence needs distributed per-line state, memory-window routing, what coherence costs, and why a clean transport proves nothing about coherence.
- Related topic
Cache Coherency Over CXL
Why a device caching host memory needs transient state and not just MESI — CXL.cache's three channels each direction, why a tag hit is not permission, the same-line restrictions the specification imposes, the snoop-versus-eviction race, dirty-data ownership, why a coherence timeout cannot restore the previous state, channel-dependency deadlock, and the coherence reference model.
Standards & specifications
- Governing standard
- CXL Specification (CXL Consortium)(opens CXL Consortium in a new tab)
Defines CXL.io, CXL.cache and CXL.mem, and the coherence and memory-pooling behaviour built on them. System design and deployment topology are not mandated.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the CXL curriculum.
