Skip to content
VLSI Mentor

CXL · Module 11

Why Memory Expansion Matters

Capacity is welded to the socket: a product of channels, slots and density that no workload respects. What the ceiling costs, how pressure is measured, why free memory on the wrong socket is stranded, and the evidence an architect brings to a capacity review.

Module 10 finished the device taxonomy — what kind of thing a CXL device is, and what each type obliges. Module 11 turns from the device to the system it exists to build, and it starts with the question that has to be answered before any of it is worth doing.

Not what can CXL memory do. What is actually wrong with the memory a server already has?

1. The Engineering Problem — Buying Two Things in a Fixed Ratio

A server is bought as a bundle. Cores and memory capacity arrive welded together, because the memory is attached to the socket and the socket has a fixed number of DDR channels.

The workload does not know this. Some workloads want enormous capacity and modest compute — an in-memory database, a recommendation model's embedding tables, a large graph. Others want the reverse.

Neither can be bought. You buy the ratio the socket offers, and then one of two things happens:

  • Memory runs out first, so cores sit idle in a machine that is "full".
  • Cores run out first, so memory sits unused in a machine that is "full".

Both are the same failure wearing different clothes: a fixed ratio meeting a workload that does not share it. And the second case is worse than it sounds, because that unused memory is not merely idle — it is unreachable by the machine next door that needs it.

That is the problem Module 11 is about. Expansion is not "more RAM". It is decoupling a ratio that hardware welded together.

2. The One-Sentence Model

Capacity is welded to the socket. It is the product of channels, slots per channel and per-module density — three numbers fixed when the board was designed — so capacity cannot be raised later at any price, and capacity that goes unused on one socket cannot serve demand on another.

Call it the welded ratio. Everything in this module is about unwelding it, and everything in this chapter is about proving the weld is real and measuring what it costs.

3. What This Chapter Owns

Memory expansion touches a lot of later curriculum, so the boundaries matter from the first page.

GroundOwner
Why the ceiling exists, and what it coststhis chapter
How CXL raises addressable capacity11.2
Sharing one device between a few hosts11.3
Server designs built around expansion11.4
What AI workloads demand of it11.5

And the deferrals:

Deferred groundOwner
Pools, many hosts, many devicesModule 12
Fabric managers, switchesModules 15, 16
Real expander products and mediaModule 17
Latency anatomy, throughput modellingModule 18
Rack and datacentre architectureModule 23

This chapter quantifies capacity pressure. It deliberately does not decompose latency — that is 18.1's job, and Module 9 already priced what CXL.mem's guarantees cost in 9.6.

Channels, slots per channel and module density multiply to give installed capacity, which is fixed at board design. Installed capacity arrives bundled with a fixed core count. A workload demand meets that bundle and produces one of two outcomes: capacity-bound, where cores sit idle, or compute-bound, where memory sits unused and stranded.channelsfixed by CPU + boardslots / channelfixed by boarddensitythe only headroominstalledcapacitybundled with corescapacity-boundcores sit idlecompute-boundmemory strandedneeds more GBneeds more cores12
Figure 1 — the welded ratio. Installed capacity is the product of three facts fixed at board design, and it arrives bundled with a fixed core count. A workload whose demand does not match that ratio is forced into one of two wasteful outcomes, and the memory-rich outcome strands capacity that a neighbouring machine cannot reach.

The two boxes on the bottom row are the same failure. A fixed ratio met a workload that did not share it, and whichever resource ran out first determined which half of the machine was wasted. Neither outcome is a purchasing mistake — both are forced by the weld.

4. Teaching-model boundary

5. RTL 1 — Capacity Is a Product, Not a Preference

The weld, in hardware.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The ceiling is a product of three physical facts, fixed at board build.
  localparam logic [15:0] CEILING_GB = CHANNELS * SLOTS_PER * DENSITY_GB;
 
  always_comb begin
    fits   = ((committed_q + req_gb) <= CEILING_GB);
    grant  = req_valid && (ELASTIC || fits);
    refuse = req_valid && !ELASTIC && !fits;
  end
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1: locally attached capacity is a product, not a preference ===
  installed = channels x slots x density = 8 x 2 x 64 = 1024 GB
  the ceiling is exactly 1024 GB                           : ok
  6 x 200 GB + exact 24 GB : committed=1024 refused=1 peak=1024
  independent oracle   : committed=1024 refused=1
  committed capacity matched an independent oracle         : ok
  the last 24 GB landed exactly on the ceiling             : ok
  peak recorded the value being created, not the previous one : ok
  elastic variant      : committed=1224 overcommit flag=1
  the elastic variant committed beyond physical memory     : ok

Three multiplicands, and you can change none of them after the board exists. Channels are a property of the CPU and the board. Slots per channel are a property of the board. Density is a property of the modules you can buy — and it is the only one with any headroom, which is why "just buy bigger DIMMs" is the first thing every team tries and the first thing that runs out.

The ELASTIC variant is the marketing model of memory, and it is included because it is how capacity is described in every planning conversation that has not yet met the board. It commits 1224 GB into 1024 GB of physical DRAM and the overcommit checker catches it. Real hardware has no such variant: the request simply fails.

Note where the checker sits. overcommit_err is written without a guard on ELASTIC — guarding it would switch the checker off on the only configuration that can ever trip it, which is the self-disabling-checker defect this track has now seen three times.

6. RTL 2 — The Number That Matters Is Distinct Pages

Sizing memory from access rate is the most common way a capacity estimate goes wrong, in both directions.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign first_touch = acc_valid && !seen_q[page_id];
  // What the counter advances on: a first touch (correct) or any access.
  assign advance = COUNT_ACCESS ? acc_valid : first_touch;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP2: the number that matters is DISTINCT pages, not accesses ===
  20 accesses over 4 pages : distinct=4 accesses=20 | count-access variant distinct=20
  independent oracle       : distinct=4
  distinct-page count matched an independent oracle        : ok
  4 distinct pages from 20 accesses                        : ok
  the count-access variant reported 20 -- 5x the true working set : ok
  and exceeded the page count, which is impossible for a working set : ok
  after a window roll, same 4 pages re-touched : distinct=4
  the window rolled and the seen-set cleared               : ok

Twenty accesses, four pages. A design sized from the access count provisions five times the memory this workload actually needs resident.

The error runs the other way too. A workload that touches a million distinct pages once each has a low access rate per page and an enormous working set — and an access-rate estimate will under-provision it just as badly.

The impossible-value check is the cheap one to keep. A distinct-page count can never exceed the number of pages that exist, so the count-access variant tripped miscount_err the moment it passed 16. That is one comparator, and it catches a whole class of metric that has quietly stopped meaning what its name says.

The window matters as much as the counter. Working set is distinct pages in a window, and the window length is a policy choice: too short and everything looks hot, too long and everything looks like the working set. The roll test proves the seen-set clears — a window that never resets reports the workload's lifetime footprint and calls it a working set.

7. RTL 3 — Pressure Is a State, Not a Comparison

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    case (st_q)
      P_EASY:     if (occupancy_pct >= HI_ENTER) st_n = P_TIGHT;
      P_TIGHT: begin
                  if (occupancy_pct >= CR_ENTER)         st_n = P_CRITICAL;
                  else if (occupancy_pct <  HI_EXIT_EFF) st_n = P_EASY;
                end
      P_CRITICAL: if (occupancy_pct <  CR_EXIT_EFF) st_n = P_TIGHT;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP3: pressure is a state with hysteresis, not a comparison ===
  exactly 80% entered TIGHT (the threshold is inclusive)   : ok
  97% entered CRITICAL                                     : ok
  leaving CRITICAL at 85% : state=1 (1=TIGHT)
  leaving CRITICAL lands in TIGHT, never straight to EASY  : ok
  occupancy 75% (between exit 70 and enter 80) : correct=1 no-hyst=0
  75% stayed TIGHT -- inside the hysteresis band           : ok
  8 cycles oscillating 81/79 : correct transitions=3 | no-hyst transitions=12 osc flag=1
  the no-hysteresis variant transitioned far more often    : ok

Twelve transitions against three, on identical input. Every action a system takes on capacity pressure — reclaiming, migrating a page, refusing an allocation, waking a daemon — is expensive, and a single threshold makes all of them fire on the noise around the boundary.

Hysteresis is not a smoothing filter. It is the statement that entering a state and leaving it are different decisions with different costs. Entering TIGHT at 80% and leaving at 70% means the system commits to a ten-point band of stability before it will reverse a decision it has already paid for.

Leaving CRITICAL lands in TIGHT, never in EASY. A system that drops two states at once discards the intermediate response entirely — it goes from emergency reclaim to doing nothing, and arrives back at the emergency shortly afterwards.

8. Waveform — The Same Occupancy, Two Behaviours

Ten clock cycles. Occupancy walks 60, 60, 81, 79, 81, 75, 96, 90, 60, 60. The hysteretic state goes EASY, EASY, EASY, TIGHT, TIGHT, TIGHT, TIGHT, CRITICAL, CRITICAL, TIGHT, making three transitions. The no-hysteresis state goes EASY, EASY, EASY, TIGHT, EASY, TIGHT, EASY, TIGHT, TIGHT, EASY, making six transitions and asserting its oscillation flag from cycle five onward.quietquietboundary noise 81/79boundary noise 81/79genuine spikegenuine spikeflat design flips; hysteretic holdsflat design flips;hysteretic holdsreal spike — both reactreal spike — both reactclkoccupancy_pct60608179817596906060hyst_stateEASYEASYEASYTIGHTTIGHTTIGHTTIGHTCRITCRITTIGHThyst_trans0001111223flat_stateEASYEASYEASYTIGHTEASYTIGHTEASYTIGHTTIGHTEASYflat_trans0001234556osc_flagt0t1t2t3t4t5t6t7t8t9
Figure 1 — ten cycles of identical occupancy through both designs. The hysteretic FSM makes three transitions and holds TIGHT across the 81/79 boundary noise at cycles 4 to 6; the single-threshold variant flips on almost every sample and raises its oscillation flag at cycle 5. Every flip is a reclaim decision the system pays for.

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

Read cycles 4 to 6. Occupancy moves 79 → 81 → 75, which is noise around a threshold, and the two designs disagree completely: hyst_state holds TIGHT throughout while flat_state flips EASY → TIGHT → EASY.

Then read cycle 7. Occupancy hits 96 — a genuine event — and both designs react. Hysteresis did not make the system less responsive to real pressure. It made it unresponsive to noise, which is the entire distinction.

9. RTL 4 — Two Stalls With Opposite Remedies

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // Capacity is checked first: if the page has nowhere to live, credit is
    // irrelevant. Merging the two loses exactly this ordering.
    cap_stall = want_issue && no_capacity;
    bw_stall  = want_issue && !no_capacity && no_credit;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP4: capacity stalls and bandwidth stalls have opposite fixes ===
  20 cycles : capacity stalls=5 bandwidth stalls=5 issued=10
  oracle    : capacity=5 bandwidth=5
  both stall causes matched an independent oracle          : ok
  exactly 10 cycles actually issued                        : ok
  merged variant : one bucket=10, other=0 (diagnosis unavailable)
  loose-bw abuse instance : misattribution flag=1
  no request offered, both blockers asserted : capacity stalls=5
  no request offered means no stall recorded               : ok

"The workload is stalled" is not a diagnosis, and this is the counter split that turns it into one.

Stall causeWhat it meansThe remedy
capacitythe page has nowhere to livemore memory
bandwidththe path is saturatedmore concurrency

Those remedies cost different money and land in different budgets. A team that reports one aggregate number will buy whichever the loudest person believes, and the merged variant shows exactly what that costs: 10 in one bucket, 0 in the other, diagnosis unavailable.

The ordering is the subtle part. Capacity is tested first because it dominates: if there is nowhere to put the data, credit availability is irrelevant. The LOOSE_BW abuse instance drops that exclusion and is immediately caught reporting a bandwidth stall while capacity was the real blocker — the same event, attributed to the wrong budget.

And a stall requires a request. With want_issue low and both blockers asserted, nothing is recorded: a system that is not asking for memory is not stalled on memory, however full it is.

10. RTL 5 — Free Memory on the Wrong Socket

This is the model that explains why expansion is an architecture change rather than a purchase.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        // Only as much surplus as there is demand to meet is usable; the rest
        // is stranded behind a socket boundary.
        uf_q <= (surplus_sum < deficit_sum) ? surplus_sum : deficit_sum;
        st_q <= (surplus_sum > deficit_sum) ? (surplus_sum - deficit_sum) : 16'd0;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP5: free memory on the wrong socket is stranded ===
  4 nodes : unmet demand=30 usable free=30 stranded=30
  one node is short by exactly 30                          : ok
  only 30 of the surplus could ever be used                : ok
  30 units are stranded behind socket boundaries           : ok
  pooled view : usable=60 stranded=0 hidden flag=1
  the pooled view reported zero stranded and was caught    : ok

Four nodes. One is 30 short. The other three have 60 spare between them. The fleet has ample free memory and one node cannot run.

The pooled view reports zero stranded, because it sums free capacity across the fleet and concludes there is plenty. That number is not wrong arithmetically — it is answering a question nobody asked. Free memory is only useful if the demand can reach it, and a socket boundary is exactly what prevents that.

This is why fleet-average utilisation is such a misleading metric. A fleet at 70% average utilisation can be simultaneously wasting a third of its memory and refusing allocations, and the average will report neither. The two numbers worth reporting are unmet demand and stranded surplus — and their coexistence is the entire business case for expansion and, later, pooling.

11. RTL 6 — The Ceiling Is Felt as a Failure Path

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    alloc_done   = active_q && space_available;
    alloc_failed = active_q && !space_available && !RETRY_FOREVER
                   && (retry_q >= RETRY_LIMIT[7:0]);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6: a capacity ceiling is felt as a failure path ===
  no space, retry limit 4 : retries=4 failures=1 | retry-forever failures=0 retries=18
  the bounded path failed exactly once                     : ok
  after exactly 4 retries                                  : ok
  the retry-forever variant never failed                   : ok
  and was caught livelocking                               : ok
  satisfied, then space removed and idle : failures still=1 retries=4

Nobody experiences a capacity ceiling as missing bytes. They experience it as an allocation that does not return, a job that will not schedule, or a process killed by something that had no idea why.

The failure has to be bounded and visible. The RETRY_FOREVER variant never fails — it simply retries, forever, and the livelock checker catches it. A system that behaves this way converts a diagnosable capacity problem into an undiagnosable hang, and the hang is reported against whatever was unlucky enough to be waiting.

The release test matters more than it looks. After a successful allocation, space is removed and the model idles: no new failure appears, because the satisfied request released. A design that leaves the request active keeps retrying against the new shortage and fails for a request that was already served.

An allocation request checks whether space is available. If yes it is granted and the request is released. If no, the retry counter is checked against its limit. Below the limit the request retries. At or above the limit the bounded design fails visibly and records the failure. A parallel path shows the unbounded design, which returns to retry regardless of the counter and never reaches a failure state.yesnonoyesno limitallocationrequestspaceavailable?granted —request releasedretries atlimit?retryfail visibly,count itunbounded: retryforever
Figure 3 — what a capacity ceiling actually does to a request. The bounded path resolves every allocation as granted or failed within a fixed number of retries; the unbounded path has no exit from the retry loop, so a diagnosable shortage becomes a hang attributed to whatever happened to be waiting.

12. RTL 7 — The Evidence for a Capacity Review

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP7: the evidence an architect brings to a capacity review ===
  requests=30 granted=24 refused=6 pressure cycles=17
  oracle  : requests=30 granted=24 refused=6 pressure=17
  every request resolved as exactly one outcome            : ok
  the conservation law held                                : ok
  abuse: grants with no requests : flag=1

Three numbers decide a capacity argument, and none of them is "we feel short of memory":

NumberWhat it proves
refusalsdemand the machine could not accept
pressure timehow long it ran near the ceiling
residency grantswhat it did manage to hold

The conservation law is what makes them trustworthy. Every request resolves as exactly one of granted or refused, so the two outcomes can never exceed the requests behind them. Without it, a refusal counter can double-count retries and turn a modest shortage into a fabricated crisis — and capacity budgets are approved on these numbers.

13. Quantitative Reasoning

Illustrative, with stated assumptions. None of these figures describes a real product.

The ceiling

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
capacity = channels x slots_per_channel x module_density
         = 8 x 2 x 64 GB
         = 1024 GB

Density is the only term with headroom, and it moves in generational steps rather than continuously. Doubling density doubles capacity and changes nothing about the ratio — the socket still offers one capacity for one core count.

What the ratio costs

Take a workload needing 2 TB resident and 32 cores, on sockets offering 1 TB and 64 cores:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
capacity-driven:  2 sockets  -> 2 TB, 128 cores   (96 cores idle)
compute-driven:   1 socket   -> 1 TB, 64 cores    (workload does not fit)

You buy two machines to use half of one. The 96 idle cores are not a rounding error; they are the price of the weld, paid on every node that has this shape.

Stranding

From §10: unmet demand 30, surplus 60, stranded 30.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fleet free memory      = 60
fleet unmet demand     = 30
free memory that helps = 30
free memory stranded   = 30   (50% of all free memory)

Half the free memory in that fleet cannot be used by the thing that needs memory. Scale the shape, not the numbers: the stranded fraction grows with how unevenly demand is distributed, which is why heterogeneous fleets strand more than uniform ones.

Working set versus access rate

From §6: 20 accesses over 4 pages.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sized from access count : 20 pages
sized from working set  :  4 pages
over-provision factor   :  5x

The two estimates disagree by the reuse factor, and reuse varies by orders of magnitude across workloads. That is why capacity planning needs a distinct-page measurement and not a bandwidth counter.

How long the ceiling is felt

From §12: 17 pressure cycles in 30, 6 refusals in 30.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
time near the ceiling = 17/30 = 57%
refusal rate          =  6/30 = 20%

A machine spending 57% of its life under pressure is not occasionally short of memory — it is running in the regime where every capacity-dependent decision is expensive, which is the argument a capacity review actually needs.

14. Assertions

Icarus Verilog 13.0 is the only simulator installed here, and it does not execute concurrent SVA propertiesunique/priority case qualities are parsed but ignored, and property blocks are not supported. Every property below is therefore implemented as synthesisable checker logic verified procedurally in simulation, not as executed SVA. 47 assertions.

Safety

PropertyIntent
Ceiling honouredcommitted capacity never exceeds the physical product
Peak accuracypeak records the value the current edge creates
Working-set sanitydistinct pages never exceed pages that exist
Window integritythe seen-set clears on every window roll
Pressure legalityCRITICAL exits to TIGHT, never straight to EASY
Stall exclusivitya bandwidth stall is never recorded while capacity blocks
Stall requires demandno request offered means no stall recorded
Stranding honestyusable free never exceeds unmet demand
Bounded failureallocation fails within the retry limit
Releasea satisfied allocation deactivates
Conservationgranted + refused ≤ requested

Liveness

PropertyAssumption it needs
An allocation request eventually resolvesthe retry limit is finite
Pressure eventually leaves CRITICALoccupancy eventually falls

Performance goals — not correctness

GoalMeasured by
Time under pressure boundedpressure cycles over total
Refusal rate boundedrefusals over requests
Stranded fraction lowstranded over total free

These belong in a report that trends, never in an assertion. A performance goal in an assertion produces a regression that fails on a busy day, and a team that learns to ignore that failure will ignore a real one later.

15. Mutation Testing

Twenty-seven mutations. Twenty-seven killed.

MutationResult
Ceiling comparator off by onekilled
Slots-per-channel dropped from the capacity productkilled
Refused capacity requests not countedkilled
Overcommit checker guarded by the fault it detectskilled
Peak committed lags the value being createdkilled
Every access treated as a first touchkilled
Touched pages never recordedkilled
Window roll does not clear the seen setkilled
Impossible distinct count not reportedkilled
Pressure entry threshold off by onekilled
Hysteresis band removedkilled
Oscillation not reportedkilled
Critical drops straight to easy, skipping tightkilled
Bandwidth stall counted while capacity blockedkilled
Capacity stall counted with no request offeredkilled
Stall misattribution not reportedkilled
Stalled cycles counted as issueskilled
All surplus assumed usablekilled
Stranded capacity always reported as zerokilled
Pooled view not flagged for hiding strandingkilled
Retry limit off by onekilled
Allocation failures not countedkilled
Livelock not reportedkilled
A satisfied allocation never releases the requestkilled
Offered requests counted as grantskilled
Conservation law disabledkilled
Pressure time measured as request countkilled

The first run scored 17 of 27, and the ten escapes sorted exactly as the previous batch predicted they would:

Cause of escapeCount
Stimulus never reached the state6
Assertion displayed a value but never checked it2
Checker unreachable without an abuse instance2

Not one escape was a missing checker. Batch 010 closed with the standing lesson that on a mutation escape you should ask what state was never reached and was the assertion exact before adding a check. Applying that rule first, rather than reflexively adding assertions, fixed all ten.

Four are worth recording individually.

A boundary that stimulus never touched. The ceiling comparator <= versus < is invisible unless a request lands exactly on the ceiling. Six 200 GB requests into 1024 GB never do. Adding a final request of exactly 24 GB — taking the total to precisely 1024 — separated them in one cycle.

A value displayed but not checked. peak_committed was printed in the transcript and never asserted, so a lagging implementation shipped through. Printing a number is not checking it, and a transcript line is the easiest place in a whole batch to mistake one for the other.

And the peak needed the creating edge. Even once asserted, the lagged version passed — because committed_q holds the new value on the following cycle, so one idle cycle lets the lag catch up. The fix was to sample the peak on the edge that created it, before any further cycle. This is the third batch in which that exact shape has appeared.

A checker unreachable by construction. The misattribution check can never fire on a correct design, because bw_stall excludes no_capacity by definition. Rather than delete it, a LOOSE_BW abuse instance was added that drops the exclusion, making the checker reachable without driving illegal behaviour into the instance under test.

16. Verification Strategy

AreaApproach
CeilingRequests below, exactly on, and beyond the ceiling; an elastic abuse variant
Working setDistinct-page stimulus with heavy reuse; window-roll crossing; a count-access variant
PressureExact thresholds; band noise; the CRITICAL path; a no-hysteresis variant
StallsBoth causes together and separately; no-request stimulus; merged and loose abuse instances
StrandingUneven demand across nodes; a pooled-view variant
Failure pathUnsatisfiable then satisfiable allocation; a retry-forever variant
CountersIndependent oracle; a conservation abuse instance

The independent oracle is the load-bearing part. Each counter is checked against a value the testbench computes by a different method than the design — a running total in the testbench against the design's registered accumulator, a bit array of seen pages against the design's seen_q vector. A testbench that reproduced the design's own expression would agree with it while both were wrong.

The coverage cross that matters is occupancy × request outcome × pressure state. Below/at/above the ceiling, crossed with granted/refused, crossed with EASY/TIGHT/CRITICAL. The at-the-ceiling × refused × CRITICAL point is where every boundary bug in this chapter lives, and random stimulus reaches it essentially never.

17. Synthesis and Implementation Reality

StructureImplementation consequence
CEILING_GB productresolved at elaboration; no multiplier in hardware
Capacity comparatora 16-bit adder plus compare in the grant path
seen_q bit-vectorone flop per tracked page; a real design needs a hash or CAM
Distinct counterwidth must cover the page count, or it wraps and lies
Pressure FSM2 flops and two comparators; trivial area, high leverage
Hysteresis thresholdstwo constants rather than one — no runtime cost at all
Stall countersone flop-bank per cause; the cost of the diagnosis is linear in causes
Stranded ledgerper-node subtract and compare; scales with node count, not capacity

The most important line in that table is the hysteresis one. Two constants instead of one costs nothing in area, nothing in timing, and it is the difference between a system that reclaims on real pressure and one that thrashes on noise. Cheap structural decisions with large behavioural consequences are the ones worth arguing for in review.

The seen_q row is the honest limitation. One flop per page is fine for a 16-page teaching model and impossible for a real address space; production working-set estimation uses sampling, hashing or aging counters. The invariant taught — distinct, not total — survives the change of mechanism.

18. Silicon Observability

CounterDiagnoses
committed vs installedhow close to the ceiling the machine runs
peak committedthe worst moment, not the average
allocation refusalsdemand the machine could not accept
retries before failurewhether the failure path is bounded
time in each pressure statehow long it runs hot
pressure transitionsthrash, if it is high with low residency
distinct pages per windowthe actual working set
capacity stalls vs bandwidth stallswhich budget to spend
stranded surpluscapacity that exists and cannot be reached
overcommit, livelock, miscountcorrectness alarms — must be zero forever

The pair to fight for is capacity stalls against bandwidth stalls. They are two counters and one comparator, and they decide whether the next purchase is memory or interconnect. Everything else on this list refines an answer that pair gives at the top level.

Pressure transitions with low residency is the thrash signature — many transitions, little time actually spent in the state. It is the counter that would have caught the no-hysteresis design in the field, and it costs one register.

19. Debug Lab

1

A capacity estimate that was five times too large

ACCESS-RATE-SIZING
Symptom

A service is provisioned from a memory-bandwidth trace and the machines arrive with five times the memory the workload ever uses. The capacity is real, the utilisation is 20%, and the finance review asks why.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  20 accesses over 4 pages : distinct=4 accesses=20
  the count-access variant reported 20 -- 5x the true working set : ok
Evidence

Compare distinct pages touched in a window against total accesses in the same window. If the ratio is large, the workload has high reuse and an access-rate estimate has over-provisioned by exactly that factor.

Likely Causes

Sizing from a bandwidth counter; a window long enough that the lifetime footprint is reported as the working set; a distinct counter that never resets.

Debug Sequence

Instrument distinct-page count per window. Sweep the window length and watch where the number stabilises — that plateau is the working set. Then compare against what was purchased.

Root Cause

Access rate and residency are different quantities. Twenty accesses to four pages need four pages resident, not twenty. The same error under-provisions a low-reuse workload just as badly.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign first_touch = acc_valid && !seen_q[page_id];
assign advance     = first_touch;              // not acc_valid
if (cnt_q > PAGES[7:0]) miscount_err <= 1'b1;  // impossible-value guard
Prevention

Assert the impossible value. A distinct count above the number of pages that exist is a broken metric, and one comparator catches it before anyone provisions from it.

2

The reclaim daemon runs constantly and reclaims nothing

NO-HYSTERESIS
Symptom

CPU time in a memory-management thread is high and steady. Occupancy hovers near a threshold. Almost no memory is actually reclaimed, and latency is worse than when the machine was fuller.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  8 cycles oscillating 81/79 : correct transitions=3 | no-hyst transitions=12 osc flag=1
  and was caught oscillating                               : ok
Evidence

Count state transitions and time-in-state together. Many transitions with little residency is thrash; the state is being entered and abandoned before its action completes.

Likely Causes

One threshold used for both entering and leaving; a threshold placed where the workload's steady state sits; a sampling period shorter than the action's completion time.

Debug Sequence

Plot occupancy against the threshold. If it straddles it, the design has no hysteresis band. Confirm by checking whether any state is left on the cycle after it is entered.

Root Cause

Entering and leaving a state were the same decision. Every crossing of the boundary triggered an expensive action that the next sample immediately reversed.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
localparam logic [7:0] HI_ENTER = 8'd80;
localparam logic [7:0] HI_EXIT  = 8'd70;   // a band, not a line
if (st_n != st_q && since_q == 8'd0) oscillation_err <= 1'b1;
Prevention

Assert that no state is left on the cycle after entry. Two constants cost nothing and the flag makes the failure visible from the field.

3

The team bought memory and the stall did not move

MERGED-STALL-COUNTER
Symptom

A workload shows heavy stalling. Memory is doubled. Throughput improves by a few percent, and the stall counter reads almost exactly what it did before.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  20 cycles : capacity stalls=5 bandwidth stalls=5 issued=10
  merged variant : one bucket=10, other=0 (diagnosis unavailable)
Evidence

Split stall cycles by cause. If capacity stalls are near zero, the shortage was never capacity — the path was saturated, and the remedy was concurrency.

Likely Causes

One aggregate stall counter; a bandwidth stall recorded while capacity was the real blocker; stalls counted with no request offered.

Debug Sequence

Add the two counters. Re-run. Compare their ratio before and after any purchase — a remedy that does not move its own counter did not address the cause.

Root Cause

The two causes were merged, so the diagnosis was unavailable and the purchase was made on intuition.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cap_stall = want_issue && no_capacity;
bw_stall  = want_issue && !no_capacity && no_credit;   // exclusion matters
Prevention

Never attribute a stall to a mechanism without a counter that names it. Ordering the causes matters as much as separating them.

4

A fleet at 70% utilisation that cannot schedule a job

STRANDED-CAPACITY
Symptom

Fleet dashboards show ample free memory. A specific job repeatedly fails to schedule. Capacity planning insists there is no shortage, and both parties have correct data.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  4 nodes : unmet demand=30 usable free=30 stranded=30
  pooled view : usable=60 stranded=0 hidden flag=1
Evidence

Report unmet demand and stranded surplus per node, not fleet totals. Their simultaneous presence is the whole finding: memory exists, and the demand cannot reach it.

Likely Causes

Fleet-average utilisation used as the capacity metric; free memory summed across sockets that cannot serve each other; heterogeneous demand across identical nodes.

Debug Sequence

For each node compute surplus and deficit separately. Sum them separately. If both are non-zero, the fleet is stranding capacity regardless of what the average says.

Root Cause

Capacity is welded to a socket, so surplus behind one boundary cannot meet demand behind another. An average erases the boundary that causes the problem.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
usable   = min(surplus_sum, deficit_sum);
stranded = (surplus_sum > deficit_sum) ? surplus_sum - deficit_sum : 0;
Prevention

Never report a fleet average without also reporting the spread. An average is only a capacity metric when demand is uniform, which it never is.

5

An allocation that never returns

RETRY-LIVELOCK
Symptom

A process hangs. No error is logged. No allocation failure is recorded. The machine is at its capacity ceiling, and the only symptom is something that will not make progress.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  no space, retry limit 4 : retries=4 failures=1 | retry-forever failures=0 retries=18
  and was caught livelocking                               : ok
Evidence

Compare retry counts against the declared limit. Retries climbing without a failure ever being recorded is a livelock, not slowness.

Likely Causes

An unbounded retry loop; a failure path that exists but is unreachable; a request that is never released after being satisfied.

Debug Sequence

Instrument retries and failures separately. If failures stay at zero while retries grow without bound, the failure path is not reachable.

Root Cause

The ceiling was real, and the design responded by retrying forever — converting a diagnosable capacity shortage into an undiagnosable hang attributed to whatever was waiting.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
alloc_failed = active_q && !space_available && (retry_q >= RETRY_LIMIT);
if (retry_q > RETRY_LIMIT + 8'd8) livelock_err <= 1'b1;
Prevention

Every retry loop needs a bound and a counter. An unbounded retry is a hang with better manners.

6

Capacity overcommitted in a planning model that hardware refuses

ELASTIC-CAPACITY
Symptom

A deployment plan allocates more resident memory than the machines physically contain. It passes every planning check. It fails on the first node it touches, and the failure looks like a scheduling bug.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  elastic variant : committed=1224 overcommit flag=1
  the elastic variant committed beyond physical memory     : ok
Evidence

Compare committed capacity against the physical product of channels, slots and density. Any excess is a plan that hardware cannot honour.

Likely Causes

A capacity model with no ceiling; density assumed at a value not actually populated; a checker guarded by the very condition that would trip it.

Debug Sequence

Derive installed capacity from the populated configuration rather than from a constant. Then assert committed never exceeds it, on every configuration including the unlimited one.

Root Cause

The model treated capacity as elastic. Hardware does not, and the request fails rather than costing more.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Unguarded on purpose: guarding with !ELASTIC would switch the checker off
// on the only configuration that can ever trip it.
if (committed_q > CEILING_GB) overcommit_err <= 1'b1;
Prevention

For every checker ask which configuration makes it fire. If the answer is "none we run", it is decoration.

7

A working-set number that only ever grows

WINDOW-NEVER-CLEARS
Symptom

A working-set estimate rises steadily through a run and never falls, eventually reporting the entire address space the process has ever touched. Provisioning from it is absurd, so the metric is abandoned.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  after a window roll, same 4 pages re-touched : distinct=4
  the window rolled and the seen-set cleared               : ok
Evidence

Cross a window boundary and re-touch pages already seen. They must count as first touches again. If the number does not reset, the estimator is reporting a lifetime footprint.

Likely Causes

The seen-set not cleared on window roll; the window counter never reaching its limit; a window chosen longer than the run.

Debug Sequence

Force a roll and re-touch a known page set. Compare the reported distinct count before and after.

Root Cause

Working set is distinct pages in a window. Without the reset, the window is infinite and the metric measures something else entirely.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (window_roll) begin
  seen_q <= '0;      // both must clear
  cnt_q  <= '0;
end
Prevention

Test the roll explicitly. A short run never reaches the boundary, so the defect ships.

8

Refusal counts that justified a purchase nobody needed

COUNTER-CONSERVATION
Symptom

A capacity review is presented with a refusal rate far above what the workload's behaviour suggests. Memory is purchased. The refusal rate barely moves.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  requests=30 granted=24 refused=6
  every request resolved as exactly one outcome            : ok
  abuse: grants with no requests : flag=1
Evidence

Check that granted plus refused never exceeds requested. If it does, retries are being counted as fresh demand and the shortage is overstated.

Likely Causes

Retries counted as new requests; offered requests counted as grants; outcomes recorded with no request behind them.

Debug Sequence

Add the conservation check and re-run the same workload. The corrected refusal rate is the one the budget should see.

Root Cause

The evidence had no conservation law, so a derived metric drifted in the direction that flattered the argument.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if ((granted + refused) > requested) conservation_err <= 1'b1;
Prevention

Give every derived metric a conservation law. Capacity budgets are approved on these numbers.

20. Design Review

  1. What are the three multiplicands of this platform's capacity, and which can still change?
  2. Is the working set measured as distinct pages in a window, or inferred from access rate?
  3. What window length was used, and where does the distinct count plateau?
  4. Does pressure have a hysteresis band, and how wide?
  5. Can any pressure state be left on the cycle after it is entered?
  6. Are capacity stalls and bandwidth stalls counted separately, and in that order?
  7. What happens to an allocation that cannot be satisfied — and how many retries first?
  8. Does a satisfied allocation release its request?
  9. What is the stranded surplus across the fleet, not the average utilisation?
  10. Do granted and refused conserve against requested?
  11. Which of these counters exist in silicon rather than only in a model?

21. How This Appears in Real Engineering

Architect. Owns the ratio argument: capacity per core, and what the workload actually needs resident. Needs distinct-page data from software and the capacity product from the board team, and produces the refusal and stranding evidence that justifies expansion.

RTL designer. Owns the counters that make the argument checkable — bounded, conserved, and cheap. The pressure FSM and the stall split are a few dozen flops that decide a purchasing conversation.

DV engineer. Owns the boundary cases: exactly at the ceiling, exactly at a threshold, window rolls, and the no-request case. Every one of those is where this chapter's mutations survived until directed stimulus reached them.

Firmware engineer. Owns what the platform reports as installed and available, and must derive it from the populated configuration rather than a constant — the elastic-capacity defect starts here.

OS/runtime engineer. Owns the allocation failure path and the reclaim policy that pressure state drives. Needs the band, not the line, or the reclaim thread burns CPU without reclaiming.

Performance engineer. Owns the distinction between the two stalls and refuses to let one aggregate number stand in for the diagnosis.

Silicon validation. Owns whether the counters are trustworthy under load — conservation laws hold, peaks record the creating edge, and no counter has quietly stopped meaning its name.

What each needs from the others: the architect needs the software team's working set, the RTL team's counters and the firmware team's honest capacity report. None of the three is useful alone, and the capacity argument fails whenever one is missing.

22. Common Misconceptions

BeliefCorrection
Memory expansion means "more RAM"It means unwelding capacity from the socket
Capacity can be increased by spending moreChannels and slots are fixed once the board exists
Access rate indicates how much memory is neededResidency does; they differ by the reuse factor
A fleet at 70% utilisation has spare memoryIt may be stranding half its free memory
One threshold is enough for pressureIt makes every reclaim decision fire on noise
"The workload is stalled" is a diagnosisCapacity and bandwidth stalls have opposite fixes
An unbounded retry is safer than failingIt converts a diagnosable shortage into a hang
A displayed counter is a verified counterPrinting a value is not checking it

23. Interview Reasoning

24. Exercises

  1. Calculation. A platform offers 12 channels, 1 slot per channel and 96 GB modules. Compute installed capacity. A workload needs 1.5 TB resident and 40 cores; the socket provides 64 cores. Compute how many sockets a capacity-driven purchase requires, how many cores are then idle, and what the idle fraction would be if module density doubled.

  2. Analysis. A fleet reports 68% average memory utilisation and a 12% allocation-failure rate. Explain how both can be true simultaneously, name the two counters that resolve it, and state what values you would expect from each if the cause is stranding rather than genuine shortage.

  3. RTL task. Extend working_set_probe to maintain two windows of different lengths simultaneously and report both distinct counts. State the storage cost, and explain what the divergence between the two numbers tells you about the workload that either number alone does not.

  4. Assertion task. Write the property that proves a pressure state is never left on the cycle after it is entered. Then explain why this property, not a threshold check, is the one that detects a missing hysteresis band — and construct a design that satisfies a threshold check while thrashing.

  5. Testbench design. Design the stimulus that distinguishes a <= ceiling comparator from a < one. Explain why a sequence of equal-sized requests will usually fail to distinguish them, and state the general rule this implies for testing any bound.

  6. Debug task. A machine shows high stall time, and doubling its memory changes nothing. Give your investigation order across this chapter's counters, the single ratio that would have predicted the outcome before the purchase, and the remedy the evidence actually supports.

  7. Design review. A colleague proposes reporting a single "memory pressure" percentage to the OS instead of a state with hysteresis, arguing it is simpler and gives software more information. Give the strongest version of that argument, then state what breaks, which measured result demonstrates it, and what you would propose instead.

  8. Coverage design. Define the coverage cross for this chapter's capacity model across occupancy, request outcome and pressure state. Identify which point is reachable only by directed stimulus, and explain what defect class lives there.

25. Summary

Capacity is welded to the socket.

  • Installed capacity is a product — channels × slots × density — fixed when the board was designed. Only density has headroom, and it moves in steps.
  • The ratio is the real problem: memory and compute are bought together, and no workload respects the ratio the socket offers.
  • Working set is distinct pages in a window, not access rate. The measured run showed 20 accesses over 4 pages — a 5× over-provision if sized from the wrong number.
  • Pressure is a state with hysteresis, not a comparison. Identical occupancy produced 3 transitions with a band and 12 without.
  • Capacity stalls and bandwidth stalls have opposite remedies. One aggregate counter makes the diagnosis unavailable and sends the money to the wrong budget.
  • Free memory on the wrong socket is stranded. 30 short on one node, 60 spare across three, and half of all free memory unusable — invisible to any fleet average.
  • A ceiling is felt as a failure path. It must be bounded and visible; retrying forever converts a diagnosable shortage into a hang.
  • The evidence for a capacity review is refusals, pressure time and residency, each protected by a conservation law.
  • Verification: 27 of 27 mutations killed, 47 assertions. All ten first-run escapes were stimulus or assertion-precision problems, not missing checkers — six states never reached, two values printed but never asserted, two checkers needing abuse instances.

Next: 11.2 — Capacity Scaling, which asks what actually changes when capacity arrives over a link rather than over channels — beginning with the address map, because memory the host cannot address is not capacity at all.

Continue learning

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.