Skip to content
VLSI Mentor

CXL · Module 10

Type 3 Devices

The device that lends and never borrows: interleaving that keeps every channel busy, capacity that must be backed by real media, errors that must be reported rather than returned, and why removing one engine is what lets this class scale. Seven RTL models, twenty-six mutations, twenty-six killed.

Chapter 10.2 built the hardest device in the taxonomy — one that borrows and lends at the same time, and must ask permission to use memory it physically owns.

Take away the borrowing and almost all of it disappears.

1. The Engineering Problem — A Server Runs Out of Memory Slots Before It Runs Out of Work

A socket has a fixed number of DDR channels. Adding memory beyond them is not a matter of cost or willingness — the pins do not exist. Meanwhile working sets grow, and a server with idle cores and full memory is a machine that has stopped scaling for a reason that has nothing to do with compute.

The fix is memory that arrives over a link rather than over dedicated channels. And the moment you propose that, the design question becomes narrow and specific:

What is the least a device can be, and still be memory?

Not "how do we build an accelerator that also has memory" — that was 10.2, and it was hard. This is the opposite exercise: strip away everything that is not required to serve host memory accesses, and see what remains.

What remains is a Type 3 device, and its simplicity is the entire product.

2. The One-Sentence Model

A Type 3 device only answers. It exposes memory to the host and never originates a coherent request of its own, so it has no cache, no coherence agent tracking its own copies, no second identity space, no permission check on its own memory, no handover, and no cycle between two directions — and that is precisely why you can put many of them in a system.

Call it the responder. Everything else follows: with one relationship instead of two, the device's hard problems become memory problems — interleaving, capacity, media health — rather than coherence problems.

3. What This Chapter Owns

GroundOwner
CXL.mem as a protocol, and what it costsModule 9 · 9.6
The borrower, and the borrower-lender10.1 · 10.2
What a memory expander must contain, and why it scalesthis chapter
Choosing between the three types10.4
Why memory expansion matters; capacity scalingModule 11
Memory pooling across hostsModule 12
Real expander products and persistent-memory devicesModule 17

Module 9 taught the protocol a Type 3 speaks. This chapter is about the device: the media behind the protocol, the decode in front of it, the capacity contract, and the one structural fact — that it never asks — from which its scalability follows.

4. What Is Left After Removing One Engine

A diagram contrasting a Type 2 device with a Type 3 device. The Type 2 column contains a cache engine, a coherence agent, a permission check, two identity spaces and a memory engine. The Type 3 column contains only the memory engine, fed by an address decode and backed by media banks. Arrows show that removing the cache engine removes the coherence agent, the permission check and the second identity space along with it, leaving a device whose remaining problems are interleaving, capacity and media health.cache engineType 2 onlycoherence agentgoes with itpermission checkgoes with itaddress decodeinterleavememory enginethe whole devicemedia banksreal concurrencymany per hostno tracking costrequiresrequiresenables12

Architectural. The arrows are dependencies, not signals.

The left column is what a Type 3 does not have, and it is a chain rather than a list. Removing the cache engine removes the coherence agent that tracked its contents, which removes the permission check on local memory, which removes the second identity space and the handover and the cycle between directions. One deletion, six consequences.

The right column is what that buys: many of these devices per host, because a memory device adds no per-device coherence tracking state. §12 measures it.

5. Teaching-model boundary

6. RTL 1 — Consecutive Addresses Must Reach Different Channels

The first thing a memory expander does with an address is decide where it lives, and that decision determines whether the device has bandwidth at all.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Interleave on the bits just above the granularity, so consecutive
  // granules land on different channels.
  wire [1:0] low_sel  = acc_addr[GRAN_LOG2+1 -: 2];
  assign channel   = NO_INTERLEAVE ? 2'd0 : (HIGH_BITS ? high_sel : low_sel);
  // Whatever selects the channel must be removed from the per-bank address,
  // or two different addresses land on the same bank location.
  assign bank_addr = ... {acc_addr[11:GRAN_LOG2+2], acc_addr[GRAN_LOG2-1:0]};
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1: consecutive addresses must reach different channels ===
  16 consecutive granules : ch0=4 ch1=4 ch2=4 ch3=4
  channel distribution matched an independent oracle     : ok
  the load was spread exactly evenly, 4 per channel      : ok
  no-interleave variant   : ch0=16 ch1=0 ch2=0 ch3=0
  the no-interleave variant sent everything to one channel : ok
  exact decode : addr0->ch0/bank0 addr4->ch1/bank0 addr8->ch2 addr16->ch0/bank4
  consecutive granules landed on channels 0, 1, 2        : ok
  two different channels reuse bank address 0            : ok
  the wrapped granule advanced the bank address to 4     : ok

Interleaving is what turns four channels into four times the bandwidth. A sequential stream is the common case, and without interleaving every access in it queues on one channel while three sit idle — 16 versus 0, 0, 0 in the measured contrast.

The second half is the part that gets designed wrong. Whatever bits select the channel must be removed from the per-bank address. Note addr0 and addr4 both map to bank address 0 on different channels: that is correct, and it is what lets each channel use its full address space. Leave the channel bits in and every channel uses only a quarter of its own banks — the device advertises its capacity and can physically reach a fraction of it.

An even distribution proved nothing

The mutation that shifted the channel-select bits by one survived the first run, and the reason is instructive. Over a strided stream, selecting addr[4:3] instead of addr[3:2] still produces a perfectly even 4/4/4/4 distribution — it just assigns the wrong addresses to each channel.

Evenness is a statistic; a decode is a function. The fix was to assert the exact channel and the exact bank address for specific addresses, which is the same "assert the value, not the property" lesson that 10.1's victim-rotation check taught in a different disguise.

7. RTL 2 — Throughput Comes From Banks Busy At Once

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  wire bank_free = (timer_q[bank] == 8'd0);
  assign accept  = issue && (IGNORE_BUSY || bank_free);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP2: throughput comes from banks busy at once ===
  one access per bank : issued=4 busy=4 peak=4
  four banks were busy simultaneously                    : ok
  four accesses to ONE bank : issued=5 blocked=3 | ignore-busy issued=8 overlap=1
  the busy bank pushed back                              : ok
  the ignore-busy variant overlapped two media accesses  : ok

This is 9.6's concurrency argument arriving at the media. A bank serves one access at a time, so the device's throughput is the number of banks it can keep busy — and §6's decode is what determines whether traffic reaches them all.

The ignore-busy variant is the media version of every "faster because wrong" defect in this track: it issues 8 where the correct design issues 5, by overlapping two accesses on media that can physically serve one.

8. Waveform — Spread Across Banks, Then Hammering One

The same ten cycles of demand, decoded two ways

10 cycles
Ten clock cycles traced from the RTL. An access is offered every cycle. For the first four cycles the accesses go to banks zero, one, two and three in turn and every one is accepted, with the busy count rising to four. From cycle five onward every access targets bank one, which is busy for four cycles at a time, so accepts become rare and the blocked count climbs steadily to four while the busy count falls away.spread — all acceptedspread — all acceptedall on one bank — mostly blockedall on one bank — mostly blockedfour banks busy at once: full bandwidthfour banks busy at once:full bandwidthsame demand, one bank: blocked climbssame demand, one bank:blocked climbsclkissuebank0123111111acceptbanks_busy0123432211issued0123444555blocked0000012234t0t1t2t3t4t5t6t7t8t9

Teaching-model timing derived from the simplified RTL in this chapter. Not CXL or DRAM timing.

The demand is identical in both halves — issue is high for all ten cycles. The first half accepts four of four; the second accepts one of six.

Nothing changed except which bank the addresses landed on, and that is a property of the decode, not of the media. A memory expander's bandwidth is mostly an address-mapping decision, which is why §6 comes before §7 and why the interleave granularity is one of the few genuinely consequential knobs on this class of device.

9. RTL 3 — Advertised Capacity Must Be Backed

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign in_advertised = (acc_addr < ADVERTISED[7:0]);
  assign backed        = (acc_addr < PHYSICAL[7:0]);
  assign serve         = acc_valid && in_advertised && (SERVE_GAP || backed);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP3: advertised capacity must be backed by media ===
  addr 10 (backed)    : advertised=1 backed=1 serve=1
  an address inside real media is served                 : ok
  addr 80, honest device : advertised=0 serve=0
  an honest device never advertised it                   : ok
  addr 80, over-advertised : advertised=1 backed=0 serve=0 | serve-gap serve=1
  the over-advertising device refused what it cannot back : ok
  the serve-gap variant answered for absent media        : ok
  unbacked accesses=1 | serve-gap phantom flag=1
  the serve-gap variant was caught inventing memory      : ok

This is a Type 3's version of 10.1's negative obligation, and it is sharper here because a memory device's entire purpose is to have the memory it claims.

Three states, not two. An address can be outside the advertised range, inside the advertised range and backed by media, or inside the advertised range and not backed. The third only exists if the device advertised more than it has, and it is the interesting one: the host will use that memory, because the device said it was there.

The serve-gap variant is the dangerous behaviour — it answers, with whatever its buffers held. A refusal is recoverable and diagnosable; a fabricated response corrupts something far away and is attributed elsewhere. Note that even the over-advertising device does the right thing at access time by refusing; the defect is in what it advertised, and the counter n_unbacked is what makes that visible before anything corrupts.

10. RTL 4 — It Answers, and Never Asks

The structural fact from which everything in §4 follows.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign out_req = ORIGINATES && want_own_access;
  ...
      // The buffer is transit storage: it must be released at completion.
      // Holding data past completion turns it into an untracked cached copy.
      if (in_complete) buf_holds_q <= BUFFER_AS_CACHE ? 1'b1 : 1'b0;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP4: a Type 3 answers and never asks ===
  internal agent wants a line : correct out_req=0 | originating variant out_req=1
  the Type 3 device originated nothing                   : ok
  and was caught originating a coherent request          : ok
  request staged : buffer holds=1
  completed : correct buffer=0 | buffer-as-cache buffer=1 stale flag=1
  the buffer released at completion                      : ok
  the buffer-as-cache variant kept an untracked copy     : ok

A Type 3 has storage that is not a cache, and the distinction is one of the easiest to lose. A staging buffer holds data in transit and is released when the transaction completes. A cache holds a copy for reuse — and a copy held for reuse in a device with no coherence agent is a copy nobody is tracking.

The BUFFER_AS_CACHE variant is a natural optimisation: the data is already there, why throw it away? Because the host has no idea the device still holds it, so a subsequent write goes unnoticed and the retained copy is silently stale. Turning a buffer into a cache turns a Type 3 into a broken Type 1.

The ORIGINATES variant is the same boundary from the other side. A Type 3 that issues its own coherent request is a device the host is not tracking as a caching device, participating in coherence anyway.

11. RTL 5 — Report, Never Return

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Corrected data is still good data. Uncorrected data is not data at all.
  assign data_valid   = read_valid && (SILENT_UE || !uncorrectable);
  assign report_error = read_valid && uncorrectable && !SILENT_UE;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP5: a memory device must never return data it could not reconstruct ===
  5 correctable reads : CEs=5 degraded=1 data_valid seen=0
  the device flagged itself degraded past the threshold  : ok
  uncorrectable read  : correct data_valid=0 report=1 | silent variant data_valid=1 report=0
  the correct device reported instead of returning data  : ok
  the silent variant returned the corrupt data           : ok
  the silent variant was caught returning unreconstructable data : ok

A memory device is the last line of defence for its own media, and it is the only party that knows whether the bits it read were reconstructable. Nothing downstream can tell.

The two error classes have opposite handling and the distinction is the whole design:

ClassDataAction
correctablegood — it was rebuiltcount it
uncorrectablenot data at allreport it

The degraded flag is the useful one operationally. Five correctable errors crossed the threshold and the device declared itself degraded — before any uncorrectable error occurred. A rising correctable rate predicts an uncorrectable one, which makes it the difference between scheduled replacement and an outage.

The silent variant returns the corrupt data with report_error low. No party downstream can distinguish that from a successful read, which puts it in the same family as 9.5's silent write failure: the device is the only one who could have said, and it did not.

A sequence diagram with four lifelines: the host, the device decode, two media channels, and the error path. The host issues four consecutive addresses. The decode sends each to a different channel, so all four are accepted and served in parallel. A later read encounters an uncorrectable media error, and the error path reports it rather than returning the bits, while a correctable error on an earlier read was counted and its data returned normally. A note records that the device is the only party able to tell the two apart.Four addresses, four channels — and the one read that must not returnhostdecodemedia channelserror path4 consecutiveaddressesone per channel —all in parallelread 3: correctedgood data, CEcountedread 4: NOTreconstructableerror reported — nodata returned

Rows three and five are the distinction the whole error path exists to make. Corrected bits are data; unreconstructable bits are not, and only the device can tell them apart — which is why returning the second as though it were the first is the one failure nothing downstream can catch.

12. RTL 6 — Why This Class Scales

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign admit_caching = attach_caching &&
                         ((track_used_q + CACHE_TRACK_COST[15:0]) <= TRACK_BUDGET[15:0]);
  // Memory devices are bounded by decode capacity, not by tracking state.
  assign admit_memory  = attach_memory && (n_memory_q < ADDR_SLOTS[7:0]) && ...
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6: why memory devices scale and caching devices do not ===
  6 caching attach attempts : admitted=4 tracking used=32 refused=2
  only four caching devices fitted the tracking budget   : ok
  they consumed the entire budget of 32                  : ok
  10 memory attach attempts : admitted=8 tracking used=32
  exactly eight memory devices fitted the decode capacity : ok
  and they added nothing at all to the tracking budget   : ok
  charge-memory variant : caching=4 memory=0
  no-budget-check abuse : caching=14 tracking used=112 budget=32 flag=1

Four caching devices exhausted the tracking budget; eight memory devices added nothing to it. That is the whole scalability argument in two lines.

The reason is 10.1's asymmetry read backwards. A caching device holds copies of host memory, so the host must track which device holds what — per-device state, and published material puts CXL 3.0's caching fanout at up to 16 devices. A memory device holds no copies of anything, so there is nothing to track. Its bound is address space and decode capacity, which are much larger and much cheaper to extend.

This is why memory pooling is a Type 3 story. CXL 2.0 introduced pooling and CXL 3.0 added sharing, and both are practical for the device class that costs the host nothing per device.

13. RTL 7 — Even Use, and Media Health

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP7: is the media being used evenly, and is it healthy? ===
  requests=40 served=34 blocked=6 channels=8/9/9/8 imbalance=2
  requests, services and blocks matched an independent oracle : ok
  after 12 accesses all to channel 0 : ch=20/9/9/8 imbalance=12 (oracle 12)
  the imbalance matched an independently computed spread : ok
  a skewed stream produced a large, visible imbalance    : ok

Imbalance is this chapter's signature counter, and it is the one a Type 1 or Type 2 has no equivalent of. A striding stream produced a spread of 2 across four channels; a skewed stream produced 12.

That number distinguishes two failures that look identical from outside — the device is slow because the media is slow, versus the device is slow because three of its four channels are idle. The second is an address-mapping problem with a software or configuration fix; the first is not. Without the counter, both present as low bandwidth with blocked accesses.

The mutation that pinned imbalance to zero survived the first run, because the original assertion was a bound — imbalance stays small — and a permanently-zero counter satisfies it. Asserting a bound on a counter that is supposed to move cannot detect a counter that never moves.

14. Quantitative Reasoning

Illustrative, with assumptions stated.

What interleaving is worth

From §8: identical demand, 4 accepted spread across banks versus 1 accepted on a single bank over six cycles.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
banks busy, spread   : 4 concurrent -> 4 accesses per BUSY_TIME
banks busy, one bank : 1 concurrent -> 1 access  per BUSY_TIME

A 4× throughput difference from address mapping alone, with the same media, the same link and the same demand. Generalising to C channels, a sequential stream achieves C× the single-channel bandwidth with interleaving and 1× without — which makes the decode the highest-leverage design decision in the device.

Choosing the granularity

The granularity trades two effects:

GranularitySpreadLocality
very fineexcellentpoor — one access can split
very coarsepoor — long runs on oneexcellent

The useful rule: the granularity should be at least one access wide and no wider than the shortest run you expect to see. Below the first, single accesses split across channels; above the second, sequential streams stop spreading. Both failures show up as low bandwidth with high imbalance.

The cost of over-advertising

A device advertising 96 lines with 64 present has 32 lines of phantom capacity — 33% of its advertised memory does not exist. If the device refuses those accesses, the system sees errors on a third of its address range. If it answers them (§9's variant), it silently corrupts a third of its address range.

Neither is a graceful degradation, which is why advertised capacity is a hard contract rather than a hint, and why n_unbacked belongs in silicon telemetry rather than only in bring-up.

Scaling contrast

Using §12's teaching units: a tracking budget of 32 at 8 units per caching device admits 4 caching devices. The same budget admits as many memory devices as decode capacity allows — 8 in the model, and the bound moves with address space rather than with host storage.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
caching devices : bounded by host tracking state
memory devices  : bounded by address space and decode

Those two bounds grow differently, which is the structural reason capacity expansion and pooling are Type 3 stories and coherent acceleration is not.

15. Assertions

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

Safety

PropertyIntent
Decode is exacta given address reaches a specific channel and bank address
Channel bits removedtwo channels reuse the same bank address space
No bank overlapa bank never serves two accesses at once
Capacity backednever serve an address without media behind it
No originationa Type 3 issues no coherent request of its own
Buffer is not a cachestaging storage is released at completion
Never return unreconstructable datauncorrectable implies report, not data
Tracking budgetadmitted devices never exceed the declared budget
Counter conservationserved ≤ requested
Imbalance exactnessreported spread equals an independently computed one

Liveness

PropertyAssumption it needs
A blocked access is eventually acceptedthe bank timer expires
A staged request eventually completesmedia responds
A refused attachment eventually succeedssome device detaches

Performance goals

GoalMeasured by
Channels used evenlyimbalance
Bank concurrency achievedpeak busy banks
Blocked fraction boundedblocked over requested
Correctable error rate stableCE count and the degraded flag

16. Mutation Testing

Twenty-six mutations. Twenty-six killed.

MutationResult
Interleave selects the wrong address bitskilled
Every access sent to one channelkilled
Channel bits left in the per-bank addresskilled
All channel traffic counted against channel 0killed
An access issued into a busy bankkilled
Bank overlap not reportedkilled
Blocked media accesses not countedkilled
Bank concurrency lags the access being issuedkilled
Every advertised address assumed backedkilled
Device answers beyond its mediakilled
Phantom service not reportedkilled
Unbacked accesses not countedkilled
A Type 3 originates its own coherent requestkilled
Origination not reportedkilled
Staging buffer kept past completionkilled
Untracked retained copy not reportedkilled
Unreconstructable data returned as validkilled
Uncorrectable error never reportedkilled
Rising correctable rate never escalateskilled
Silent corruption not reportedkilled
Memory decode capacity off by onekilled
Caching devices charged nothing for trackingkilled
Budget checker guarded by the fault it detectskilled
Blocked accesses counted as servedkilled
Conservation law disabledkilled
Channel imbalance always reported as zerokilled

The first run scored 21 of 26 — the largest number of escapes in this batch — and they sort cleanly into three lessons already established in earlier chapters, arriving in new disguises.

Two were assertions that were true and useless. The channel distribution was asserted as even, and a decode selecting the wrong address bits is still perfectly even — it merely sends the wrong addresses to each channel. The imbalance counter was asserted with a bound, and a counter pinned to zero satisfies any upper bound. Both were fixed by asserting exact values: the specific channel and bank address for specific addresses, and the imbalance against an independently computed spread.

Two were untested capacity paths — offering more memory devices than decode slots, and the per-bank address never being checked at all.

One needed an abuse instance. The tracking-budget checker is unreachable on a design that honours its budget, so a NO_BUDGET_CHK instance was added that admits past its own limit, exceeding a declared budget of 32 with 112 units and tripping the checker. Note the checker is deliberately written without a guard on that parameter — the self-disabling-checker defect from 9.1 and 9.6, whose mutation re-adds the guard and is killed by exactly that instance.

The recurring pattern across this batch is now unambiguous. Nearly every mutation escape has been a stimulus or assertion-precision problem rather than a missing check. The instinct on an escape is to add an assertion; the correct move is usually to ask what state was never reached, or what value was asserted as a property when it should have been asserted exactly.

17. Verification Plan

AreaApproach
DecodeStrided sweep against an oracle; exact channel and bank address per address; no-interleave and high-bit variants
MediaOne access per bank, then repeated access to one bank; an ignore-busy variant
CapacityBacked, unadvertised, and advertised-but-unbacked; a serve-gap variant
RoleInternal demand with no origination; an originating variant and a buffer-as-cache variant
ErrorsCorrectable run past the threshold, then uncorrectable; a silent variant
ScalingCaching attach to budget exhaustion; memory attach past decode capacity; charge-memory and no-budget-check instances
CountersIndependent oracle; skewed stream for imbalance; a conservation abuse instance

The coverage cross is access pattern × channel distribution × media state: sequential / strided / hot-spot, crossed with balanced / skewed, crossed with idle / busy / degraded. The hot-spot × skewed × busy point is where the decode, the bank concurrency and the imbalance counter all interact, and it is the operating point a badly-mapped workload actually occupies.

18. Silicon Observability

CounterDiagnoses
per-channel access countswhether the decode is spreading traffic
imbalanceaddress-mapping problem versus media problem
peak busy bankswhether media concurrency is being reached
blocked accessesbank contention
requests versus servedend-to-end throughput
unbacked accessesadvertised capacity exceeding real media
correctable error count and ratemedia ageing
degraded flagpredictive replacement signal
uncorrectable error countmedia failure
phantom_servecorrectness alarm — must be zero forever
originated, stale_buffercorrectness alarms — must be zero forever
silent_corruptcorrectness alarm — must be zero forever
bank_overlapcorrectness alarm — must be zero forever

The imbalance counter is the one to fight for. It separates two failures that are indistinguishable from outside: slow media, and a decode that is using a quarter of the media it has. One needs different hardware; the other needs a configuration change, and without the counter a team will buy the first to fix the second.

The degraded flag is the one operations will care about most, because it is predictive rather than diagnostic. Everything else on the list explains a failure that already happened.

19. Debug Lab

1

Four channels of media, one channel of bandwidth

NO-INTERLEAVE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign channel = acc_addr[11:10];   // interleave on the high bits
Symptom

The device delivers roughly a quarter of expected bandwidth on sequential workloads and behaves correctly on random ones. Media is healthy, the link is idle, and every channel reports low utilisation individually.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  no-interleave variant   : ch0=16 ch1=0 ch2=0 ch3=0
  the no-interleave variant sent everything to one channel : ok
Evidence

Read the per-channel counters and the imbalance. A sequential stream should spread across all channels; a spread near the total access count means one channel is doing everything.

Root Cause

The channel was selected from high address bits, so a sequential stream stays within one channel for a very long run. Three channels sit idle while the fourth queues.

Random workloads spread naturally regardless of the decode, which is exactly why they hide this.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire [1:0] low_sel = acc_addr[GRAN_LOG2+1 -: 2];
assign channel = low_sel;

Interleave on the bits just above the granularity.

Prevention

Benchmark with a sequential stream, not a random one. Random access is the pattern least sensitive to interleaving, and it is the pattern most benchmarks default to.

2

The device can only reach a quarter of its own media

CHANNEL-BITS-RETAINED
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign bank_addr = acc_addr[9:0];   // channel bits left in
Symptom

The device advertises its full capacity and behaves correctly for a fraction of the address range. Beyond that, accesses alias onto locations already in use — data written to one address appears at another.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  exact decode : addr0->ch0/bank0 addr4->ch1/bank0 addr16->ch0/bank4
  two different channels reuse bank address 0            : ok
  the wrapped granule advanced the bank address to 4     : ok
Evidence

Check the bank address for two addresses that differ only in the channel-select bits. They should map to the same bank address on different channels. If the bank addresses differ, the channel bits were not removed.

Root Cause

The bits used to select the channel were also left in the per-bank address, so each channel only ever uses the subset of its bank addresses whose bits match its own index. The device physically reaches a quarter of what it advertises.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign bank_addr = {acc_addr[11:GRAN_LOG2+2], acc_addr[GRAN_LOG2-1:0]};

Remove the selecting bits, so the decode is a bijection onto the full media.

Prevention

Assert the exact bank address for chosen addresses, and sweep the whole advertised range writing distinct values before reading any back. A spot check inside the working quarter passes.

3

An even distribution that was sending the wrong addresses

WRONG-DECODE-BITS
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire [1:0] low_sel = acc_addr[GRAN_LOG2+2 -: 2];   // off by one bit
Symptom

Channel counters are perfectly even. Bandwidth on sequential streams is roughly half of expected. Everything looks correctly balanced, and the imbalance counter reports zero.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  16 consecutive granules : ch0=4 ch1=4 ch2=4 ch3=4
  exact decode : addr0->ch0/bank0 addr4->ch1/bank0 addr8->ch2
  consecutive granules landed on channels 0, 1, 2        : ok
Evidence

Check the exact channel for specific consecutive addresses. Address 0, 4 and 8 must reach channels 0, 1 and 2. A distribution check cannot detect this — the wrong decode is also perfectly even.

Root Cause

The channel is selected one bit too high, so pairs of consecutive granules land on the same channel before moving on. The distribution is even across a long stream and the short-range spreading — the whole point of interleaving — is halved.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire [1:0] low_sel = acc_addr[GRAN_LOG2+1 -: 2];
Prevention

Assert the decode as a function, not as a statistic. Evenness is a property many wrong decodes satisfy; the exact channel for a known address is satisfied by only one.

4

A third of the advertised memory does not exist

OVER-ADVERTISED
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
localparam ADVERTISED = 96;   // PHYSICAL is 64
Symptom

The system enumerates the full advertised capacity and uses it. Accesses to the upper range either fail with no obvious cause or — worse — return data. Failures cluster in a contiguous high region that no one thinks to correlate with capacity.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  addr 80, over-advertised : advertised=1 backed=0 serve=0 | serve-gap serve=1
  the serve-gap variant was caught inventing memory      : ok
Evidence

Compare the advertised capacity against the physically populated media, and check the unbacked-access counter. Any non-zero value means the host is using memory the device does not have.

Root Cause

Advertised capacity exceeded populated media — a configuration constant that did not track a depopulated build. The host trusts the advertisement, so it allocates and uses the whole range.

Fix

Derive the advertised capacity from the populated media rather than from a constant, and assert serve implies backed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (serve && !backed) phantom_serve_err <= 1'b1;
Prevention

Sweep the entire advertised range at bring-up, writing and reading back distinct values. The unbacked region is contiguous and at the top, so any partial sweep is likely to miss it.

5

A buffer that quietly became a cache

BUFFER-AS-CACHE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// data's already here, keep it for the next access
if (in_complete) buf_holds_q <= 1'b1;
Symptom

The device occasionally returns stale data for an address the host has since written. It requires repeat access to the same address and correlates with locality, so it appears under real workloads and never in sweeps.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  completed : correct buffer=0 | buffer-as-cache buffer=1 stale flag=1
  the buffer released at completion                      : ok
  the buffer-as-cache variant kept an untracked copy     : ok
Evidence

Check whether the staging buffer still holds data after a transaction completes. If it does, the device is holding a copy nobody is tracking.

Root Cause

Staging storage was retained past completion as an optimisation. A Type 3 has no coherence agent, so nothing invalidates that copy when the underlying data changes — the host does not know it exists.

A buffer holds data in transit; a cache holds it for reuse. Only the second needs coherence, and this device has none.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (in_complete) buf_holds_q <= 1'b0;

Release at completion. If reuse is genuinely wanted, the device needs a coherence agent — and then it is not a Type 3 any more.

Prevention

Assert the buffer is empty whenever nothing is in flight. Repeat-access tests are the only functional way to see this, and they are not standard in a memory sweep.

6

Corrupt data returned as though it were good

SILENT-UE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign data_valid = read_valid;   // always return something
Symptom

Silent data corruption traced eventually to one memory region. No errors reported anywhere, no machine checks, no counters moving. Application-level checksums are the first thing to notice, weeks later.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  uncorrectable read : correct data_valid=0 report=1 | silent variant data_valid=1 report=0
  the silent variant was caught returning unreconstructable data : ok
Evidence

Inject uncorrectable errors and check the response is classified as an error, not merely that a response arrived. "A response came back" passes on the broken design.

Root Cause

The device returned bits it could not reconstruct as though they were data. It is the only party that knows — nothing downstream can distinguish corrected data from uncorrected data.

Correctable and uncorrectable have opposite handling: the first is good data that should be counted, the second is not data at all.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign data_valid   = read_valid && !uncorrectable;
assign report_error = read_valid && uncorrectable;
Prevention

Watch the correctable rate and escalate on a threshold. A rising CE rate predicts a UE, which turns an outage into scheduled maintenance.

7

A counter that could not have detected anything

BOUND-NOT-VALUE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
imbalance_q <= 16'd0;   // reduction dropped in a refactor
Symptom

Channel imbalance reports zero on every workload, including deliberately skewed ones. The team concludes the decode is perfect and stops looking at address mapping entirely.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  after 12 accesses all to channel 0 : ch=20/9/9/8 imbalance=12 (oracle 12)
  the imbalance matched an independently computed spread : ok
Evidence

Drive a deliberately skewed stream and check the imbalance moves. A counter that is supposed to vary and never does is broken, not healthy.

Root Cause

The reduction over the channel counters was lost, pinning the output to zero. The original assertion was a boundimbalance stays small — which a permanently-zero counter satisfies perfectly.

This is a general failure of bound assertions on counters that should move. The same shape appeared in 10.1 as distinct-but-wrong victim addresses.

Fix

Compute the expected spread independently in the testbench and assert equality:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
chk(t_im == orc_spread[15:0], "imbalance matched an independently computed spread");
Prevention

For every counter that is supposed to vary, include a stimulus that makes it large and assert the exact value. A bound proves only that the counter is not too big.

8

Two media accesses on a bank that can serve one

BANK-OVERLAP
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign accept = issue;   // media is fast, just send it
Symptom

Reported throughput is excellent — better than the media's specified capability, which nobody questions. Data is intermittently wrong under load, in a way that correlates with access rate to the same bank.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  four accesses to ONE bank : issued=5 blocked=3 | ignore-busy issued=8 overlap=1
  the ignore-busy variant overlapped two media accesses  : ok
Evidence

Compare issued accesses per bank against the bank's busy time. Exceeding one access per busy period means two accesses are overlapping on media that can serve one.

Root Cause

The bank-busy check was removed, so accesses were issued into banks still serving a previous one. Throughput beat the correct design by doing something the media cannot do.

Same family as every other "faster because wrong" defect in this track: the performance gain and the corruption are the same event.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire bank_free = (timer_q[bank] == 8'd0);
assign accept  = issue && bank_free;
if (accept && !bank_free) overlap_err <= 1'b1;
Prevention

Sanity-check reported throughput against the media's theoretical maximum. A device that beats its own media is not fast; it is wrong.

20. Design Review

  1. Which address bits select the channel, and are they removed from the bank address?
  2. What is the interleave granularity, and against which access pattern was it chosen?
  3. Does a sequential stream reach every channel?
  4. Does advertised capacity come from a constant or from the populated media?
  5. What happens to an access inside the advertised range with no media behind it?
  6. Can the device originate a coherent request under any condition?
  7. Is staging storage released at completion, and what asserts it?
  8. What happens on an uncorrectable read — report, or return?
  9. What is the correctable error rate, and what threshold escalates it?
  10. How many of these devices can one host take, and what is the actual bound?
  11. Does any counter distinguish slow media from a skewed decode?

21. How This Appears in Real Engineering

Architecture. The decode is the highest-leverage decision — 4× bandwidth in the model, from address mapping alone.

RTL. Small and disciplined: decode, bank timers, capacity check, error path. The temptation to retain the staging buffer is where a Type 3 design goes wrong.

DV. Sequential streams and repeat-address tests, both of which a random memory sweep omits. Every range check needs its boundaries.

Performance. Imbalance is the counter that separates a media problem from a mapping problem, and the two have entirely different fixes.

Firmware. Advertised capacity must be derived from populated media, and the degraded flag is the interface to predictive replacement.

Post-silicon. The correctable error rate is the only leading indicator in this chapter; everything else explains a failure that already happened.

22. Common Misconceptions

BeliefCorrection
A Type 3 is a Type 2 with the accelerator removedIt is a device with one relationship instead of two
Interleaving is a performance tweakIt is 4× bandwidth on the common access pattern
An even channel distribution proves the decodeSeveral wrong decodes are also perfectly even
Retaining staging data is a free optimisationWith no coherence agent it is an untracked stale copy
Correctable and uncorrectable errors are both errorsOne is good data; the other is not data at all
A quiet error counter means healthy mediaA rising correctable rate is the warning you get
Fanout limits apply to all CXL devicesPublished caching-fanout limits bound .cache devices
A bound assertion on a counter is a checkA counter pinned to zero satisfies any upper bound

23. Interview Reasoning

24. Exercises

  1. Calculation. A device has 8 channels, a 256-byte interleave granularity and a 64-byte access size. Compute how many consecutive accesses are needed to touch every channel, and the achieved bandwidth of a sequential stream as a fraction of peak. Then recompute both for a 4 KB granularity and state which workload each choice favours.

  2. Analysis. A device reports even per-channel counts, an imbalance of zero, and half of expected sequential bandwidth. Explain why the first two facts do not exonerate the decode, and give the single directed test that settles it.

  3. RTL task. Extend interleave_decode to support a configurable channel count that is not a power of two. State what breaks in the bit-slicing approach, what the bank address must become, and the new invariant that must be asserted.

  4. Assertion task. Write the property that proves the decode is a bijection over the advertised range. Explain why a per-address check is insufficient on its own, and what additional structure the testbench needs.

  5. Waveform analysis. Using §8's trace, compute the accept rate in each half and the effective bank concurrency. Then state what the second half would look like with an 8-cycle bank busy time, and which counter would reveal the difference first.

  6. Debug task. A memory expander returns correct data for the first two thirds of its advertised range and aliased data above that. Give your investigation order, the two defects in this chapter that both produce that symptom, and the single measurement that distinguishes them.

  7. Design review. A colleague proposes retaining the staging buffer across transactions as a small read cache, arguing it is a few lines of storage and cannot affect correctness because the device never modifies data. Give the strongest version of that argument, then state exactly what breaks and what the device would have to become for the optimisation to be legal.

25. Summary

A Type 3 device only answers.

  • It exposes memory and never originates a coherent request, which removes — in one chain — the cache, the coherence agent, the permission check, the second identity space, the handover and the cycle between directions.
  • Interleaving is worth 4× on sequential access in the model: same media, same link, same demand, spread across banks versus queued on one.
  • The channel bits must be removed from the bank address, or every channel reaches a quarter of its own media while the device advertises all of it.
  • Advertised capacity is a hard contract. An address inside the advertised range with no media behind it is either an error across a contiguous region or silent corruption of one.
  • A buffer is not a cache. With no coherence agent, staging data retained past completion is a copy nobody is tracking.
  • Report, never return. Correctable errors are good data to be counted; uncorrectable ones are not data at all, and the device is the only party that can tell. The degraded flag is the chapter's only leading indicator.
  • This class scales because it costs the host nothing per device — four caching devices exhausted the tracking budget while eight memory devices added nothing to it. Published fanout limits bound caching devices.
  • Imbalance separates a media problem from a mapping problem, and nothing else does.
  • Verification: 26 of 26 mutations killed, 50 assertions. Five first-run escapes, and the lesson is now unmistakable across this batch: assertion precision and unreached states, not missing checks. An even distribution proved nothing about a decode, and a bound proved nothing about a counter pinned to zero.

Next: 10.4 — Device-Type Selection Discipline, which closes Module 10 with the question these three chapters have been setting up — not which type is best, but what a device becomes when part of it is turned off, and how to keep the answer from surprising you.

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.