Skip to content
VLSI Mentor

CXL · Module 8

Device Cache Access

What a device owes when coherence activity reaches a line it holds: stall inbound actions but never drop them, supply the value before dropping the state, gate eviction on live operations, and never starve coherence behind the accelerator. Seven RTL models simulated, eighteen mutations, eighteen killed.

Chapter 8.2 followed a request outward — the device wanting a line and needing to find its authoritative copy. This chapter reverses the direction. Coherence activity arrives at a line the device is already holding, and the device has to answer.

1. First, What This Chapter Is Not

The curriculum names this chapter "how the host accesses a coherent device cache", and that phrase is easy to read in a way that is architecturally false.

That distinction also tells you what direction the data flows. When the host needs a line the device holds modified, the value moves device to host — which looks superficially like the host "reading the device cache", and is in fact the device discharging an obligation it took on when it cached the line.

2. The One-Sentence Model

A cached copy is a promise to answer coherence questions later. Caching is not a read that finished; it is a commitment that stays open for as long as the line is held — so the device must remain able to respond, must supply the value before it gives up a modified line, and must not let its own housekeeping quietly break that promise.

Call it the standing promise. The device chose to make it, the moment it decided to cache.

3. What This Chapter Owns

QuestionOwned by
What a borrowed line obliges8.1
Finding the authoritative copy8.2
What the device owes when coherence arrivesthis chapter
Living inside a real accelerator8.4
Moving writable ownership8.5
The host-side coherence engine3.4
Generic coherency theoryModule 13
End-to-end annotated flowsModule 14

4. The Exchange, From the Device's Side

A sequence diagram with four lifelines: host coherence logic, the device coherence agent, the device cache, and the accelerator. The host coherence logic sends an action concerning a line. The device coherence agent looks up the line in the device cache. In the first case the line is held modified, so the agent supplies the value to the host and only then drops the copy and responds. In the second case the line is not held at all, and the agent responds immediately that it holds nothing. The accelerator is shown being stalled during the first exchange because the cache port is busy.Two inbound actions: one the device holds modified, one it does not holdhost coherencedevice agentdevice cacheacceleratoraction concerningline Alook up Aheld, modifiedport busy — stallthe modified valuenow drop the copydoneaction concerningline Bnot held — nothingto do

Architectural. The arrows are obligations, not named messages.

Three things to read off it.

The value is supplied before the copy is dropped. Those two arrows are in that order for a reason, and §8 shows what happens when they are swapped.

"Not held" is a real answer, and a cheap one. Most inbound actions on most devices find nothing, and that path must be fast — a device that treats every action as expensive spends its cache port on non-answers.

The accelerator is stalled during the first exchange. That arrow is the honest cost of coherence, and §12 measures who wins the port.

5. Teaching-model boundary

6. RTL 1 — Stall an Action, Never Drop One

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Ready is the whole contract: refuse to accept, never accept and discard.
  assign act_ready = DROP_WHEN_FULL ? 1'b1 : !full;
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP1: a coherence action may be stalled, never dropped ===
  6 actions into depth 4 : correct level=4 in=4 stalled=2 dropped=0
  the two it could not take were stalled, not lost   : ok
  ready deasserted: back-pressure is the mechanism    : ok
  drop-when-full variant : in=4 dropped=1
  the dropping variant lost actions silently         : ok

A device may say "not yet". It may not say "yes" and then discard. The distinction is the ready signal, and it is the entire safety property of this module.

Why dropping is unrecoverable is worth being precise about. The system believes the device holds a copy of that line. An action that is dropped never gets answered, so that belief is never corrected — and there is no timeout on the device side that would notice, because from the device's point of view nothing happened at all. The system waits, or worse, proceeds on a stale assumption.

Compare that with a dropped request in 8.2: the device is the one waiting, so it has a timer and can recover. Here the device is the one being waited on, and only back-pressure keeps the conversation alive.

7. RTL 2 — The Obligation Is a Function of State

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The obligation is a function of state, not of what the accelerator wants.
  assign owes_data    = (st_m[rd_idx] != 2'd0) && dty_m[rd_idx];
  assign owes_nothing = (st_m[rd_idx] == 2'd0);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP2: the obligation is a function of state ===
  not held      : state=0 owes_data=0 owes_nothing=1
  a line we do not hold owes nothing                 : ok
  held clean    : state=1 owes_data=0
  a clean copy owes state, not data                  : ok
  held MODIFIED : state=2 dirty=1 owes_data=1
  a modified copy owes the VALUE                     : ok

Three states, three different debts, and the comment on that first line is the one to remember: the obligation depends on what the device holds, not on what the accelerator still wants. An accelerator that has finished with a line changes nothing about what the system is owed for it.

8. RTL 3 — Supply the Value, Then Drop the Line

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The correct order: invalidate only once nothing is owed.
  assign do_invalidate  = busy_q && (INVALIDATE_FIRST ? 1'b1 : !wb_pending_q);
  assign rsp_valid      = busy_q && (INVALIDATE_FIRST ? 1'b1 : !wb_pending_q);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP3: supply the value, THEN drop the line ===
  modified line : correct needs_wb=1 invalidate=0 rsp=0 | first-variant invalidate=1
  the correct handler waits for the value            : ok
  the broken variant dropped it immediately          : ok
  after capture : invalidate=1 rsp=1 dirty-responses=0
  value captured first, then the line was dropped    : ok
  the broken variant destroyed the only copy         : ok

rsp_valid carries the same condition as do_invalidate, and that is deliberate. Answering early is as dangerous as dropping early: the response tells the system it may proceed, and if the modified value has not yet been captured, whatever proceeds does so against a line whose only current copy is about to vanish.

Two separate errors exist for the two separate mistakes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
          // Dropping the line while its value was still owed loses it.
          if (wb_pending_q) data_lost_err <= 1'b1;
        // Answering before the value is safe tells the system it may proceed.
        if (rsp_valid && wb_pending_q) rsp_before_wb_err <= 1'b1;

9. Waveform — What a Modified Line Costs

Two inbound actions: modified, then clean

10 cycles
Ten clock cycles traced from the RTL. An action on a modified line arrives at cycle one. The handler becomes busy at cycle two with a writeback owed, and stays busy through cycle five. The value is captured at cycle four, the writeback obligation clears, and only then does the invalidate and response occur at cycle five. The dirty-response counter increments at cycle six. A second action on a clean line arrives at cycle seven, becomes busy at cycle eight, and invalidates and responds in that same cycle because nothing is owed.modified line: value owed firstmodified line: value owed firstclean line: one stepclean line: one stepvalue captured — only now may the line govalue captured — only nowmay the line goinvalidate and respond, togetherinvalidate and respond,togetherclean line: nothing owed, done in one cycleclean line: nothing owed,done in one cycleclkactionline_dirtybusywriteback_oweddata_capturedinvalidateresponsedirty_rsps0000001111clean_rsps0000000001t0t1t2t3t4t5t6t7t8t9
Icarus Verilog 13.0. Architectural teaching waveform derived from the simplified RTL model; it is NOT CXL.cache message timing.

Read busy across the two actions. The modified line holds the handler for four cycles; the clean line for one. The whole difference is the writeback obligation — and during those four cycles the cache port is unavailable to the accelerator, which is the cost §12 quantifies.

Notice also that invalidate and response rise in the same cycle, and that both wait for writeback_owed to fall. Three signals, one ordering rule, and every one of the three errors in this chapter's Debug Lab is that rule broken in a different place.

10. RTL 4 — The Evictor Does Not Know About Coherence

This is the path most likely to skip the protocol entirely, because the accelerator's reason for evicting — "I need this way for something else" — has nothing to do with coherence.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Two independent reasons to refuse, and they are not the same reason.
  assign evict_ok        = evict_req && (IGNORE_BUSY ? 1'b1 : !line_busy);
  assign needs_writeback = evict_ok && line_dirty && (line_state != 2'd0);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP5: the accelerator's evictor does not know about coherence ===
  evict a busy line : correct ok=0 | ignore-busy ok=1
  eviction was blocked while coherence held the line : ok
  the broken variant evicted it anyway               : ok
  evict a settled dirty line : ok=1 needs_wb=1
  a settled line may be evicted                      : ok
  and a settled dirty eviction still owes a writeback: ok

Two independent obligations, and a design can satisfy one while breaking the other. A busy line may not be evicted at all — a coherence operation holds a reference the tag array knows nothing about. A settled dirty line may be evicted, and still owes its value on the way out.

This is 8.1's invariant reappearing from the other direction, and it is worth stating as a rule: the device cannot discard coherence-relevant state merely because the accelerator no longer needs the line. The accelerator's opinion is not an input to the obligation.

11. RTL 5 — One Accepted Action, One Response

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP6: one accepted action, one response ===
  2 accepted : owed=2
  each accepted action owes exactly one response      : ok
  responding cleared exactly one obligation           : ok

The conservation law of this chapter. Accepting an action creates a debt; responding discharges exactly one. A response for an action nobody accepted must clear nothing, and the abuse instance proves the checker fires:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  abuse instance flagged a response nobody owed      : ok
  and it cleared nothing: an unowed id is void       : ok

12. RTL 6 — Coherence Must Not Starve Behind the Accelerator

The accelerator and the coherence path share one tag/state port. The scheduling policy is therefore a correctness decision, not a performance one — because the system is waiting on the coherence side, and a device that stops answering is a device that has broken its promise.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Coherence is promoted once it has waited long enough. Without this the
  // accelerator can hold the port indefinitely.
  assign promote     = (coh_wait_q >= COH_AGE_LIMIT[7:0]);
  assign grant_coh   = coh_req && (ACCEL_ALWAYS_WINS ? !accel_req
                                                     : (!accel_req || promote));

Twenty-four cycles of full contention, both requesters asking every cycle:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== EXP7: coherence must not starve behind the accelerator ===
  24 cycles of contention : fair accel=20 coh=4 max_coh_wait=4
                          : strict accel=24 coh=0 max_coh_wait=24
  the fair scheduler served coherence                : ok
  strict accelerator priority served it never       : ok
  worst coherence wait was exactly the age limit, 4  : ok
  under strict priority it waited all 24 cycles      : ok

Strict accelerator priority served coherence exactly zero times in twenty-four cycles. Not "rarely" — never. And the accelerator's own throughput barely changed: 24 versus 20 grants, a 17% gain bought at the price of the device no longer functioning as a coherent participant.

The aging policy bounds the worst case at exactly the age limit, which is the property worth asserting: not "coherence got some service" but "coherence waited no longer than the number we chose". That is a bound a design review can argue about.

13. The Inbound Path

An inbound coherence action enters a queue that back-pressures rather than dropping. It then contends for the tag and state port against the accelerator, arbitrated with aging so coherence cannot starve. The lookup result drives an obligation handler which supplies the value if one is owed before dropping the line and responding. An eviction gate sits beside the port, blocking the accelerator's replacement policy from taking a line that is busy.inbound actionstalled, never droppedport arbiteraging: coherencecannot starveacceleratorcontends for the sameporttag + stateheld? clean? modified?obligationhandlersupply, drop, answereviction gateblocks a busy lineresponse owedone per acceptedactionwhat is owedgated on busydischarge12

The eviction gate is the block that surprises people, because it does not sit on the inbound path at all — it sits on the accelerator's path, protecting inbound work from local housekeeping. That placement is the architectural point of §10: the threat to an in-flight coherence operation comes from the device's own replacement policy, not from the coherence side.

14. Assertions

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

Safety

PropertyIntent
No dropped actionaccepted implies stored
Value before stateinvalidate |-> !writeback_owed
Response after valueresponse |-> !writeback_owed
Busy implies presentbusy |-> state != INVALID
No eviction under a live opevict_ok |-> !line_busy
Dirty eviction owes dataevict && dirty |-> needs_writeback
One response per actionrespond |-> obligation existed
Single port grantnever both grant_accel and grant_coh

Liveness

PropertyAssumption
An accepted action eventually respondsthe writeback path drains
A stalled action is eventually acceptedthe queue drains
Coherence eventually gets the portthe aging promotion is enabled

The third is the one the strict-priority variant violates, and it is a liveness failure specifically: nothing unsafe happens, the device simply never answers.

Performance goals

GoalMeasured by
Bounded coherence waitmax_coh_wait_q against the age limit
Queue sized for burstinesspeak_q, n_stalled_q
Cheap non-answersmiss rate on inbound actions

15. Mutation Testing

Eighteen mutations. Eighteen killed.

MutationResult
Ready asserted while fullkilled
Stalled actions not countedkilled
Simultaneous push/pop as two incrementskilled
Modified line treated as owing nothingkilled
Busy-but-absent not flaggedkilled
Line invalidated before the value is capturedkilled
Response sent before the value is safekilled
Losing the modified value not flaggedkilled
No writeback ever considered pendingkilled
Eviction ignores a live coherence operationkilled
Dirty eviction owes no writebackkilled
Response for no outstanding action acceptedkilled
Responding does not clear the obligationkilled
Coherence never promoted over the acceleratorkilled
Both requesters granted the port at oncekilled
Max coherence wait lags by onekilled
Hit-plus-miss conservation check disabledkilled
Every inbound action counted as a hitkilled

The first run scored 14 of 18, and one survivor was a defect in my assertion rather than my stimulus.

Max coherence wait lagged by one and survived, because I had asserted a bound — "waited no more than 6" — when I could assert an exact value. The measured worst wait is exactly the age limit, 4, so the correct assertion is equality:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    if (gmw !== 8'd4) begin
      $display("FAIL max coherence wait=%0d expected exactly 4 (the age limit)", gmw);

A bound is a weaker assertion than an equality, and mutation testing is what tells you that you settled for one. If the design has a computable exact answer, assert the exact answer.

The other three were the familiar shapes: two needed instances reserved for illegal stimulus (busy-but-absent, and a response nobody owed), and one checker was unreachable until a variant made hits exceed counted actions.

16. Verification Plan

AreaApproach
Inbound queueOverflow with back-pressure; dropping variant compared
ObligationAll three states, each asserted separately
OrderingModified line: invalidate blocked until capture
Clean pathOne-step release, counted separately from dirty
EvictionBusy line blocked; settled dirty line allowed but owing
ResponsesOne per action; unowed response on an abuse instance
ArbitrationSustained contention, fair against strict
CountersIndependent oracle on the hit/miss split

The coverage cross is line state against inbound action kind against accelerator activity: not-held / clean / modified, crossed with downgrade / invalidate / fetch-data, crossed with accelerator idle / contending. The third axis is the one that produced the arbitration finding, and a testbench that exercises coherence with an idle accelerator will never see it.

17. Silicon Observability

CounterDiagnoses
inbound actions, hits, misseshow often the device is actually a holder
dirty responses vs clean responseshow much data the device is supplying
max_coh_wait_qwhether coherence is being starved
queue peak_q and stall countinbound burstiness and sizing
blocked evictionscontention between replacement and coherence
responses owedwhether anything is stuck undischarged
accelerator stall cyclesthe cost coherence is imposing on compute

The pair that answers the most common escalation is dirty responses against inbound actions. A device supplying data on most inbound actions is being used as a cache of modified data by the workload, which is expensive and often unintended; a device that almost never supplies data is holding mostly clean lines and its coherence cost is dominated by lookups, not writebacks. Those two situations look identical in an aggregate latency number and have completely different fixes.

18. Debug Lab

1

The system hangs waiting for a device that thinks nothing happened

DROPPED-ACTION
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign act_ready = 1'b1;               // always accept
if (act_valid && !full) store(...);    // ...but only store when there is room
Symptom

Under bursts of coherence activity the system stalls waiting on the device. The device's own counters show nothing wrong — its queue is not full, no errors are set, and it is idle. It reproduces only when inbound actions arrive faster than they are consumed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  drop-when-full variant : in=4 dropped=1
  the dropping variant lost actions silently         : ok
Root Cause

ready and "actually stored" disagreed. The device told the fabric it had accepted an action and then discarded it, so no response was ever generated.

This is unrecoverable from the device side by construction: from the device's point of view nothing happened, so no timer fires and no counter moves. The only agent that knows something is missing is the one waiting, and it has no way to say which action it was waiting for.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign act_ready = !full;              // refuse instead of discarding
assign do_push   = act_valid && act_ready;

Back-pressure is the mechanism. Count the stalls so the queue can be sized, and assert that accepted implies stored.

Prevention

Assert ready && valid |-> stored as a continuous property, and drive the queue past full in a directed test. A device that is never saturated in simulation cannot fail this, and inbound coherence bursts are exactly what a random accelerator workload does not produce.

2

A value the accelerator computed vanishes when the host asks for the line

INVALIDATE-FIRST
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Coherence wants the line — give it up.
state_m[idx] <= INVALID;
send_response();
Symptom

Silent corruption in shared buffers. A value the accelerator wrote is later read by the CPU as the pre-modification version. It only happens when the CPU touches a line the accelerator recently wrote, and there is no error anywhere.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  modified line : correct needs_wb=1 invalidate=0 rsp=0 | first-variant invalidate=1
  the broken variant destroyed the only copy         : ok
Root Cause

The device held the only current copy of the line and dropped it. Memory is stale by definition for a modified line, so invalidating without first supplying the value does not release a copy — it destroys the data.

Giving up a clean line and giving up a modified line look like the same operation in the state machine, and they are not: one releases a duplicate, the other releases the original.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign do_invalidate = busy_q && !wb_pending_q;   // nothing owed
assign rsp_valid     = busy_q && !wb_pending_q;   // same condition
if (do_invalidate && wb_pending_q) data_lost_err <= 1'b1;

Supply first, then drop, then answer — and gate the response on the same condition, because answering early has the same effect.

Prevention

Test the modified-line path explicitly and assert that invalidate never coincides with an outstanding writeback. A clean-line-only test passes on the broken design, and clean lines are the common case.

3

The host proceeds on a line whose value is still in flight

EARLY-RESPONSE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (start) send_response();          // answer immediately, write back later
Symptom

Rare corruption that looks like a race in the host rather than the device. The device's writeback does eventually happen, and the data arrives — after the host already read the line. Timing-dependent and effectively unreproducible.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  after capture : invalidate=1 rsp=1 dirty-responses=0
  value captured first, then the line was dropped    : ok
Root Cause

The response is a statement that the device has discharged its obligation. Sending it while the value is still owed tells the system it may proceed against a line whose current value the device has not yet handed over.

The line is not lost here — it arrives eventually — so this is subtler than Debug Lab 2: the failure is an ordering violation rather than a data loss, and the window is the length of the writeback path.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign rsp_valid = busy_q && !wb_pending_q;
if (rsp_valid && wb_pending_q) rsp_before_wb_err <= 1'b1;

Tie the response to the same condition as the invalidate, and give the violation its own error so it is distinguishable from data loss.

Prevention

Keep two separate checkers — data-lost and response-before-writeback. They have the same root cause and different symptoms, and a single merged assertion makes the debug harder rather than easier.

4

A line disappears while a coherence operation is acting on it

EVICT-BUSY
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Replacement policy picked this way.
assign evict_ok = evict_req;
Symptom

A coherence operation completes against a line that is no longer there, producing a response that describes a state the device does not have. Rare, load-dependent, and correlated with cache pressure rather than with coherence traffic.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  evict a busy line : correct ok=0 | ignore-busy ok=1
  the broken variant flagged evicted-while-busy      : ok
Root Cause

Two independent agents act on the tag array: the coherence path and the accelerator's replacement policy. The coherence operation holds a reference the tag array knows nothing about, so the evictor sees an ordinary line and takes it.

The accelerator's reason for evicting is completely unrelated to coherence, which is why this path is the one most likely to skip the protocol.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign evict_ok = evict_req && !line_busy;   // gate on the live operation

Gate eviction on the busy flag, and count blocked evictions so the contention is visible rather than merely handled.

Prevention

Direct a test at eviction of a line mid-coherence-operation. The two events have to be aligned deliberately; random cache pressure plus random coherence traffic hits the window too rarely to rely on.

5

A dirty line is evicted and its value is never written back

SILENT-EVICT
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (evict_ok) state_m[idx] <= INVALID;   // no writeback consideration
Symptom

Data written by the accelerator is occasionally missing, with no correlation to host activity at all — which is what makes this different from the coherence-triggered losses. It correlates with working-set size: it appears when the cache starts evicting.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  evict a settled dirty line : ok=1 needs_wb=1
  and a settled dirty eviction still owes a writeback: ok
Root Cause

Eviction was treated as purely local housekeeping. For a clean line it is — the copy is a duplicate. For a modified line the device holds the only current value, so eviction is a data-movement operation, not a bookkeeping one.

The absence of host involvement is what makes this easy to miss: no coherence action arrived, nothing external happened, and the device quietly discarded the value on its own initiative.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign needs_writeback = evict_ok && line_dirty && (line_state != INVALID);
// and the line may not go INVALID until the writeback is accepted

Make the writeback obligation part of the eviction path, and count writebacks so the rate is visible.

Prevention

Test eviction of a dirty line with no coherence traffic present. It is the purely local path, and a testbench that only evicts under coherence pressure will not cover it.

6

The device stops answering coherence entirely under load

COH-STARVED
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign grant_coh   = coh_req && !accel_req;   // accelerator always wins
assign grant_accel = accel_req;
Symptom

The accelerator runs at full rate and the rest of the system degrades. Host threads touching shared lines stall for long periods. The device reports no errors — it is busy and productive, and its own throughput counters look excellent.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  24 cycles of contention : fair accel=20 coh=4 max_coh_wait=4
                          : strict accel=24 coh=0 max_coh_wait=24
Root Cause

Strict accelerator priority on a shared cache port. While the accelerator has work, coherence never gets the port — and a busy accelerator always has work.

Measured over twenty-four cycles of contention, coherence was served zero times. This is a liveness failure, not a safety one: nothing incorrect happens, the device simply stops participating, and the symptom appears everywhere except the device.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
assign promote   = (coh_wait_q >= COH_AGE_LIMIT);
assign grant_coh = coh_req && (!accel_req || promote);

Age the coherence request and promote it past the accelerator once it has waited long enough. The measured cost was 24 accelerator grants falling to 20 — a 17% reduction that buys a bounded worst-case coherence wait.

Prevention

Run sustained contention with both requesters asserting every cycle, and assert an exact bound on the worst coherence wait. A test where the accelerator idles occasionally will let coherence through and hide the policy entirely.

7

Reported coherence hit rate is impossible

ACTION-MISCOUNT
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (action_in) n_hit_q <= n_hit_q + 1;   // every action counted as a hit
Symptom

Telemetry claims the device holds nearly every line the host asks about, which contradicts its cache size. Capacity decisions made from the number are wrong in an expensive direction — the cache is enlarged to fix a problem it does not have.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  actions=20 hits=13 misses=7 supplied=3 invalidated=5 stalls=4
  every inbound action landed in exactly one class   : ok
Root Cause

The hit counter was driven by the arrival of an action rather than by the lookup result. Most inbound actions on most devices find nothing — the device holds a small subset of host memory — so conflating arrival with hit inflates the number toward 100% by construction.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (action_hit)  n_hit_q  <= n_hit_q  + 1;
if (action_miss) n_miss_q <= n_miss_q + 1;
if (n_hit_q + n_miss_q > n_action_q) classify_err <= 1'b1;

Count the outcome, expose both classes, and assert the conservation law so drift is detectable.

Prevention

Check the counters against an independent oracle rather than against each other, and prove the conservation checker can fire — on the correct design it is true by construction and therefore proves nothing until a variant makes it reachable.

19. Design Review

  1. Can an inbound coherence action ever be dropped? If ready and "stored" can disagree, yes.
  2. Is the response gated on the same condition as the invalidate? Answering early has the same effect as dropping early.
  3. What blocks eviction of a line under a live coherence operation?
  4. Does a dirty eviction with no coherence traffic still write back?
  5. Who wins the cache port under sustained contention, and what bounds the loser's wait?
  6. Is the coherence wait bound asserted as an equality or a bound?
  7. Does the hit counter count arrivals or outcomes?
  8. Which counter tells you the device is holding mostly modified data?
  9. What happens to outstanding obligations on reset?

20. How This Appears in Real Engineering

Architecture. The shared cache port and its arbitration policy is the decision that determines whether the device is a good coherent citizen. It is usually made early, for area reasons, and revisited late for correctness reasons.

RTL. The three-signal ordering rule — writeback, invalidate, response — is small and touched by several paths. Centralising it in one handler is what stops each path getting it slightly wrong.

DV. The eviction-under-coherence race and the sustained-contention case are both directed tests. Neither is reachable by random traffic, and both find serious bugs.

Post-silicon. Dirty-response rate against inbound action rate is the number that tells an operator whether the workload is using the device as intended.

Software. Placement again: a workload where the accelerator writes lines the CPU then reads generates a supply-the-value action every time, which is the most expensive inbound path there is.

21. Common Misconceptions

BeliefCorrection
The host reads the device's cacheThe host's operation concerns a line; the device is a holder
The device cache is addressable memoryIt holds copies indexed by host addresses
An inbound action can be dropped if busyIt can be stalled; dropping is unrecoverable
Invalidate and writeback are independentThe value must be supplied first
Responding early is harmlessIt tells the system it may proceed
Eviction is local housekeepingFor a dirty line it is data movement
The accelerator finishing frees the obligationThe obligation depends on state, not on use
Accelerator priority is a performance choiceStarving coherence is a liveness failure

22. Interview Reasoning

23. Exercises

  1. Analysis. A device reports inbound_actions=50000, hits=140, dirty_responses=138, and the accelerator stalls heavily. Nothing is inconsistent. Explain what the workload is doing and name the one change most likely to help.

  2. Design. Add a downgrade action that reduces a modified line to clean rather than invalidating it. State what the device still owes, what it keeps, and which existing assertion must be weakened.

  3. RTL task. Extend evict_gate so a blocked eviction is retried automatically rather than dropped. State the new liveness property and the counter that would show the retry loop is not making progress.

  4. DV task. Write the coverage cross for the inbound path, then explain why the accelerator-activity axis is the one most often omitted and what it hides.

  5. Debug task. Host threads touching shared lines stall for long periods while the accelerator reports excellent throughput and no errors. Give your investigation order and the single counter that identifies the cause.

  6. Design review. A colleague proposes dropping inbound actions when the queue is full "because the host will retry". Give the strongest version of that argument, then name exactly what breaks and why the device cannot detect it.

24. Summary

A cached copy is a standing promise, and this chapter is what keeping it costs.

  • The host does not read the device's cache. The host's operation concerns a line; the device is a holder and its job is to answer.
  • An inbound action may be stalled but never dropped — the device is the one being waited on, so back-pressure is the only safe relief.
  • The obligation is a function of state: nothing if not held, state if clean, the value if modified.
  • Supply, then drop, then answer — and the response is gated on the same condition as the invalidate, because answering early has the same effect as dropping early.
  • Eviction is not local housekeeping. A busy line may not be evicted at all; a settled dirty line may be, and still owes its value.
  • The accelerator finishing changes nothing. The obligation depends on the line's state, not on its usefulness.
  • Measured: a modified line costs four cycles of obligation against one for a clean line.
  • Measured: strict accelerator priority served coherence zero times in twenty-four cycles, for a 17% accelerator gain. Aging bounds the worst wait at exactly the age limit.
  • Verification lesson: a bound is a weaker assertion than an equality. A mutation survived because I asserted "no more than six" where the design has an exact answer of four.

Chapter 8.4 puts this machinery inside a real accelerator and asks what it does to the compute pipeline.

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.