Skip to content
VLSI Mentor

CXL · Module 12

Datacenter Architecture

A pool that spans a rack is the same pool with a geography. Physical placement, which hosts can reach which enclosures, what one device failure actually costs, and why a pool that can survive a loss is a pool forbidden to use all its capacity.

12.1 through 12.3 treated the pool as one flat set of units in one place.

Every invariant in those chapters still holds here. What changes is that the pool now has a geography, and geography decides what a failure costs.

1. The Engineering Problem — The Pool Is Not In One Place

At chassis scale, where an allocation sits is an efficiency question. At rack scale it stops being one.

A device failure has a radius, and placement sets it. Four allocations on one device and four spread across four devices consume identical capacity, and one device failure takes four of them or one. Nothing in the capacity accounting distinguishes the two arrangements.

Capacity and reachability stop being the same statement. A host that cannot reach an enclosure does not have a small amount of capacity there; it has none. Two hosts can look at the same pool, both correctly, and report different amounts of free memory.

Distance costs. Spreading allocations to limit the blast radius moves them away from the hosts using them, and the access cost rises. The safest placement and the fastest placement are not the same placement.

And a pool that can survive a failure is a pool forbidden to use all its capacity. Headroom held back to absorb a device loss is capacity that exists, is healthy, and may not be granted. That is a deliberate cost, and a pool that reports it as available is promising something it has already decided not to deliver.

2. The One-Sentence Model

Scale changes the blast radius, not the invariants. Conservation, unique ownership, generation safety and isolation are exactly as they were — what rack scale adds is that every allocation now has a location, and location decides who is affected when something breaks.

Call it the same rules, with a map. A pool without recorded placement can still be correct and cannot answer the only question anyone asks after a failure: who is affected.

3. What This Chapter Owns

GroundOwner
What makes a pool; free versus allocatable12.1
Where an allocation goes within the pool, and its lifecycle12.2
Who owns it, identity, and host failure12.3
Physical placement, reach, and what a device failure coststhis chapter
The economics of the whole arrangement12.5

Deferred:

Deferred groundOwner
Fabric topology, managers, discoveryModule 15
Switch internals and routingModule 16
Latency anatomy and performance modellingModule 18
Disaggregation as a datacentre philosophyModule 23

And nothing here is a fabric. Reachability is modelled as a matrix that says which hosts can reach which enclosures. How that connectivity is built, discovered, managed or made redundant is Modules 15 and 16, and this chapter deliberately treats it as a given.

4. Teaching-model boundary

The hierarchy below — 16 units on 4 devices in 2 enclosures — is a teaching scale, chosen so every number in the chapter is checkable by hand. Real pools are larger by orders of magnitude and the arithmetic does not change.

The reachability matrix is likewise a teaching structure. It expresses the fact that connectivity is not uniform without claiming anything about how CXL establishes it. Nothing below should be read as a CXL topology or a fabric-manager mechanism.

5. RTL 1 — Placement Is State

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//   16 units -> 4 devices (4 units each) -> 2 enclosures (2 devices each)
module placement_map #(parameter int UNITS = 16) (
  input  logic [3:0] q_unit,
  output logic       q_valid, output logic [1:0] q_owner, q_dev, output logic q_enc,
  output logic [2:0] d_used0, d_used1, d_used2, d_used3,
  output logic [3:0] e_used0, e_used1,
  output logic [2:0] dev_spread,          // how many devices this pool spans
  output logic       place_err
);
  // Placement is structural here: the unit index names its device and its
  // enclosure. In a real pool this is a stored mapping; what matters is that
  // it EXISTS, because without it a failure has no bounded answer.
  assign q_dev = q_unit[3:2];
  assign q_enc = q_unit[3];

dev_spread counts how many devices the pool actually occupies, and it is the cheapest exposure metric there is. Measured on four allocations placed two ways:

LayoutDevicesOccupancy
packed14, 0, 0, 0
spread41, 1, 1, 1

Same capacity, same hosts, same everything the accounting can see.

Pool hierarchy of units, devices and enclosurespool16 unitsenclosure 08 unitsenclosure 18 unitsdevice 0units 0 to 3device 1units 4 to 7device 2units 8 to 11device 3units 12 to 1512
Figure 2 — The teaching hierarchy. Sixteen units on four devices in two enclosures. Capacity accounting sees a flat total; a failure sees this tree, and only the tree can answer who is affected.

Placing a unit that is already placed is refused and reported — measured place_err=1 with the original owner unchanged, the same rule as 12.1's ownership table applied to physical units.

6. RTL 2 — Reachable Is Not The Same As Free

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The capacity a host can see is not the pool total: it is the sum over the
  // enclosures it can reach. Two hosts looking at the same pool can correctly
  // report different amounts of free capacity.
  logic [4:0] vis;
  always_comb begin
    vis = 5'd0;
    if (m_q[req_host][0]) vis = vis + {1'b0, enc_free0};
    if (m_q[req_host][1]) vis = vis + {1'b0, enc_free1};
  end
 
  // Reachability is tested FIRST. A host that cannot reach an enclosure is not
  // short of capacity there -- it has none, and reporting a capacity failure
  // sends an operator to add memory that host still could not use.
  assign reach_short = req_en && !reachable;
  assign cap_short   = req_en && reachable &&
                       (req_size > (req_enc ? enc_free1 : enc_free0));

Measured on one pool with 16 free units:

HostEnclosures reachableVisible capacity
host 0both16
host 1enclosure 0 only8

Both numbers are correct. A pool that publishes a single free-capacity figure is answering a question nobody asked, because the number a host needs is the one it can actually consume.

The two refusals are separated for the same reason the two in 12.1 were. A host refused for reach has ample capacity in front of it that it cannot use; adding memory to that enclosure changes nothing. Measured: fail_reach=1 with fail_cap=0 for a host asking into an enclosure it cannot reach, and the reverse for a reachable enclosure that is short.

The second width defect lived here. visible_cap was four bits summing two four-bit enclosure totals, so 8 plus 8 read as 0 — a host that could reach everything appeared to be able to reach nothing.

7. Waveform — One Device Failure, And Everything It Restates

Transcribed from the printed cycle trace.

A device drains, then fails

8 cycles
A device drains, then failstakes nothing new, still servestakes nothing new, stillservescapacity restatedcapacity restatedclkdrainingfailedusable3232323224242424util_pct6262626283838383headroom00000044grantable121212124400grantf_drainf_failt0t1t2t3t4t5t6t7
Figure 1 — Transcribed from the printed trace. The allocations never change; at cycle 4 the denominator does. Utilisation moves 62 to 83 percent because the pool got smaller, not because anything was allocated.

Three things happen that are worth separating.

Cycles 2 and 3: draining, not failed. The device still serves what it holds and accepts nothing new. f_drain rises and f_fail stays low, and usable capacity is unchanged because nothing has been lost yet. A design with only a failed state has no way to express a planned evacuation.

Cycle 4: the failure restates the pool. Usable falls from 32 to 24 and utilisation rises from 62% to 83% — with the same 20 units allocated. Nothing was granted; the denominator shrank.

Cycle 6: headroom removes what is left. Holding 4 units back for the next failure takes grantable from 4 to 0 while usable stays at 24. The capacity is healthy, present, and not available.

8. RTL 3 — What A Failure Actually Costs

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  always_comb begin
    ha = 4'd0; hh = 4'd0;
    for (k = 5'd0; k < UNITS[4:0]; k = k + 5'd1)
      if (v[k] && (k >= {1'b0, fail_dev, 2'b00}) && (k < ({1'b0, fail_dev, 2'b00} + 5'd4))) begin
        ha = ha + 4'd1;
        hh[o[k]] = 1'b1;
      end
  end

Two numbers, and they are not related by anything except placement.

ArrangementAllocs lostHosts hit
packed on one device, one host41
spread over four, one host11
packed on one device, four hosts44

Spreading reduced allocations lost by a factor of four and did nothing for hosts affected, because in that case there was only ever one host. The third row is the one that matters at rack scale: a single device holding one allocation each for four different hosts puts every one of them in the blast radius of one failure.

Three placements of four allocations and the cost of losing device 0packedone hostdev 04 allocsdev 1emptydev 2emptylost4 allocs, 1 hostspreadone hostdev 01 allocdev 11 allocdev 21 alloclost1 alloc, 1 hostpackedfour hostsdev 04 hostsdev 1emptydev 2emptylost4 allocs, 4 hosts12
Figure 3 — The same four allocations, three arrangements, one device failure. Capacity accounting cannot distinguish them; the blast radius differs by a factor of four in allocations and a factor of four in hosts.

An empty device has no blast radius at all, measured at zero on both counters — which sounds obvious and is the case a for loop with a wrong bound gets wrong.

The total-lost counter is accumulated combinationally and added once, for the reason 12.3 established the hard way: a nonblocking increment inside a loop keeps only the last iteration and under-reports in proportion to the severity of the failure.

9. RTL 4 — Capacity After A Loss

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  assign unavailable = ({5'b0, failed_map[0]} + {5'b0, failed_map[1]}
                      + {5'b0, failed_map[2]} + {5'b0, failed_map[3]}) * DEV_CAP[5:0];
  assign usable = physical - unavailable;
 
  // Utilisation is measured against USABLE capacity, not physical. After a
  // failure the denominator shrinks, and reporting against physical flatters
  // the system precisely when it is least healthy.
  //
  // The intermediate is 16 bits on purpose. Verilog sizes an expression from
  // its operands, so `allocated * 7'd100` is evaluated in seven bits and 2000
  // wraps to 80 -- a percentage that is wrong by a factor of thirty and looks
  // entirely plausible.
  logic [15:0] util_num, util_q;
  always_comb begin
    util_num = {10'b0, allocated} * 16'd100;
    util_q   = (usable != 6'd0) ? (util_num / {10'b0, usable}) : 16'd0;
  end

Measured, with 20 units allocated throughout:

StatePhysUsableUtil
healthy323262%
one lost322483%

Against physical capacity it would still read 62% after the failure — the flattering number, reported at the moment the pool is least able to absorb anything.

grantable subtracts both what is already allocated and the headroom held back on purpose: measured 4 units grantable with no headroom, and 0 once 4 are reserved, with usable unchanged at 24 in both cases.

And overcommit_err fires when allocated capacity exceeds what survives — measured at 20 allocated on 16 usable after a second device is lost. That is not a policy problem; it means allocations exist on capacity that is gone, and something has to be migrated or killed.

10. RTL 5 — Admission While Degraded

A degraded pool is not a smaller healthy pool. Parts of it are unusable and parts are being emptied on purpose.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Order matters and encodes an operational meaning. A failed device cannot
  // serve anything; a draining device still serves what it holds but takes
  // nothing new; only then is capacity the question.
  assign fail_failed   = req_en &&  dev_failed;
  assign fail_draining = req_en && !dev_failed &&  dev_draining;
  assign fail_cap      = req_en && !dev_failed && !dev_draining && (req_size > dev_free);
  assign grant         = req_en && !dev_failed && !dev_draining && (req_size <= dev_free);

Four outcomes, four counters, measured one each over the run:

TargetOutcome
healthy device with roomgranted
failed devicefail_failed
draining devicefail_draining
healthy device that is shortfail_cap

The draining state is what makes planned maintenance possible. A device being evacuated must keep serving its existing allocations while accepting no new ones; collapsing it into either failed or healthy removes the only state in which an operator can empty a device without an outage.

An exact fit is granted, not refused — measured, a request for exactly the free capacity succeeds. That case had to be driven deliberately: without it the boundary can be off by one in either direction and every other test still passes.

11. RTL 6 — Packing And Spreading, Chosen Explicitly

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // PACK: the lowest-indexed device that fits. Fills devices in order and
    // leaves whole devices empty, which is what you want if you intend to
    // power one down or hold it as failure headroom.
    // SPREAD: the least-loaded device that fits. Limits how much any single
    // failure can take, and leaves no device empty.

On free capacity of 4, 6, 8 and 2 units with a two-unit request:

PolicyDevice chosenFree there
pack04
spread28

Different devices, same pool state, same request. Neither is wrong.

Packing is right when you intend to act on empty devices — power one down, hold it as failure headroom, or reserve it for a large contiguous request. Spreading is right when the cost of a single failure dominates. The measurement in section 8 is the argument for spreading; the measurement in section 13 is the argument against.

Both policies skip failed devices, and neither selects anything for a zero-size request. That last case is not pedantry: a policy that returns a device for a zero-size request will hand out a placement for an allocation that does not exist.

12. RTL 7 — What Spreading Costs

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module locality_cost #(parameter int LOCAL = 4, parameter int REMOTE = 10) (
  input  logic       acc_en, input logic host_enc, target_enc,
  output logic [4:0] cost,
  output logic [15:0] n_local, n_remote, total_cost, avg_cost_x10
);
  assign is_remote = (host_enc != target_enc);
  assign cost = is_remote ? REMOTE[4:0] : LOCAL[4:0];

With illustrative costs of 4 for a local access and 10 for a remote one — these are teaching values and describe no measured system:

Access mixAverage cost
all local4.0
half remote7.0
three quarters remote8.5

This is the price of the spreading policy in section 11, and it is why the argument cannot be settled by blast radius alone. A placement that limits a device failure to one allocation may put most accesses in the far enclosure, and the workload pays for that on every access rather than once per failure.

The average is scaled by ten before the division. A mean reported as a truncated integer would show 4, 7 and 8 for these three cases — hiding exactly the movement the metric exists to reveal.

13. RTL 8 and 9 — The Capacity You Are Not Allowed To Use

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // survive_n is three bits; part-selecting [5:0] from it reads past the end
  // of the vector and yields x. Zero-extend instead.
  assign required = {3'b0, survive_n} * DEV_CAP[5:0];
 
  // The ceiling utilisation a pool may run at and still absorb the stated
  // number of failures. Running above it is a decision, not an accident, and
  // it should be visible as one.

Measured on a 32-unit pool of four devices:

Losses to absorbReserve requiredUtilisation ceiling
00100%
1875%
21650%

A four-device pool that must survive one device loss can never exceed 75% utilisation. That is the entire economic cost of resilience, in one number, and it is a design decision rather than an accident.

It compounds after a failure. With one device already lost, still holding 8 units back for the next one, usable is 24 and the ceiling is 66% — a tighter constraint on a smaller pool.

Running above the ceiling is detected: 28 units allocated of 32 while claiming to survive one loss raises cannot_absorb_err, because the pool is asserting a resilience property it can no longer deliver. The boundary is exact and needed its own test — 24 allocated leaves precisely the 8-unit reserve, spare is zero, and the loss can still be absorbed.

The final model reconciles two independent views of where capacity sits — counted from the placement map and from the accounting — per enclosure and in total. Measured: a disagreement in either enclosure is detected and named, and a total that does not match the sum of the enclosures is detected separately.

14. Quantitative Reasoning

Five numbers describe a rack-scale pool, and four of them do not exist at chassis scale.

Blast radius, in two dimensions.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
allocations_lost = allocations placed on the failed device
hosts_affected   = distinct owners among them

Measured: 4 and 1 for a packed single-host arrangement, 1 and 1 for a spread one, and 4 and 4 for four hosts sharing one device. The two numbers move independently, and only the second predicts how many people notice.

Utilisation, against the right denominator.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
U = allocated / usable          where usable = physical - unavailable

Measured 62% healthy and 83% after one device loss, with the allocations unchanged. Against physical capacity it reads 62% in both cases — a number that is technically true and useless, because it is highest-confidence exactly when the pool is least able to absorb anything.

Utilisation ceiling under a resilience promise.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
max_U = (usable - survive_n x device_capacity) / usable

Measured 100%, 75% and 50% for absorbing zero, one and two device losses on a four-device pool, and 66% for absorbing one more after one has already gone. This is the price of resilience stated as a percentage of capacity that may never be granted.

Visible capacity, per host.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
visible(h) = sum of free capacity over enclosures h can reach

Measured 16 for one host and 8 for another on the same pool at the same instant. Both correct. A single pool-wide free figure answers neither host's question.

Average access cost, as a function of placement.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
avg = (n_local x local_cost + n_remote x remote_cost) / (n_local + n_remote)

Measured 4.0, 7.0 and 8.5 for all-local, half-remote and three-quarters-remote mixes at illustrative costs of 4 and 10.

The two policies pull in opposite directions and the numbers say so. Spreading takes the blast radius from 4 allocations to 1; it also moves accesses into the far enclosure and takes the average cost from 4.0 toward 8.5. There is no placement that optimises both, and the honest framing is that a rack-scale pool chooses which failure it prefers: a rare expensive one, or a continuous small one paid on every access.

15. Assertions

Written as SystemVerilog for the reader, executed procedurally — see section 17.

Usable capacity never exceeds physical.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_usable_bounded;
  @(posedge clk) disable iff (!rst_n)  usable <= physical;
endproperty

Per-device totals sum to the pool occupancy.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_placement_conserved;
  @(posedge clk) disable iff (!rst_n)
    (d_used0 + d_used1 + d_used2 + d_used3) == (e_used0 + e_used1);
endproperty

The blast radius is confined to the failed device.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_blast_confined;
  @(posedge clk) disable iff (!rst_n)
    fail_en |-> (hit_allocs <= UNITS_PER_DEV);
endproperty

A host never sees capacity it cannot reach.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_visible_reachable;
  @(posedge clk) disable iff (!rst_n)
    !m_q[req_host][1] |-> (visible_cap <= enc_free0);
endproperty

A reach failure implies the capacity was there.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_reach_not_capacity;
  @(posedge clk) disable iff (!rst_n)  fail_reach |-> !fail_cap;
endproperty

Exactly one admission outcome per request.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_one_outcome;
  @(posedge clk) disable iff (!rst_n)
    req_en |-> ($countones({grant, fail_failed, fail_draining, fail_cap}) == 1);
endproperty

A failed device is never selected by any policy.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_never_place_on_failed;
  @(posedge clk) disable iff (!rst_n)  found |-> !failed_map[chosen];
endproperty

Grantable capacity excludes the reserve.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_headroom_withheld;
  @(posedge clk) disable iff (!rst_n)
    grantable <= (usable - allocated - headroom);
endproperty

Allocations never outlive the capacity they sit on.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
property p_no_overcommit;
  @(posedge clk) disable iff (!rst_n)  allocated <= usable;
endproperty

105 assertion sites — 98 across the nine models and 7 in the waveform trace. All pass.

16. Mutation Testing

55 mutations injected, 55 killed, 0 surviving.

FamilyInjected
placement, spread and enclosure totals8
reachability and visible capacity5
blast radius5
capacity after loss6
degraded admission5
placement policy7
locality cost5
headroom and utilisation ceiling7
rack reconciliation5
integration, run under the waveform trace2

Representative kills:

MutationCaught by
device 2 counted from the wrong unitsuneven per-device occupancy
enclosure totals mix the wrong devicesthe same uneven arrangement
unreachable capacity counted as visiblea host reaching one enclosure
unreachable reported as a capacity failureseparate reach and capacity counters
every allocation counted, not just the failed devicean empty device with no radius
total lost advances once per failurea device holding four allocations
utilisation measured against physicalthe device-loss restatement
a draining device accepts new allocationsthe drain-then-fail sequence
failed and draining share one counterfour distinct outcomes
spread ignores load, takes the last eligiblefree capacity 4, 6, 8, 2
required headroom always zerothe 75% ceiling
ceiling utilisation ignores the reserveabsorbing one loss versus none

Nine mutations did not die on the first run.

SurvivorClassification
device 2 counted from the wrong unitsstimulus gap — every device held exactly one unit, so every wrong answer was also the right number
enclosure totals mix the wrong devicessame gap
double placement acceptedstimulus gap — no test placed twice on one unit
device index from the wrong bitsoutput never observed
enclosure from the wrong bitoutput never observed
capacity boundary off by onestimulus gap — no exact-fit request
spread takes the most loaded deviceequivalent — the mutation removed one guard and the following comparison restored the correct answer
headroom ok exactly at the limitstimulus gap — spare was never exactly zero
enclosure 0 drift ignoredstimulus gap — only enclosure 1 was ever drifted

The first two are the instructive pair. A test in which every device holds the same amount cannot detect a device counted from the wrong place, because the wrong answer and the right answer are the same number. Making the per-device occupancy uneven — 0, 2, 3 and 1 — killed both immediately.

The seventh was genuinely equivalent and was replaced rather than papered over. Removing the load comparison from one branch changed nothing, because the next comparison corrected it; the useful mutation removes the comparison from all four and makes the policy pick the last eligible device instead of the emptiest.

The taxonomy holds for an eighth consecutive batch.

17. Verification Strategy

Tool reality. Icarus Verilog 13.0 — no concurrent SVA, so every property has an executable procedural counterpart, and every run is bounded by a hard timeout.

Read the warnings. This chapter is the strongest argument in the batch for that. Four defects in these models were width or select errors, and two of them were announced by the compiler before any test ran:

DefectSymptomWarned?
3'd8 truncates to 0devices counted from wrong unitsyes
four-bit sum of two four-bit values8 plus 8 read as 0no
percentage evaluated in seven bits62% reported as 2%no
part-select past a vector's endevery headroom output xno

Verilog sizes an expression from its operands, not from its destination. Three of these four follow directly from that rule, and none of them produces an error at runtime — they produce plausible numbers. The hardened checker introduced in 12.2, which treats x as a failure rather than a silent pass, is what caught the fourth.

Independent oracles.

ModelDesign, then oracle
placementbit vectors per device → a per-unit array in the testbench
blast radiusa masked scan → expected allocations and hosts
capacityderived from a failure bitmap → capacities per configuration
policytwo selection chains → expected device per free vector
headroommultiply and subtract → reserve and ceiling per case

Coverage. The points that matter are placement shape, failure count, reach configuration, and the position of allocated capacity relative to the ceiling. The crosses worth driving are placement shape crossed with failure — the only way to reach the packed-versus-spread divergence — and reach configuration crossed with request target, which is what separates the two refusal counters. A cross of unit index against host is noise.

18. Synthesis and Implementation Reality

Placement is a table, and at rack scale it is a large one. The structural mapping here — the unit index names its device — is a teaching simplification. A real pool stores it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
placement bits = UNITS x (log2(DEVICES) + log2(ENCLOSURES))

That grows linearly with capacity and is read on every allocation decision and every failure analysis.

The per-device tally is the timing problem, and it is the same shape as the derived counters in 12.1: a wide combinational reduction over the whole map, recomputed continuously. The resolutions are the same — pipeline it and accept a reporting latency, or maintain incremented counters and reconcile them periodically, which is what the reconciliation model exists for.

The reachability matrix is small and its cost is in where it sits. Hosts times enclosures is a handful of bits, but the check is on the allocation path and must complete before a placement decision, so it is a lookup rather than a scan.

The blast-radius scan is not on any critical path and can be as slow as it likes. It runs on failure, which is rare, and its answer is consumed by software. This is worth stating because it is the one structure here that should not be optimised — correctness and completeness matter and latency does not.

Headroom arithmetic is trivial — a multiply by a small constant and two subtractions — and the only thing to get right is the width, which section 17 covers at length.

19. Silicon Observability

CounterClass
dev_spreadpolicy input
per-device and per-enclosure occupancypolicy input
hit_allocs, n_hit_hoststelemetry, per failure
n_total_losttelemetry
usable, unavailablepolicy input
util_pct (against usable)policy input
grantablepolicy input
overcommit_errhard alarm
n_reach_fail vs n_cap_failpolicy input
n_failed vs n_draining vs n_cappolicy input
avg_cost_x10telemetry
max_util_pct, sparepolicy input
cannot_absorb_errhard alarm
enc_drift_err, total_drift_errhard alarm
place_errhard alarm
ObservationReading
util_pct jumps with no allocation activitythe denominator shrank — something failed
n_reach_fail high, n_cap_fail zeroconnectivity, not capacity
n_draining risinga planned evacuation is in progress and is being respected
n_failed rising after a drain completedrequests are still targeting a device that is gone
avg_cost_x10 climbing with dev_spreadthe spreading policy is being paid for on every access
spare at zero with cannot_absorb_err clearexactly at the resilience limit, which is legal and fragile
cannot_absorb_err setthe pool asserts resilience it cannot deliver
enc_drift_err settwo views of placement disagree; a failure analysis would be wrong

The most valuable pair is hit_allocs against n_hit_hosts. They are recorded per failure and they are the only record of what placement policy actually bought. A pool that spread its allocations and still lost four hosts to one device learns something no capacity metric can tell it.

20. Debug Lab

1

Utilisation jumps and nothing was allocated

DENOMINATOR
Symptom

Utilisation moves from 62% to 83% in one sample. No allocation was granted. No host changed its footprint. Capacity alarms are quiet.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  healthy      : physical=32 usable=32 utilisation=62%
  one device lost : unavailable=8 usable=24 utilisation=83%
Evidence

Read usable alongside util_pct. If allocated capacity is unchanged and utilisation rose, the denominator shrank — something became unavailable.

Likely Causes

A device failure; a device taken out for maintenance; a reachability change that removed an enclosure from this host's view.

Debug Sequence

Compare physical against usable. If they now differ, capacity has been lost rather than consumed. Then check whether the pool reports utilisation against usable or physical: against physical it would still read 62% and the event would be invisible.

Root Cause

The pool got smaller. This is the correct and useful reading — and it only exists if the denominator excludes what has failed.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign usable = physical - unavailable;
util_q = (usable != 6'd0) ? (util_num / {10'b0, usable}) : 16'd0;
Prevention

Always measure utilisation against usable capacity. Reporting against physical flatters the system precisely when it is least able to absorb anything.

2

One device failure took down four hosts

BLAST-RADIUS
Symptom

A single device fails. Four separate hosts report memory errors. The allocations were spread across the pool by policy and the impact was predicted to be one host.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  4 hosts, 1 device                     | device 0 lost -> 4 allocations, 4 hosts
Evidence

Read hit_allocs and n_hit_hosts for the failure. They are different numbers and only the second predicts how many people notice.

Likely Causes

A spreading policy that balances load per device without regard to owner; several hosts each holding one allocation on the same device; a prediction based on allocations rather than owners.

Debug Sequence

Compare the two counters across recent failures. Spreading one host's allocations reduces allocations lost — measured, from 4 to 1 — and does nothing at all for hosts affected when the device carries one allocation each for four hosts.

Root Cause

Spreading reduces how much any one host loses. It does not reduce how many hosts a device touches, and those are separate objectives.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (v[k] && (k >= {1'b0, fail_dev, 2'b00}) && (k < ({1'b0, fail_dev, 2'b00} + 5'd4))) begin
  ha = ha + 4'd1;
  hh[o[k]] = 1'b1;     // owners, not just allocations
end
Prevention

Record both numbers per failure. A policy tuned on allocations lost will happily concentrate many hosts onto one device.

3

A host cannot allocate and the pool is half empty

REACHABILITY
Symptom

A host's requests are refused. The pool reports 16 free units. Memory is added to the pool and the refusals continue unchanged.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  visible capacity: host 0 = 16 units, host 1 = 8 units (same pool)
Evidence

Read visible capacity per host rather than pool-wide. Two hosts can correctly report different free capacity on the same pool at the same instant.

Likely Causes

The host cannot reach the enclosure holding the free capacity; a reachability entry never installed; the added memory placed in an enclosure the complaining host cannot see.

Debug Sequence

Check n_reach_fail against n_cap_fail. A reach failure means the capacity is there and unusable by this host, and no amount of additional memory in that enclosure will change it. Then confirm which enclosures the host can actually reach.

Root Cause

Capacity and reachability are independent properties, and a pool-wide free figure conflates them.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign reach_short = req_en && !reachable;
assign cap_short   = req_en && reachable && (req_size > (req_enc ? enc_free1 : enc_free0));
Prevention

Publish visible capacity per host. A single pool-wide number answers a question no host asked.

4

Per-device totals are wrong and every test passed

WIDTH-TRUNCATION
Symptom

Device occupancy counters report plausible but incorrect values. Every allocation is correctly placed. The pool total is right. Only the per-device breakdown is wrong, and only sometimes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  uneven placement: devices hold 2, 3, 1 -> enclosures 2 and 4
Evidence

Read the compile log. A constant that does not fit its declared width is truncated silently at runtime and announced at compile time.

Likely Causes

A loop index narrower than the offsets added to it; an expression sized from its operands rather than its destination; a part-select reading past the end of a vector.

Debug Sequence

Make the per-device occupancy uneven and re-run. With every device holding the same amount, a device counted from the wrong units produces the same number as a device counted correctly, and no test can tell.

Root Cause

3'd8 is zero. Eight does not fit in three bits, so the index arithmetic addressed the wrong units, and the compiler said so before any test ran.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
logic [4:0] j;                                  // wide enough for the offsets
u2 = u2 + {2'b0, v_q[5'd8  + j]};
Prevention

Treat width warnings as failures, and never verify a per-partition counter with equal occupancy in every partition.

5

A device being evacuated causes an outage

DRAIN-STATE
Symptom

An operator takes a device out of service for planned maintenance. Hosts holding allocations on it immediately lose access, or the device keeps accepting new allocations and never empties.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  degraded admission : failed=1 draining=1 capacity=1
Evidence

Check whether the design has a state between healthy and failed. If it does not, an evacuation must be expressed as one or the other, and both are wrong.

Likely Causes

Draining collapsed into failed, so existing allocations stop working; or draining collapsed into healthy, so the device keeps taking new work and never empties.

Debug Sequence

Confirm that a draining device refuses new requests with its own counter while continuing to serve what it holds. In the measured run fail_draining rises and fail_failed stays low, and usable capacity is unchanged because nothing has been lost yet.

Root Cause

Planned evacuation is a distinct state. Without it, every maintenance action is either an outage or a no-op.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign fail_failed   = req_en &&  dev_failed;
assign fail_draining = req_en && !dev_failed &&  dev_draining;
Prevention

Give draining its own counter as well as its own state. An operator needs to see that the evacuation is being respected, not merely that requests are being refused.

6

The pool promised capacity it had already reserved

HEADROOM
Symptom

A scheduler is told the pool has free capacity, places work against it, and the allocation is refused. Free capacity has not changed between the query and the request.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  grantable    : 4 with no headroom, 0 holding 4 back
Evidence

Compare usable against grantable. Capacity withheld as failure headroom is healthy, present, and not available — and reporting usable as grantable promises it anyway.

Likely Causes

Grantable computed as usable minus allocated, with no headroom term; headroom applied at allocation time but not published; two different subsystems using different definitions of free.

Debug Sequence

Set a non-zero reserve and confirm grantable falls while usable does not — measured, 4 to 0 with usable unchanged at 24. If both move together, the reserve is not being withheld.

Root Cause

The pool published capacity it had decided not to give away. The reservation is deliberate; publishing it as available is not.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always_comb g = $signed({2'b0, usable}) - $signed({2'b0, allocated})
                                        - $signed({2'b0, headroom});
Prevention

Publish usable and grantable separately, and make sure every consumer knows which one it is reading.

7

A resilience promise the pool cannot keep

CANNOT-ABSORB
Symptom

The pool is documented as tolerating a single device failure. A device fails and allocations cannot be relocated — there is nowhere for them to go.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  survive 1 loss : required=8 spare=0 max utilisation=75%
  28 of 32 allocated while claiming to survive a loss : err=1
Evidence

Compare allocated capacity against usable minus the required reserve. If it exceeds that, the promise stopped being true at some point and nothing said so.

Likely Causes

Utilisation allowed past the ceiling; the reserve computed but never enforced at admission; the ceiling not recomputed after an earlier failure shrank the pool.

Debug Sequence

Compute the ceiling for the stated resilience level: on four devices, surviving one loss caps utilisation at 75%, and after a loss has already occurred the same promise caps it at 66% of what remains. Then check where actual utilisation sits.

Root Cause

Resilience is capacity you agree not to use, and the agreement has to be enforced at admission rather than documented.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign headroom_ok = (sp >= 0);
if (!headroom_ok) cannot_absorb_err <= 1'b1;
Prevention

Publish the ceiling next to actual utilisation and alarm on crossing it. A promise that is only in a document degrades silently as the pool fills.

8

A failure analysis named the wrong hosts

PLACEMENT-DRIFT
Symptom

A device fails. The blast-radius report names a set of hosts. A different set complains. The report is internally consistent and confidently wrong.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  reconciliation : enclosure drift=1 (enc 1)  total drift=1
Evidence

Reconcile the placement map against the accounting, per enclosure and in total. A failure analysis reads placement, so placement being stale makes the analysis wrong in a way nothing else detects.

Likely Causes

A migration that updated the accounting and not the placement map; a release that cleared one and not the other; two structures updated by different events.

Debug Sequence

Sample both views at the same instant, per enclosure first and then in total. A per-enclosure disagreement with a matching total means capacity moved between enclosures without being recorded; a total disagreement means capacity appeared or vanished.

Root Cause

Two independent views of placement drifted apart, and the failure analysis trusted the stale one.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign d0 = (place_e0 != acct_e0);
assign d1 = (place_e1 != acct_e1);
assign dt = (({2'b0, place_e0} + {2'b0, place_e1}) != total_alloc);
Prevention

Reconcile continuously and separate the per-enclosure check from the total. They fail for different reasons and a single comparison detects only one of them.

21. Design Review

Rack-scale pool with placement, reach, policy, degraded admission and failure analysishostsrequestsreachwhich enclosurespolicypack or spreaddegraded admitfailed, draining,shortplacement mapunit, device,enclosureheadroomreserve and ceilingblast radiusallocations and hostsreconcilertwo views must agreerequesteligibledeviceceilinggrantedon failure12
Figure 4 — The rack-scale pool assembled. Placement feeds both the admission path and the failure path; the reconciler compares the two views that a failure analysis depends on.

What a reviewer should attack first.

Whether placement is recorded at all. If the allocator does not store which device holds each unit, the blast radius of any failure is the whole pool by definition, and no analysis after the fact can narrow it.

The utilisation denominator. Ask what it is measured against. If the answer is physical capacity, the number is highest exactly when the pool is least healthy and the failure is invisible in the metric.

The draining state. Ask how a device is taken out for maintenance. If the only states are healthy and failed, every planned evacuation is either an outage or a no-op.

Grantable versus usable. Ask which one is published to schedulers. Publishing usable promises capacity the pool has already decided to withhold.

The resilience ceiling, enforced rather than documented. Ask what stops utilisation crossing it. If the answer is a runbook, it will be crossed.

Every width in the file. Four defects in these models were width or select errors, two of them announced by the compiler. This is the specific class to grep for.

What is deliberately not here. No fabric, no topology, no discovery, no manager — Modules 15 and 16. No latency modelling: the local and remote costs are illustrative weights that make a tradeoff visible, not a performance model, which is Module 18. No TCO, no fleet economics, no composability — 12.5 for the economics of pooling and Module 23 for the datacentre architecture built on it.

22. How This Appears in Real Engineering

In architecture review, the recurring argument is packing against spreading, and it is usually conducted without numbers. It has two: the blast radius and the average access cost, and they move in opposite directions. A review that produces a policy without both is choosing by preference.

In RTL design, this chapter's defect class is width. Verilog sizes an expression from its operands rather than its destination, so a percentage can be wrong by a factor of thirty and look plausible. Two of the four defects here were announced by the compiler and the warnings were initially skimmed.

In verification, the trap is symmetric stimulus. Every device holding the same amount cannot detect a device counted from the wrong place, because the wrong answer equals the right one. Uneven occupancy is not a nicety; it is what makes per-partition counters testable at all.

In bring-up, the surprise is that utilisation moves without any allocation activity. It is correct behaviour and it looks alarming, and an operator who has not seen the denominator explained will chase an allocation storm that never happened.

In operations, the question is why can this host not allocate when the pool is half empty, and at rack scale the answer is often reachability rather than capacity. That is a connectivity change, not a purchase, and only per-host visible capacity distinguishes them.

23. Common Misconceptions

"Rack scale needs new invariants." It needs the same ones. Conservation, unique ownership, generation safety and isolation are unchanged from 12.1 through 12.3; what is added is a location per allocation.

"Spreading reduces the blast radius." It reduces how much any one host loses — measured, 4 allocations to 1. It does nothing about how many hosts a device touches, and a device carrying one allocation each for four hosts affects all four.

"Free capacity is free capacity." Not per host. The same pool showed 16 units to one host and 8 to another, both correct, because reachability is not uniform.

"Utilisation is utilisation." Against physical capacity it reads 62% before and after a device loss. Against usable capacity it reads 62% then 83%. The second is the one that predicts a refusal.

"A failed device and a draining device are the same thing." One cannot serve anything; the other serves what it holds and takes nothing new. Without the second, planned maintenance is an outage.

"Headroom is wasted capacity." It is the price of the resilience the pool claims. A four-device pool surviving one loss is capped at 75% utilisation, and capacity above that line was never really available.

"The compiler warnings are noise." Two of the four defects in this chapter's models were printed by the compiler before any test ran, and both produced plausible numbers at runtime.

"Equal test coverage per device is thorough." Equal occupancy per device is the one arrangement in which a mis-indexed per-device counter cannot be detected.

24. Interview Reasoning

25. Exercises

  1. Calculation. A pool has 8 devices of 64 units each. Compute the utilisation ceiling for surviving one, two and three device losses. Then repeat for 16 devices of 32 units at the same total capacity, and state which arrangement pays less for the same resilience and why.

  2. Analysis. A device fails and the blast-radius report shows 6 allocations and 6 hosts. Explain what placement produced that, what the two numbers would have been under a spreading policy, and why spreading might not have improved the second one.

  3. RTL task. Extend placement_map so placement is a stored mapping rather than derived from the unit index. State the storage cost, what must now be validated on every allocation, and the new failure mode the stored version introduces that the derived version cannot have.

  4. Assertion task. Write the property proving the blast radius is confined to the failed device. Then explain why checking only the allocation count is insufficient, and construct the failure on which a count-only check passes while the host set is wrong.

  5. Design task. Add a third enclosure with partial reachability — reachable by two of four hosts. State how visible capacity changes per host, what the admission path must now check, and how the placement policy should choose between an enclosure that is closer and one that is more widely reachable.

  6. Testbench design. Design the stimulus that verifies per-device occupancy counters. Explain why equal occupancy on every device cannot detect a mis-indexed counter, and state the minimum arrangement that can.

  7. Debug task. A pool reports 62% utilisation before and after a device failure. Give the defect, explain why it produces a plausible number rather than an obvious error, and state the two measurements that expose it.

  8. Design review. A colleague proposes removing the failure headroom, arguing that migration can relocate allocations after a failure and reserving capacity in advance is wasteful. Give the strongest version of that argument, name the state it assumes exists, and state the measurement that decides whether the argument holds for a given pool.

26. Summary

Scale changes the blast radius, not the invariants.

  • Placement decides the cost of a failure. Four allocations packed lose 4 allocations and 1 host; spread they lose 1 and 1; four hosts sharing one device lose 4 and 4. Capacity accounting cannot distinguish any of them.
  • The invariants are unchanged. Conservation, unique ownership, generation safety and isolation carry over intact from 12.1 to 12.3. What is added is a location.
  • Reachable is not free. The same pool showed 16 units to one host and 8 to another, both correct, and a reach failure is fixed by connectivity rather than by memory.
  • Utilisation must use the usable denominator. The same 20 allocated units read 62% healthy and 83% after one device loss; against physical capacity both read 62%.
  • Draining is its own state. A device being evacuated serves what it holds and takes nothing new — without it, planned maintenance is an outage.
  • Grantable is not usable. Holding 4 units back took grantable from 4 to 0 while usable stayed at 24.
  • Resilience is a utilisation ceiling: 100%, 75% and 50% for absorbing zero, one and two device losses, and 66% for absorbing one more after a loss. Running above it raises cannot_absorb_err.
  • Spreading is paid for on every access. Average cost moved 4.0 to 7.0 to 8.5 as accesses shifted from local to three-quarters remote, which is the argument against the policy that section 8 argues for.
  • Verification: 105 assertion sites, 55 of 55 mutations killed, zero surviving. Nine first-run escapes were six stimulus gaps, two unobserved outputs and one equivalent mutation that was replaced rather than recorded. The baseline found four width defects — two of which the compiler had already printed.

Next: 12.5 Memory-Pooling Benefits and Challenges, which stops building mechanisms and asks whether the whole arrangement pays — stranded capacity recovered against latency added, utilisation gained against shared fate, and what evidence would settle it either way.

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.