Skip to content
VLSI Mentor

CXL · Module 9

Memory-Expansion Semantics

What a reachable location must guarantee before it is memory: same-address ordering without global serialisation, atomicity that survives a link, the gap between accepted and visible, and the scope of the coherence contract. Seven RTL models simulated, twenty mutations, twenty killed.

Chapter 9.2 got a load to the device and back. That proves the location is reachable. This chapter asks the harder question: what does the access actually guarantee?

1. The Engineering Problem — Reachable Is Not Usable

Software does not merely require that a load returns a value. It requires a set of guarantees so ingrained that nobody states them:

Software assumesWhich really means
"I wrote it, so I can read it"a later read of that address sees the earlier write
"the flag guards the data"the data is visible before the flag is
"compare-and-swap is atomic"nothing can intervene between the read and the write
"the other core sees it too"the location is inside a coherence scope

Every one of those is free in a local DRAM controller and none of them is free across a link. A device that answers loads correctly and provides none of these is reachable and unusable — and worse, it will pass every functional test, because functional tests issue one access at a time.

This chapter is about the guarantees, and each one costs something specific.

2. The One-Sentence Model

A location you can reach is not yet memory. Memory is a set of promises — that accesses to the same address appear in order, that a read-modify-write cannot be split, that a write becomes visible at a defined point, and that a stated set of agents agrees about the value — and each promise costs hardware that pure reachability does not.

Call it the promise set. 9.1 and 9.2 built the plumbing; this chapter is what the plumbing has to guarantee.

3. What This Chapter Owns

QuestionOwned by
The device's window contract9.1
The host's path to the device9.2
What the access guaranteesthis chapter
Read flows end to end9.4
Write flows and completion ordering9.5
Latency/throughput cost9.6
Generic coherency theoryModule 13
Coherency flows in detailModule 14

4. The Four Promises

A sequence diagram with four lifelines: a writer core, a reader core, the host coherence point, and the CXL memory device. The writer stores data and then stores a flag. The host coherence point ensures the data write commits before the flag write becomes visible. The reader core loads the flag, sees it set, then loads the data and receives the new value. A note shows that if acceptance were treated as visibility, the flag could become visible before the data, and the reader would load stale data.Why a flag-guards-data handshake needs more than reachabilitywriter corehost coherence pointCXL memory devicereader corestore DATAaccepted — not yetvisiblecommitted — nowvisiblestore FLAGaccepted, thencommittedload FLAG — sees itsetload DATAthe new value,because DATAcommitted first

Architectural. The arrows are obligations, not named messages.

The whole diagram turns on the second and third arrows being different events. If "accepted" were reported as done, the writer could proceed to the flag while the data was still uncommitted, and the reader would find the flag set and the data stale. §8 builds exactly that failure.

5. Teaching-model boundary

6. RTL 1 — Order the Address, Not the Device

The first promise is the one that costs the most if you get it wrong in either direction.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Same address: order. Different address: proceed. That is the contract.
  assign must_order = NO_ORDER     ? 1'b0
                    : (GLOBAL_ORDER ? busy_valid : same_addr);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1: same address orders, different addresses do not ===
  different address : must_order=0 proceed=1 | global-order proceed=0
  a different address proceeds concurrently          : ok
  the global-order variant serialised it anyway      : ok
  same address      : must_order=1 proceed=0 | no-order proceed=1
  the same address must wait                         : ok
  the no-order variant let it overtake               : ok
  exactly one proceeded and one was ordered         : ok

Three designs, and only the middle one is memory.

DesignCorrect?Cost
no orderingno — same-address programs breakfastest
per-address orderingyesan address comparison
global orderingyes, butserialises the whole device

The global-order variant is safe and useless: a memory expander that serialises every access delivers one access per round trip, which 9.2 showed is roughly two orders of magnitude below what the link supports. Correctness that costs all the performance is not a viable answer, which is why the contract is scoped to the address rather than the device.

7. RTL 2 — Atomicity Means Holding the Location

A read-modify-write is atomic only if nothing can observe or change the location between the read and the write. Across a link that window is long.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign conflict      = held && other_access && (other_addr == hold_addr_q);
  assign other_blocked = conflict && !NO_LOCK;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP2: atomicity means holding the location ===
  rmw started : held=1
  the location is held for the sequence              : ok
  other access to the same line : correct blocked=1 | unlocked blocked=0
  a conflicting access is blocked, not interleaved   : ok
  the unlocked variant let it through                : ok
  the hold released when the write completed         : ok
  atomicity held across the sequence                 : ok
  the unlocked variant flagged broken atomicity      : ok

Atomicity cannot be built by the requester out of a separate read and a separate write. The two are individually correct and the gap between them is exactly the window an atomic operation must not have — and across a link that gap is hundreds of nanoseconds rather than a few cycles.

So the location must be held at the point of serialisation, which is why atomic support is a property of the memory system rather than something software can synthesise. Note also that holding costs concurrency: n_blocked_q counts accesses delayed by someone else's atomic sequence, and a workload with heavy contention on one line will show it.

8. RTL 3 — Accepted Is Not Visible

This is the promise most often assumed and least often stated.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign wr_reported_done = ACCEPT_IS_VISIBLE ? wr_accept : wr_commit;
  // A probe sees the new value only once the write has actually committed.
  assign rd_sees_new = rd_probe && (rd_addr == pend_addr_q) && !pending_q;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP3: accepted is not visible ===
  at acceptance : correct reported_done=0 | accept-is-visible reported=1
  acceptance alone does not report completion        : ok
  the broken variant reported done immediately       : ok
  probe before commit : sees_new=0
  a reader does not see it before it commits         : ok
  after commit : sees_new=1 reported_done=1
  once committed the value is visible                : ok
  accept-is-visible variant flagged premature done   : ok

Accepted means the device owns the write and it will not be lost. Visible means another observer reading that address will see it. Those are different events with a real gap between them, and conflating them breaks every producer/consumer handshake in software — the flag becomes visible before the data it guards.

A checker that could not see its own violation

The first version of the premature-visibility check read:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (wr_reported_done && pending_q && !wr_commit) ...

It never fired. On the acceptance cycle pending_q has not yet been set — it is set by that edge — so the condition was false exactly when the violation occurred. The corrected form drops the term that cannot be true yet:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // Reporting completion on any cycle that is not the commit cycle. On the
      // correct design reported_done IS wr_commit, so this is unreachable; the
      // accept-is-visible variant trips it on the acceptance cycle, when
      // pending_q has not yet been set and cannot be part of the condition.
      if (wr_reported_done && !wr_commit) begin

A checker that samples state the violating edge is still creating cannot see the violation. This is the same state-versus-transition mistake that has appeared throughout this track, in its most compact form yet.

9. RTL 4 — Coherence Has a Scope

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign in_scope     = access_valid && (ASSUME_ALL_COHERENT ? 1'b1 : via_host_path);
  assign out_of_scope = access_valid && via_side_path && !ASSUME_ALL_COHERENT;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP4: coherence has a scope ===
  an access through the host is inside the contract  : ok
  side path : correct in_scope=0 out=1 | assume-all in_scope=1
  a path the host does not mediate is out of scope   : ok
  the assume-all variant claimed it anyway           : ok
  assume-all variant flagged an unsafe assumption    : ok

Architectural. The sourced contract says the host manages coherency, which means the guarantee covers agents reaching the memory through the host's coherence machinery. An agent that reaches the same media by another route — a device-internal engine, a management path — is outside that contract.

The failure this prevents is a design that assumes coherence universally and therefore never asks the question. n_out_q exists so the answer is a number rather than an assumption, and a non-zero value on a system that believed it had none is a design conversation worth having early.

10. RTL 5 — A Read Must Not Overtake an Uncommitted Write

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign hazard    = rd_valid && pend_valid_q && (rd_addr == pend_addr_q) && !NO_HAZARD;
  // Forward the pending value rather than reading stale media.
  assign forwarded = hazard && !NO_FORWARD;
  assign rd_data   = forwarded ? pend_data_q : media_data;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP5: a read must not overtake an uncommitted write ===
  read same address : hazard=1 forwarded=1 data=0xCAFE0001
  the pending write was forwarded, not media read   : ok
  and it returned the new value, not the old        : ok
  no-forward variant : data=0xDEADBEEF (media)
  the no-forward variant returned stale media       : ok
  the no-forward variant flagged a stale read        : ok

This is §6's ordering promise made concrete. Detecting the hazard is mandatory; forwarding is the optimisation that makes the promise cheap. Without forwarding the read must stall until the write commits, which on a device with real media latency is expensive and shows up as n_stall_q.

The broken variant returns 0xDEADBEEF — the media's old contents — for an address that was just written. Every counter balances and the value is wrong.

11. RTL 6 — What a Fence Actually Waits For

Architectural, modelling the obligation rather than any CXL-defined mechanism.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign fence_done = fence_pending_q &&
                      (RETIRE_EARLY ? 1'b1 : (outstanding_q == 8'd0));
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6: a fence waits for what came before ===
  3 accepted, 0 committed : outstanding=3
  fence issued : correct done=0 | retire-early done=1
  the fence does not pass with work uncommitted     : ok
  the retire-early variant passed immediately        : ok
  the fence passed once everything committed        : ok
  retire-early variant flagged an early fence pass   : ok

A fence is how software asks for an ordering guarantee it does not get for free. Its cost is exactly n_wait_q — cycles spent waiting for prior work to commit — and on a device with long media latency that cost is substantial. This is why per-address ordering matters so much: it is what lets most programs avoid fences entirely.

The counter defect, for the seventh time

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6b: accept and commit in the same cycle ===
  4 accepted : outstanding=4
  3 cycles of accept+commit together : outstanding=4
  the case-based counter held steady                 : ok
  and drained to zero when commits caught up         : ok

Two non-blocking assignments to one variable; the second wins; accept-and-commit together decrements. Seventh appearance in this track. Here the consequence is a fence that passes early, because the outstanding count reads zero while work is still in flight — a correctness failure produced by a counter bug.

12. RTL 7 — Semantics Counters

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP7: semantics counters, against an oracle ===
  accepted=36 committed=33 max_uncommitted=3
  ordered=9 forwarded=7 rmw=4 rmw-blocked=3 fence-waits=5
  accept/commit matched an independent oracle       : ok
  nothing committed that was not accepted           : ok
  peak uncommitted was exactly 3                    : ok
  same-address orderings counted exactly (9 of 36)  : ok

These are semantics counters, not throughput counters, and each answers a question a performance counter cannot:

CounterThe question
same-address orderingshow much the workload contends on single addresses
forwardshow often ordering was made cheap instead of costly
atomic holds and blockshow much concurrency atomics are costing
fence waitshow much software is paying for explicit ordering
peak uncommittedhow far acceptance runs ahead of visibility

Peak uncommitted is the one worth watching. It is the size of the window in which accepted-but-invisible writes exist, and it bounds how wrong an accept-is-visible design would be.

13. Quantitative Reasoning — What Each Promise Costs

Illustrative, using this chapter's measured teaching run.

Of 36 accesses:

PromiseCountCost
same-address order9 (25%)one compare; 7 stalls avoided by forwarding
atomic holds43 accesses blocked
fence waits5cycles until nothing was in flight

Two conclusions.

Forwarding converted most of the ordering cost into nothing. Seven of the nine same-address cases were satisfied by forwarding rather than by stalling. Without it, each would have waited for a media commit — at 9.1's illustrative 100 ns media latency, that is 700 ns of stall avoided in 36 accesses.

Atomics cost more than they appear to. Four atomic sequences blocked three unrelated accesses — nearly one blocked access per atomic. On a contended line that ratio rises sharply, which is why heavy atomic use on far memory is a placement problem rather than a hardware one.

The ordering trade, in throughput terms

OrderingIn flightRate
global1
per-addressthe budget125×
noneunboundedwrong

Using 9.1's 125-outstanding figure, per-address ordering is worth up to two orders of magnitude over global ordering while providing the same guarantee to any single-address program. That is the entire reason memory expansion is viable.

14. The Four Promises, as Structures

Four guarantees each map to a hardware structure. Same-address ordering maps to an address comparator plus a forwarding path. Atomicity maps to a location hold. A defined visibility point maps to separate accept and commit events. Coherence scope maps to a path check. Below them, a counters block observes all four so each promise's cost is measurable.orderingsame address onlyatomicityread-modify-writevisibilitya defined pointscopewhich agentsagreeaddresscompare+ forwarding pathlocation holdblocks conflictsaccept ≠committwo distincteventspath checkhost-mediated ornotsemanticscounterseach promise'scost, measured12

Read the middle row as a bill of materials. The cheapest promise is ordering — one comparator and a forwarding path — and it is worth up to two orders of magnitude of throughput. The most expensive in practice is atomicity, because a hold blocks other agents for a link round trip.

15. Assertions

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

Safety

PropertyIntent
Same-address orderingsame_addr && busy |-> !may_proceed
Atomicityheld && other_same_addr |-> blocked
Visibilityreported_done |-> committed
Reader sees committed onlysees_new |-> !pending
Coherence scope honestvia_side_path |-> !in_scope
No stale readpending_same_addr && read |-> forwarded
Fence completenessfence_done |-> outstanding == 0
Commit conservationcommits <= accepts

Liveness

PropertyAssumption it needs
An ordered access eventually proceedsthe blocking access commits
An atomic hold is eventually releasedthe write completes
A fence eventually passesall prior work commits

The atomic-hold property is the one to watch: a read-modify-write whose write never arrives holds a location forever, blocking every other agent. That is the liveness hazard this chapter's safety mechanism creates, and it is why a hold needs a bound in any real design.

Performance goals

GoalMeasured by
Ordering scoped to addressesordered count against total accesses
Ordering made cheapforward count against ordered count
Atomics not dominatingblocked count against atomic count
Fences rarefence waits against total accesses

16. Mutation Testing

Twenty mutations. Twenty killed.

MutationResult
Same-address accesses not orderedkilled
Every address treated as the same addresskilled
Same-address reordering not flaggedkilled
Conflicting access not blocked during an atomickilled
Conflicts during an atomic not detectedkilled
Broken atomicity not flaggedkilled
Write reported done at acceptancekilled
Reader sees a value before it commitskilled
Premature completion not flaggedkilled
Every path assumed coherentkilled
Unsafe coherence assumption not flaggedkilled
Pending write never forwardedkilled
Write-read hazard not detectedkilled
Stale read not flaggedkilled
Fence passes with work outstandingkilled
Early fence pass not flaggedkilled
Fence outstanding counted with two assignmentskilled
Same-address orderings not countedkilled
Peak uncommitted lags by onekilled
Commit conservation disabledkilled

The first run scored 18 of 20, and both escapes were shapes now familiar enough to be predictable.

The two-assignment fence counter survived because the testbench never had an accept and a commit in the same cycle. EXP6b added exactly that, and the consequence here is worse than a wrong statistic: an outstanding count that reads zero while work is in flight makes a fence pass early, turning a counter bug into an ordering violation.

The commit conservation law was unreachable on legal stimulus — commits can never exceed accepts on a correct design — so it needed an instance driven with commits and no accepts at all.

Three checkers in this chapter were repaired before mutation testing, by the baseline itself: the premature-visibility check that sampled pending_q too early (§8), and two combinational outputs the testbench sampled after their inputs had deasserted. The baseline catching its own bugs is worth as much as the mutation suite catching mine.

17. Verification Plan

AreaApproach
OrderingSame and different addresses; global and no-order variants compared
AtomicityConflicting access during a hold; locked and unlocked compared
VisibilityProbe before and after commit; accept-is-visible variant
ScopeHost path and side path; assume-all variant
HazardRead to an address with an uncommitted write; forward and no-forward
FenceOutstanding work at fence time; retire-early variant
CountersIndependent oracle; conservation proven reachable on an abuse instance
ConcurrencyAccept and commit in the same cycle

The coverage cross is address relationship × access pair × commit state: same / different address, crossed with read-after-write, write-after-write and atomic, crossed with committed / uncommitted. The uncommitted column is where every guarantee in this chapter is actually tested, and a testbench that commits immediately never enters it.

18. Silicon Observability

CounterDiagnoses
same-address orderingscontention on individual addresses
forwards vs stallswhether ordering is cheap or costly here
atomic holds and blocked accessesthe concurrency cost of atomics
fence waitshow much software is paying for explicit ordering
peak uncommittedthe size of the accepted-but-invisible window
out-of-scope accessesagents reaching the media outside the contract
premature_visiblea correctness alarm — must be zero forever
stale_reada correctness alarm — must be zero forever
atomicity_brokena correctness alarm — must be zero forever

Three alarms and six tuning counters. The alarms describe failures with no other symptom — a premature visibility, a stale read and a broken atomic all return plausible values — and each must be identically zero for the life of the product.

The most useful tuning pair is forwards against stalls. A high forward rate means same-address contention exists and is being handled cheaply; a high stall rate means the same contention is being paid for in full, and the fix is usually in software placement rather than hardware.

19. Debug Lab

1

A read returns the value from before the write that preceded it

NO-SAME-ADDR-ORDER
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign may_proceed = req_valid;      // no ordering at all
Symptom

Single-threaded code observes its own write not taking effect. A store followed by a load of the same address returns the old value, intermittently, under load. It never reproduces with a single access outstanding.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  same address      : must_order=1 proceed=0 | no-order proceed=1
  no-order variant flagged a same-address reorder    : ok
Root Cause

Two accesses to the same address were allowed to proceed concurrently, so the read reached the media before the write committed. The device is fast and wrong.

This breaks the most fundamental assumption software makes — that a program observes its own writes — and it is invisible until enough concurrency exists for the two accesses to overlap.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign same_addr   = busy_valid && (req_addr == busy_addr);
assign must_order  = same_addr;
assign may_proceed = req_valid && !must_order;

Order the address, not the device — see Debug Lab 2 for why the other extreme is also wrong.

Prevention

Direct a test at two accesses to one address with the first still outstanding. Random address streams over a large space essentially never collide, which is why this survives long regressions.

2

The device is correct and delivers a hundredth of the link

GLOBAL-ORDER
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign must_order = busy_valid;      // any outstanding access blocks
Symptom

Perfect correctness and catastrophic throughput. One access completes per round trip regardless of how many the host issues. Outstanding depth never exceeds one, and every host-side and device-side counter looks healthy because nothing is failing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  different address : must_order=0 proceed=1 | global-order proceed=0
  the global-order variant serialised it anyway      : ok
Root Cause

Ordering was applied to the device rather than to the address. Every access waits for the previous one, so the device delivers one access per round trip — at 9.1's 250 ns that is roughly 4 million accesses per second against a link that supports far more.

It is the safe extreme, and it is the reason "just serialise it" is not an acceptable answer to Debug Lab 1.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign must_order = busy_valid && (req_addr == busy_addr);

One address comparison converts a global constraint into a per-address one, and it is worth up to two orders of magnitude of throughput.

Prevention

Assert a concurrency property alongside the ordering one: accesses to different addresses must be able to be outstanding simultaneously. Correctness-only testing rates the global design as perfect.

3

A compare-and-swap silently loses an update

SPLIT-RMW
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Read, then write. No hold between them.
issue_read(addr); ... issue_write(addr, new_value);
Symptom

A shared counter increments fewer times than there were increments. No error is reported; the values are all plausible. The loss rate rises with the number of participating agents and with access latency.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  other access to the same line : correct blocked=1 | unlocked blocked=0
  the unlocked variant flagged broken atomicity      : ok
Root Cause

The read-modify-write was built from a separate read and a separate write, so another agent read the same location in between and both wrote back values derived from the same original. One update is lost.

Across a link the gap is hundreds of nanoseconds rather than a few cycles, so the window is enormous compared with a local atomic — the same code that is nearly safe on local DRAM fails routinely on far memory.

Fix

The location must be held at the point of serialisation for the duration of the sequence:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign conflict      = held && other_access && (other_addr == hold_addr_q);
assign other_blocked = conflict;

Atomicity is a property the memory system provides; the requester cannot synthesise it.

Prevention

Test with a second agent deliberately accessing the same address inside an atomic sequence. A single-agent test cannot exercise atomicity at all — it only shows that the read and the write both work.

4

A consumer reads the flag as set and the data as stale

ACCEPT-IS-VISIBLE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign wr_reported_done = wr_accept;   // acceptance reported as completion
Symptom

A producer/consumer handshake fails intermittently. The consumer sees the flag set and reads data that has not been updated. Adding a delay in the consumer makes it disappear, which sends the investigation toward the consumer's code.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  at acceptance : correct reported_done=0 | accept-is-visible reported=1
  accept-is-visible variant flagged premature done   : ok
Root Cause

Acceptance and visibility were treated as the same event. The producer's data write was accepted — the device owns it and will not lose it — but it was not yet observable, so the producer proceeded to set the flag and the flag became visible first.

The window is the device's commit latency, which is why a consumer-side delay hides it perfectly and why the bug appears to be in the consumer.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign wr_reported_done = wr_commit;
if (wr_reported_done && !wr_commit) premature_visible_err <= 1'b1;

Two distinct events with the completion gated on the later one.

Prevention

Probe the address between acceptance and commit and assert the old value is still returned. A test that probes only after commit passes on both designs.

5

A checker that could not see the violation it was written for

CHECK-TOO-EARLY
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (wr_reported_done && pending_q && !wr_commit) premature_visible_err <= 1'b1;
Symptom

None. The check exists, reads correctly, and never fires — including on the deliberately broken variant it was written to catch. Mutation testing reported the design as covered.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  accept-is-visible variant flagged premature done   : ok
Root Cause

pending_q is set by the acceptance edge. On the cycle the violation occurs it is still zero, so the condition is false exactly when it needs to be true. The check samples state that the violating transition is still in the process of creating.

This is the state-versus-transition mistake in its most compact form: the extra term reads as a sensible guard and silently makes the checker unreachable.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (wr_reported_done && !wr_commit) premature_visible_err <= 1'b1;

On the correct design reported_done is wr_commit, so this is unreachable by construction — which is the correct meaning of "never fires on a good design".

Prevention

For every checker, name the exact cycle on which it should fire and confirm the signals it reads have the values you expect on that cycle, not one cycle later.

6

A fence passes while writes are still in flight

FENCE-EARLY
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (acc_accept) outstanding_q <= outstanding_q + 1;
if (acc_commit) outstanding_q <= outstanding_q - 1;
Symptom

Ordering violations after a fence, under sustained traffic only. The fence appears to work — it does wait sometimes — but occasionally passes with writes uncommitted. Burst-then-drain tests pass.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  3 cycles of accept+commit together : outstanding=4
  the case-based counter held steady                 : ok
Root Cause

Two non-blocking assignments to one variable: the second wins, so a cycle with an accept and a commit decrements instead of holding. The outstanding count drifts toward zero under steady traffic, and the fence reads zero while work is in flight.

Seventh appearance of this defect in this track, and the first where the consequence is a correctness violation rather than a wrong statistic.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
case ({acc_accept, acc_commit})
  2'b10: outstanding_q <= outstanding_q + 8'd1;
  2'b01: outstanding_q <= outstanding_q - 8'd1;
  default: ;                                  // both or neither: hold
endcase
Prevention

Test the cross of accept and commit, not each alone. Any counter with independent increment and decrement paths deserves this test automatically.

7

A device-internal engine corrupts host-visible memory

OUT-OF-SCOPE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign in_scope = access_valid;      // every path assumed coherent
Symptom

Host-visible memory changes without any host write. It correlates with device-internal activity — a maintenance engine, a scrubber, a management operation — and never with host traffic. Host-side coherence counters show nothing because nothing host-side happened.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  side path : correct in_scope=0 out=1 | assume-all in_scope=1
  assume-all variant flagged an unsafe assumption    : ok
Root Cause

The design assumed every path to the media was inside the host's coherence contract. An agent reaching the media by another route is not — the host cannot mediate what it does not see — so its writes are invisible to the coherence machinery and its reads may observe values no host agent could.

The sourced contract places coherency management with the host, which is precisely a statement about which paths are covered.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign in_scope     = access_valid && via_host_path;
assign out_of_scope = access_valid && via_side_path;
if (via_side_path && in_scope) unsafe_assumption_err <= 1'b1;

Make the scope explicit and count what falls outside it, so the answer is a number rather than an assumption.

Prevention

Enumerate every path that can reach the media, not just the host one. The question "what else can write this?" is a design-review question, and a design that has never asked it has assumed the answer is "nothing".

20. Design Review

  1. Is ordering scoped to the address or to the device? Both extremes are wrong in different ways.
  2. Can a read-modify-write be interrupted, and what holds the location?
  3. Is a hold bounded? An atomic whose write never arrives blocks the line forever.
  4. Are acceptance and visibility distinct events, and which one reports completion?
  5. Which agents are inside the coherence contract, and what reaches the media outside it?
  6. Does a read to an address with an uncommitted write forward, stall, or return media?
  7. What exactly does a fence wait for, and what counts it?
  8. Which three counters must be zero for the life of the product?
  9. What is the peak accepted-but-invisible window?
  10. What happens on reset with uncommitted writes?

21. How This Appears in Real Engineering

Architecture. The ordering scope decision is worth up to two orders of magnitude of throughput and is usually made once, early, and never revisited.

RTL. Forwarding is a small structure that converts a correctness requirement into a cheap one. Without it the same guarantee costs a media round trip per same-address pair.

DV. Every guarantee here needs two agents or two accesses in flight. A single-access testbench verifies none of them while appearing thorough.

Post-silicon. The three alarms are the difference between a diagnosable corruption and a multi-week investigation, because all three failures return plausible values.

Software. Atomic-heavy data structures on far memory are a placement problem: the measured ratio here was nearly one blocked access per atomic, and that ratio worsens with contention.

22. Common Misconceptions

BeliefCorrection
Reachable memory is usable memoryReachability is necessary and far from sufficient
Ordering means serialising the deviceOrder the address; global ordering costs ~100×
Software can build atomics from a read and a writeThe gap between them is the whole problem
Accepted means visibleTwo distinct events with a real gap
Coherence is universalIt has a scope, and side paths fall outside it
A fence is cheapIt costs everything outstanding at the time
One access at a time proves the semanticsEvery guarantee here needs two
A counter bug is a statistics bugHere it makes a fence pass early

23. Ordering, Drawn

Architectural teaching state machine for one address. The address starts idle. An accepted access moves it to in-flight. While in flight, another access to the same address is deferred and returns to in-flight, while an access to a different address does not affect this state at all. A commit returns the address to idle. A read arriving while in flight is served by forwarding rather than by waiting.IDLEINFLIGHTFORWARDEDDEFERREDaccess acceptedaccess acceptedcommitcommitread, same addressread, same addressvalue returnedvaluereturnedwrite, same addresswrite, same addressprior commitprior commit

Teaching model — this is per address, not per device, and that is the whole point. A different address has its own copy of this machine and is unaffected by anything here. The two right-hand states are the two ways a same-address conflict resolves: a read can be forwarded cheaply, while a write must be deferred.

24. Interview Reasoning

25. Exercises

  1. Analysis. A device reports ordered=1000, forwarded=0, stalls=1000, and poor throughput on a workload with heavy same-address reuse. Explain what is missing, estimate the cost using 100 ns media latency, and name the structure that would fix it.

  2. Design. Add a bound to the atomic hold so a read-modify-write whose write never arrives cannot block a location forever. State what the bound must do to the partially completed operation and why that is harder than aborting a read.

  3. RTL task. Extend order_domain to order same-address accesses only when at least one is a write. State the new invariant and name the workload that benefits most.

  4. Assertion task. Write the property that catches a fence retiring with outstanding work, then explain why the property must read the outstanding count rather than the fence's own state.

  5. Debug task. A producer/consumer handshake fails only when the consumer runs without a delay. Give your investigation order and the single directed test that identifies the cause.

  6. Design review. A colleague proposes reporting write completion at acceptance "because the device never loses an accepted write". Give the strongest version of that argument, then name exactly what it breaks and why the argument is true and irrelevant.

26. Summary

A location you can reach is not yet memory.

  • Memory is a promise set: same-address ordering, atomicity, a defined visibility point, and a stated coherence scope. All four are free locally and none is free across a link.
  • Order the address, not the device. Global ordering is safe and costs up to two orders of magnitude; no ordering breaks programs observing their own writes.
  • Atomicity requires holding the location, because the requester cannot close the gap between a read and a write — and across a link that gap is hundreds of nanoseconds.
  • The hold is a liveness hazard its own safety mechanism creates, and needs a bound.
  • Accepted is not visible. They are distinct events with a real gap, and conflating them breaks every flag-guards-data handshake.
  • Coherence has a scope. The sourced contract places it with the host, so paths the host does not mediate fall outside it — and the question is usually unasked rather than answered wrongly.
  • Forwarding makes ordering cheap: seven of nine same-address cases avoided a media commit in the measured run.
  • Measured: four atomic sequences blocked three unrelated accesses — atomics on contended far memory are a placement problem.
  • The two-assignment counter defect appeared for the seventh time, and here it made a fence pass early — a counter bug becoming an ordering violation.
  • Verification lesson: a checker that sampled state the violating edge was still creating could not see its own violation, and the baseline caught it before mutation testing did.

Chapter 9.4 walks the read path end to end, and 9.5 takes the write path — which is harder, precisely because of the visibility distinction this chapter drew.

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.