Skip to content
VLSI Mentor

CXL · Module 9

CXL.mem Read Flows

The read path end to end: four ordered stages, exactly one of three sources, same-line merging that must not merge responses, out-of-order returns that keep their identity, and partial reads that fail only on bytes they asked for. Seven RTL models simulated, twenty-one mutations, twenty-one killed.

Chapter 9.3 established what the access must guarantee. This chapter and the next walk the two flows that deliver those guarantees. The read is the simpler of the two — and it is still four stages, three possible sources and two ways to be wrong about identity.

1. The Engineering Problem — A Read Is a Question

A read asks: what is the value at this address? The device owes exactly one answer, and the answer must be

PropertyMeaning
completethe request is answered, not abandoned
singularanswered once, not twice
sourcedfrom exactly one of media, a pending write, or an error
identifieddelivered to the requester that asked

Each of those is a separate way to be wrong, and three of the four fail silently — an unanswered read looks like a hang, but a doubly-answered read, a wrongly-sourced read and a misdelivered read all return plausible values.

The read path is where a device's internal parallelism becomes visible, and every optimisation it makes — merging, reordering, forwarding — is an opportunity to break one of the four.

2. The One-Sentence Model

One question, one answer, one source, one identity. A read passes through ordered stages exactly once, draws its value from exactly one of three places, and returns carrying the identity of the request that asked — and every performance optimisation on the read path is a chance to violate one of those without producing an error.

Call it the single answer. Everything below is a way of keeping it true while going fast.

3. What This Chapter Owns

QuestionOwned by
The device's window contract9.1
The host's path and tag pool9.2
Ordering, atomicity, visibility9.3
The read path end to endthis chapter
Write flows and completion ordering9.5
Latency/throughput cost9.6
Latency anatomy and modellingModule 18

4. The Read, End to End

A sequence diagram with four lifelines: two requesting agents, the device front end, and the device media. The first agent issues a read of a line, and the front end issues a media access. The second agent issues a read of the same line, and the front end merges it rather than issuing a second media access. The media returns the line once. The front end then sends two separate responses, one to each agent, each carrying its own identity. A note marks that merging the media access is the optimisation and merging the responses would strand a reader.Two readers, one media access, two responsesagent Aagent Bdevice front endmediaread line X (id 1)media access for Xread line X (id 5)same line in flight— merge, do notre-issueline X, onceresponse (id 1)response (id 5)

Architectural. The arrows are obligations, not named messages.

The two final arrows are the point. Merging the media access is the optimisation; merging the responses would lose a requester. §8 builds exactly that mistake, and it strands an agent waiting forever for an answer that was folded into someone else's.

5. Teaching-model boundary

6. RTL 1 — Four Stages, Each Exactly Once

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      if (data_ready) begin
        if (stage_q[data_id] == 3'd3)      double_data_err <= 1'b1;
        else if (stage_q[data_id] != 3'd2) out_of_order_stage_err <= 1'b1;
        else stage_q[data_id] <= 3'd3;
      end
      if (retire) begin
        // Retiring before data is ready completes nothing.
        if ((stage_q[retire_id] != 3'd3) && !ALLOW_SKIP)
          retire_without_data_err <= 1'b1;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1: a read passes four stages, each exactly once ===
  accepted                                            : ok
  looked up                                           : ok
  data ready                                          : ok
  retired, and the slot is free                        : ok
  no stage was skipped or repeated                    : ok

Teaching values, but the ordering is architectural: a read cannot be looked up before it is accepted, cannot produce data before it is looked up, and cannot retire before it has data.

Three distinct failures live here, and they are worth separating because they present completely differently:

ViolationWhat it looks like
stage skippeda read that never produced a value, retired
data twicea second response corrupting a later request
retire earlya requester released with nothing
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1b: retiring before the data is ready ===
  abuse instance flagged a retire with no data       : ok
  the allow-skip variant permits it silently          : ok

That check needed an instance reserved for illegal stimulus — a correct device never retires early, so driving it into the instance under test would trip that instance's own checker and be scored as a device fault.

7. RTL 2 — Exactly One of Three Sources

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Priority is not arbitrary: an error beats everything (there is no value),
  // and a pending write beats media (media is stale by definition).
  assign src_error   = rd_valid && media_bad;
  assign src_forward = rd_valid && !media_bad && has_pending_write;
  assign src_media   = rd_valid && !media_bad &&
                       (BOTH_SOURCES ? 1'b1 : !has_pending_write);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP2: exactly one source per read ===
  clean media : media=1 fwd=0 err=0 data=0xDEAD0000 hot=1
  a clean read comes from media                       : ok
  pending write : media=0 fwd=1 data=0xFEED0001 | both-sources hot=2
  a pending write beats stale media                   : ok
  and the forwarded value was returned                : ok
  the both-sources variant selected two               : ok
  media bad : err=1 fwd=0 hot=1
  an unreadable location beats everything             : ok

The priority is derived, not chosen. An error wins because there is no value to return at all. A pending write wins over media because 9.3 established that media is stale by definition while an uncommitted write exists. Media is the default because it is the only source when neither special case applies.

A design that can select two sources has no defined answer, and hot — the count of asserted sources — is what makes that checkable rather than assumed.

8. RTL 3 — Merge the Access, Not the Answer

This is the chapter's most important distinction, and it is easy to get half right.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // EVERY waiter on this line gets its own response. Merging the media access
  // is the optimisation; merging the responses would lose a requester.
  assign respond_mask = media_done ? (MERGE_RESPONSES ? (same_line & -same_line)
                                                      : same_line) : '0;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP3: two readers, one media access, two responses ===
  two readers of one line : media issues=1 merges=1
  one media access served both readers                : ok
  media done : correct mask=00100010 | merge-responses mask=00000010
  BOTH readers are responded to separately            : ok
  the broken variant answered only one                : ok
  two requests, two responses, one media access       : ok
  the broken variant stranded a waiting reader        : ok

Read the mask: 00100010 sets bits 1 and 5 — both waiters. The broken variant sets only the lowest, so agent 5 waits forever for an answer that was folded into agent 1's.

This is the device-side counterpart to 8.4's MSHR merging, and the constraint is different. An accelerator merging misses wakes several lanes from one fill and that is the whole point. A memory device is a responder: each request arrived separately with its own identity, and each is owed its own response. Merging the expensive part — the media access — is right; merging the cheap part is a lost requester.

A counter defect in my own RTL, for the eighth time

The response counter was originally written inside the wake loop:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        for (k = 0; k < NENT; k = k + 1)
          if (respond_mask[k]) begin
            wait_q[k]   <= 1'b0;
            n_respond_q <= n_respond_q + 16'd1;    // WRONG
          end

Four iterations, four non-blocking assignments to one variable, all computing n_respond_q + 1 from the same pre-edge value — so the counter advanced by one no matter how many readers were woken. The baseline caught it immediately (responses=1 expected 2). The fix accumulates combinationally and adds once:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    resp_this_cycle = 16'd0;
    for (k = 0; k < NENT; k = k + 1)
      if (media_done && respond_mask[k]) resp_this_cycle = resp_this_cycle + 16'd1;
...
        n_respond_q <= n_respond_q + resp_this_cycle;

Eighth appearance of this defect family in this track, and the second time I wrote it myself. The bit-vector-wake shape is a reliable place for it: whenever a loop clears several bits, any counter in that loop is wrong.

9. Waveform — One Media Access, Two Responses

Reader 1 issues, reader 5 merges, both are answered

8 cycles
Eight clock cycles traced from the RTL. At cycle one reader with id one issues a read of line X and the front end issues a media access. At cycle two reader with id five issues a read of the same line and merges instead of issuing a second media access, so the media counter stays at one and the merge counter becomes one. The media access is in flight through cycles three and four. At cycle five the media returns and the respond mask shows bits one and five set. At cycle six the response counter jumps from zero to two, showing both readers answered from a single media access.two readers, one linetwo readers, onelineone media access in flightone media access inflightboth answeredboth answeredsame line — merged, no second media accesssame line — merged, nosecond media accessmask 0x22 = readers 1 and 5mask 0x22 = readers 1 and 5responses jumps 0 → 2 from one accessresponses jumps 0 → 2 fromone accessclkrd_validrd_id01555555issue_mediamergedmedia_donerespond_mask0000000000220000media_issues00111111merges00011111responses00000022t0t1t2t3t4t5t6t7
Icarus Verilog 13.0. Architectural teaching waveform derived from the simplified RTL model; it is NOT CXL.mem message timing.

Read media_issues and responses at the right edge: 1 and 2. That ratio is the entire value of merging, and it is also the invariant — responses must exceed or equal media accesses, never the reverse.

10. RTL 4 — Out of Order, With Identity Intact

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign rsp_id    = REUSE_ID ? oldest_q : deq_sel;
  ...
        // The response must carry the id of the entry it drained.
        if (rsp_id != deq_sel) wrong_id_err <= 1'b1;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP4: responses may leave in any order, with their own id ===
  3 queued : level=3 peak=3
  drain id 2 first : rsp_id=2 data=0x33330002 | reuse-id rsp_id=0
  the response carried its own id                     : ok
  and its own data                                    : ok
  the reuse-id variant used the oldest id instead     : ok
  reordering was recorded, not prevented             : ok
  every response carried the id it drained           : ok

The device is free to reorder. 9.2 established that the host must tolerate it, so the device's only obligation is that the pairing survives — a response drained from entry 2 must say it is entry 2.

n_reorder_q records rather than prevents. That is deliberate: a device that never reorders is not more correct, it is less parallel, and the counter exists so the amount of reordering is visible to whoever is sizing the host's tolerance for it.

11. RTL 5 — A Partial Read Fails Only on Bytes It Asked For

Media works in lines; requesters work in bytes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  logic [7:0] touched_bad;
  assign touched_bad = byte_en & bad_bytes;
  // The read fails only if a byte it actually asked for is unreadable.
  assign rd_error    = rd_valid && (WHOLE_LINE_ERROR ? (bad_bytes != 8'd0)
                                                     : (touched_bad != 8'd0));
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP5: a partial read fails only on bytes it asked for ===
  request low half, bad high half : correct error=0 | whole-line error=1
  bad bytes it did not ask for did not fail it        : ok
  the whole-line variant failed it anyway            : ok
  request the bad half : error=1
  a read of unreadable bytes does fail               : ok
  the spared read was counted separately             : ok

Over-reporting an error is a real defect, not a conservative choice. A line with one unreadable byte can still serve every request that does not touch it, and failing those requests turns a small media defect into a large functional outage — the difference between losing eight bytes and losing a whole page.

n_spared_q counts reads that succeeded despite the line having a bad byte elsewhere. On ageing media that number is the difference between a device that degrades gracefully and one that falls over.

12. RTL 6 — Where a Read's Time Goes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6: where a read's time goes ===
  20 reads : queue=60 lookup=40 media=192 return=60 max_total=20
  forwarded=4 merged=2
  forwarded reads counted exactly (4 of 20)          : ok
  and forwarded reads charged zero media time        : ok
  stage accounting held                              : ok
  media is 54% of total read time

Media is 54% of read time even in a model with generous queue and return costs — consistent with 9.1's finding that the device's own internals dominate, not the protocol.

The stage checker is the interesting part:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // A forwarded read must not have spent media time.
      if (was_forwarded && (t_media != 8'd0)) stage_sum_err <= 1'b1;

A forwarded read never touched the media, so charging it media time is an accounting contradiction — and one that would make forwarding look useless in exactly the telemetry meant to justify it.

13. RTL 7 — Everything Accepted Is Answered Once

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP7: everything accepted is answered exactly once ===
  accepted=32 media issues=21 answered=30 errors=2 peak=2
  answers matched an independent oracle              : ok
  error count matched the oracle                     : ok
  nothing was answered that was not accepted         : ok
  peak in-flight was exactly 2                       : ok
  media issues (21) below acceptances (32): merging works : ok

Media issues below acceptances is the signature of a working merge path — 21 media accesses served 32 reads. If those two numbers are equal, merging is not happening, and on a workload with line sharing that is a third of the media bandwidth wasted.

14. Quantitative Reasoning — What the Read Path Saves

Illustrative, from the measured runs.

Merging

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  accepted=32  media issues=21

A third of reads were served without a media access. Using 9.1's 100 ns media latency and 4-deep media concurrency, eliminating 11 of 32 media accesses returns roughly 11 × 100 ns of media occupancy to other work — on a device whose media is the bottleneck, that is directly throughput.

Forwarding

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  20 reads : media=192 cycles total
  forwarded=4, each charging zero media time

Four forwarded reads at 12 cycles of media time each is 48 cycles avoided in 20 reads, and the checker guarantees the saving is real rather than an accounting artefact.

The stage split

StageCycles (20 reads)Share
queue6017%
lookup4011%
media19254%
return6017%

Media dominates, which is why merging and forwarding — the two optimisations that avoid media entirely — matter more than anything done to the other three stages.

15. The Read Path, as Structures

A read enters a lifecycle tracker that enforces four ordered stages. It then reaches a merge check which either issues a media access or attaches to one already in flight. A source selector chooses between media, a forwarded pending write, and an error. Results enter a return queue that may drain out of order but preserves identity. A partial-read mask decides whether a bad byte actually affects this request. Counters observe every stage.read acceptedstage 1 of 4merge checkrisks: stranded readermedia access54% of read timesource selectrisks: two sourcespartial maskrisks: over-reportederrorreturn queuerisks: lost identitycountersmedia issues vsacceptancesif newone12

Every box carries its own way of being silently wrong, and the captions name them. That is the shape of this chapter: the read path is not complicated, but each of its five optimisations has a failure that returns a plausible value.

16. Assertions

Icarus Verilog 13.0 does not support concurrent SVA here, so every property is synthesisable checker logic verified in simulation.

Safety

PropertyIntent
Stage ordereach stage entered only from its predecessor
Single answerdata_ready |-> stage != DATA_READY
No early retireretire |-> stage == DATA_READY
One sourceexactly one of media / forward / error
No stranded readerevery same-line waiter is responded to
Response identityrsp_id == deq_sel
No over-reported errorrd_error |-> (byte_en & bad_bytes) != 0
Stage accountingforwarded |-> t_media == 0
Answer conservationanswered <= accepted

Liveness

PropertyAssumption it needs
An accepted read is eventually answeredthe media completes or reports failure
A merged reader is eventually responded tothe media access it merged onto completes

The second is the one merging creates: a merged reader has no media access of its own, so its liveness depends entirely on someone else's. If the leading access is lost, every merged waiter is lost with it — which is why missing_response_err is a safety property and not merely telemetry.

Performance goals

GoalMeasured by
Merging effectivemedia issues below acceptances
Forwarding effectiveforwarded count, with zero media time
Media not over-occupiedmedia share of total read time
Reordering visiblereorder count

17. Mutation Testing

Twenty-one mutations. Twenty-one killed.

MutationResult
Lookup on an unaccepted read allowedkilled
Data produced twice not flaggedkilled
Retiring without data not flaggedkilled
Pending write never forwardedkilled
Unreadable location answered with datakilled
Media selected alongside a forwardkilled
Two simultaneous sources not flaggedkilled
Same-line reads never mergekilled
Only one waiter responded tokilled
Multiple responses counted as onekilled
Stranded reader not flaggedkilled
Response carries the oldest idkilled
Wrong response id not flaggedkilled
Reordering not recordedkilled
Read fails on bytes it did not requestkilled
Over-reported read error not flaggedkilled
Spared reads not countedkilled
Forwarded read charging media time not flaggedkilled
Forwarded reads not countedkilled
In-flight counted with two assignmentskilled
Media issues counted as answerskilled

The first run scored 18 of 21, and all three escapes were the same shape: illegal stimulus that a correct device never produces — a lookup for an unaccepted read, data produced twice, and a forwarded read charging media time. Each got a dedicated abuse instance.

Notably, the most valuable defect this chapter found was not found by mutation testing. The baseline caught it: the response counter written inside the wake loop reported responses=1 where two readers had been answered. Mutation testing verifies the checkers; the baseline verifies the design, and here the design was mine and wrong.

18. Verification Plan

AreaApproach
LifecycleFull four-stage pass; early retire and out-of-order stages on an abuse instance
SourceAll three cases plus priority; both-sources variant compared
MergingTwo readers of one line; mask asserted, not just the count
ResponsesMerge-responses variant shown stranding a reader
Return orderThree queued, drained out of order, ids and data checked per entry
PartialBad bytes inside and outside the request; whole-line variant
LatencyPer-stage accounting with a forwarded-read contradiction check
CountersIndependent oracle; exact peak asserted

The coverage cross is line sharing × source × byte coverage: unique / shared line, crossed with media / forward / error, crossed with full-line / partial. The shared-line column is what exercises merging, and a testbench generating distinct addresses per request never enters it — the same blind spot 8.4 had.

19. Silicon Observability

CounterDiagnoses
accepted vs media issueswhether merging is working
mergeshow much line sharing the workload has
forwardshow often 9.3's hazard path is taken
reorder countdevice internal parallelism
spared readsmedia ageing without functional impact
error responsesmedia health
per-stage latency sumswhich stage dominates
peak in-flightread-path concurrency
missing_responsea stranded reader — must be zero forever
wrong_idmisdelivery — must be zero forever

Two alarms, eight tuning counters. A stranded reader and a misdelivered response both produce silent failures — one hangs a requester, the other corrupts it — and both must read zero for the life of the product.

The most useful ratio is media issues over acceptances. Near 1.0 means no merging is happening; well below means line sharing is being exploited. On a media-bound device that single ratio predicts throughput better than any latency number.

20. Debug Lab

1

One reader of a shared line never receives its data

MERGED-RESPONSE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Wake the waiter for this line.
respond_mask = same_line & -same_line;      // lowest set bit only
Symptom

A requester hangs waiting for a read that the device believes it answered. It happens only when two agents read the same line close together, so it scales with sharing and disappears under low load. The device's response counter looks healthy.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  media done : correct mask=00100010 | merge-responses mask=00000010
  the broken variant stranded a waiting reader        : ok
Root Cause

The merge optimisation was applied to the responses as well as to the media access. Merging the access is correct and valuable; merging the responses loses a requester, because each read arrived separately with its own identity and is owed its own answer.

The isolate-lowest-bit idiom is the tell. It answers one waiter and silently drops the rest.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign respond_mask = media_done ? same_line : '0;
if ((same_line & ~respond_mask) != '0) missing_response_err <= 1'b1;

Respond to every waiter, and assert that none is left.

Prevention

Assert the mask, not the count. A test that checks "a response happened" passes on the broken design; only checking which bits were set catches it.

2

The merge counter says one where two readers were woken

LOOP-NBA-COUNTER
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for (k = 0; k < NENT; k = k + 1)
  if (respond_mask[k]) begin
    wait_q[k]   <= 1'b0;
    n_respond_q <= n_respond_q + 1;      // inside the loop
  end
Symptom

Response counts are systematically low, and the shortfall grows with how much line sharing the workload has. The readers are all woken correctly — only the telemetry is wrong, which makes merging look ineffective in exactly the counter meant to prove it works.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  two requests, two responses, one media access       : ok
Root Cause

A non-blocking increment inside a loop. Every iteration computes n_respond_q + 1 from the same pre-edge value, so the last one wins and the counter advances by one however many bits were set.

Eighth appearance of this defect family in this track. The bit-vector wake is a reliable place to find it: whenever a loop clears several bits, any counter inside that loop is wrong.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
resp_this_cycle = 16'd0;
for (k = 0; k < NENT; k = k + 1)
  if (media_done && respond_mask[k]) resp_this_cycle = resp_this_cycle + 16'd1;
...
n_respond_q <= n_respond_q + resp_this_cycle;

Accumulate combinationally, add once.

Prevention

Never place a non-blocking increment inside a loop. Test with more than one bit set — a single-waiter test cannot distinguish the two implementations.

3

A read returns a value from the wrong request

RSP-ID-REUSE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign rsp_id = oldest_q;      // assume responses drain in order
Symptom

Wrong data with every count balancing. It appears only with several reads outstanding and only when the device reorders — which it does whenever media latency varies between addresses.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  drain id 2 first : rsp_id=2 | reuse-id rsp_id=0
  reuse-id variant flagged a wrong response id       : ok
Root Cause

The response carried a positional id rather than the identity of the entry it drained. The device is free to reorder — that is not the bug — but the pairing of response to request must survive the reordering, and a positional id does not.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign rsp_id = deq_sel;
if (rsp_id != deq_sel) wrong_id_err <= 1'b1;

Carry the identity of the entry actually drained.

Prevention

Give each queued read distinguishable data and drain deliberately out of order. A test that drains in order cannot fail on either design.

4

A whole page becomes unreadable because of eight bad bytes

WHOLE-LINE-ERROR
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign rd_error = (bad_bytes != 0);      // any bad byte fails the read
Symptom

Reads fail for a region far larger than the actual media defect. Applications lose access to data that is entirely intact. The failure region is line-granular even though the defect is byte-granular, and it worsens as media ages.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  request low half, bad high half : correct error=0 | whole-line error=1
  bad bytes it did not ask for did not fail it        : ok
Root Cause

The error was computed over the whole line rather than over the requested bytes. A line with one unreadable byte can still serve every request that does not touch it, and failing those turns a small media defect into a large functional outage.

Over-reporting feels conservative and is not — it converts recoverable degradation into data loss.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign touched_bad = byte_en & bad_bytes;
assign rd_error    = rd_valid && (touched_bad != 0);
if (rd_error && (touched_bad == 0)) overreported_err <= 1'b1;

Fail on what was asked for, and count the reads that were spared.

Prevention

Test a partial read whose requested bytes are clean on a line with a bad byte elsewhere. A full-line-only test cannot distinguish the designs.

5

A read is answered twice and a later request is corrupted

DOUBLE-DATA
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (data_ready) stage_q[data_id] <= DATA_READY;   // no repeat check
Symptom

A read that was already answered produces a second response. The extra response lands on whatever now owns that identity, corrupting an unrelated request. The corrupted request looks well-formed; the one that caused it retired successfully long before.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  abuse instance flagged duplicate data             : ok
Root Cause

No check that a read had already reached the data-ready stage. Any path that can signal data twice — a retry, a merge that also issues, a race between two producers — then produces two responses for one request.

It is the same identity hazard as tag reuse, arriving from the opposite direction: not a name reused too early, but a name answered twice.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (stage_q[data_id] == DATA_READY)      double_data_err <= 1'b1;
else if (stage_q[data_id] != LOOKED_UP)  out_of_order_stage_err <= 1'b1;
else stage_q[data_id] <= DATA_READY;

Make the stage progression explicit so a repeat is detectable.

Prevention

Drive a duplicate data-ready on an abuse instance. A correct device never produces one, so the check is unreachable without deliberately illegal stimulus.

6

Forwarding appears to save nothing

STAGE-MISATTRIBUTION
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sum_media_q <= sum_media_q + t_media;   // charged even for forwarded reads
Symptom

Telemetry shows forwarded reads costing the same as media reads, so the forwarding path looks like wasted logic. A proposal to remove it follows, which would make things substantially worse.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  and forwarded reads charged zero media time        : ok
Root Cause

Stage time was attributed without regard to which stages the read actually used. A forwarded read never touched the media, so any media time charged to it is fabricated — and it appears in exactly the counter meant to justify the optimisation.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (was_forwarded && (t_media != 8'd0)) stage_sum_err <= 1'b1;

Assert the accounting contradiction rather than trusting the inputs.

Prevention

For every per-stage counter, assert which stages each path is allowed to use. Latency accounting is logic and can be wrong like any other.

7

Media bandwidth is a third higher than it should be

NO-MERGE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign issue_media = rd_valid;      // every read issues its own access
Symptom

Media occupancy is far higher than the read rate justifies, and throughput saturates early. Every read is answered correctly and no error is reported. Media issues equal acceptances exactly.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  media issues (21) below acceptances (32): merging works : ok
Root Cause

Reads of a line already being fetched issued their own media accesses instead of merging. On a workload with line sharing this multiplies media traffic — measured here, merging cut 32 reads to 21 media accesses, a third saved.

On a media-bound device that is directly throughput, and it is invisible functionally because every read still gets the right value.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for (k = 0; k < NENT; k = k + 1)
  if (wait_q[k] && (line_m[k] == rd_line)) line_busy = 1'b1;
assign issue_media = rd_valid && !line_busy;

And expose media issues against acceptances so the ratio is measurable.

Prevention

Generate deliberately shared lines. A testbench with distinct addresses per request exercises the merge path zero times while reporting high coverage — the same blind spot as an accelerator's MSHR tests.

21. Design Review

  1. What stages does a read pass, and what enforces the order?
  2. Can data be signalled twice for one read?
  3. How many sources can a read draw from simultaneously?
  4. When two reads share a line, how many media accesses and how many responses?
  5. Does a response carry its own identity or a positional one?
  6. Does a partial read fail on bytes it did not request?
  7. Is a forwarded read charged media time?
  8. What is the ratio of media issues to acceptances on a sharing workload?
  9. Which two counters must be zero for the life of the product?
  10. What happens to merged readers if the leading media access is lost?

22. How This Appears in Real Engineering

Architecture. The media-issues-to-acceptances ratio is the read path's headline number on a media-bound device, and merging is the cheapest way to move it.

RTL. The wake loop is a small structure with two classic defects — a merged response mask and a counter inside the loop — and this chapter hit both.

DV. Shared lines, out-of-order drains and partial reads on partially bad lines are all directed scenarios. None arises from a random address generator.

Post-silicon. Spared reads are the counter that distinguishes media ageing from media failure, and it only exists if someone decided partial reads should not over-report.

Firmware. The reorder count tells a driver how much response reordering to expect, which sizes the host-side tolerance 9.2 requires.

23. Common Misconceptions

BeliefCorrection
Merging reads means merging responsesMerge the access; each requester keeps its answer
Reordering is a device defectIt is normal; losing identity is the defect
A bad byte fails the lineIt fails only requests that touch it
Conservative error reporting is safeOver-reporting turns degradation into outage
A read has one sourceIt has three, and exactly one must win
Latency accounting is bookkeepingIt is logic, and it can contradict itself
Distinct addresses are good coverageThey exercise the merge path zero times
Counters cannot cause correctness bugsOne inside a loop under-counts silently

24. Interview Reasoning

25. Exercises

  1. Calculation. A workload issues 1000 reads with 40% of them sharing lines with an in-flight read. Compute the media accesses with and without merging, and the media time saved at 100 ns per access.

  2. Analysis. A device reports accepted=10000, media_issues=10000, merges=0, and high media occupancy on a workload known to share data. Name the defect and the counter that confirms it.

  3. RTL task. Extend read_merge so a read arriving after the media access has returned but before the responses are sent does not merge. State the new race and the assertion that catches it.

  4. Assertion task. Write the property that catches a read retiring before its data is ready, and explain why it must be expressed over the stage rather than over the retire signal alone.

  5. Debug task. One requester hangs on a read while all others succeed, correlated with data sharing rather than load. Give your investigation order and the single counter that identifies the cause.

  6. Design review. A colleague proposes failing any read to a line with a bad byte, "because partial errors are complicated". Give the strongest version of that argument, then quantify what it costs using line and byte sizes.

26. Summary

One question, one answer, one source, one identity.

  • A read passes ordered stages, each exactly once. Skipping, repeating and early retirement are three different failures with three different symptoms.
  • Its value comes from exactly one of three sources — error, forward, or media — and the priority is derived rather than chosen.
  • Merge the media access, not the responses. Two readers of one line means one access and two answers; merging the answers strands a requester forever.
  • Reordering is normal; losing identity is the defect. A response must carry the id of the entry it drained.
  • A partial read fails only on bytes it requested. Over-reporting turns a byte-granular defect into a line-granular outage.
  • Latency accounting is logic. A forwarded read charging media time is a contradiction, and it appears in the counter meant to justify forwarding.
  • Measured: 21 media accesses served 32 reads — a third saved by merging; media was 54% of read time.
  • Verification lesson: mutation testing found three escapes, all illegal stimulus needing abuse instances — but the most valuable defect, a non-blocking counter inside a wake loop, was found by the baseline, not by mutation. The two techniques answer different questions.

Chapter 9.5 takes the write path, which is harder for the reason 9.3 gave: a read is done when it has a value, and a write is not done until others can see it.

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.