Skip to content

AMBA CHI · Module 10 · Cache State Management

Dirty Data Handling

Ownership and sharing were about who holds a line; this chapter is about how the dirty data itself moves. A dirty line is the only up-to-date copy of its value, so the protocol's overriding job is to never lose it. When the owner gives the line up, that value must travel to one of two durable places: back to memory on a writeback, or to another Request Node cache-to-cache. Every dirty-data flow is one of these two routes, chosen by the event. The law underneath is conservation of the latest value: a dirty release routes the data to memory or a peer, never nowhere. Release the line without either, and the modification evaporates. Representative model, not the specification.

Intermediate16 min readAMBA CHIDirty DataWritebackCache-to-CacheConservation

Module 10 · Chapter 10.5 · Cache State Management

Project thread — 10.3 tracked the owner, 10.4 the sharers. This chapter follows the dirty data itself — the two routes it takes and why it is never lost. 10.6 reads the spec's state tables.

1. Learning Outcomes

By the end of this chapter you should be able to:

  • State that a dirty line is the only up-to-date copy of its value.
  • Name the two routes dirty data takes — to memory (writeback) or to another RN (forward).
  • Match each dirty-data event to its route.
  • State the invariant — conservation of the latest value: a dirty release routes to one durable place.
  • Diagnose the lost update when a dirty line is released to nowhere.
  • Implement a representative dirty-data router in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

A dirty line is special: it is the only copy of the latest value, and memory is behind it. Everything the protocol does with dirty data is arranged so that value is never lost — every writeback, every cache-to-cache transfer, every PassDirty bit exists to keep the modification alive as the line moves. If you understand this one goal, the scattered dirty-data mechanisms of Modules 8 and 9 snap into a single picture.

That picture is a conservation law. The latest value always lives in exactly one place, and when it leaves that place it must arrive at another durable one — memory or a peer cache. There is no third option and no "gap" allowed. This chapter is the synthesis: it shows the two routes, the events that choose them, and the invariant that ties them together — so you can look at any dirty-data flow and ask the one question that matters: where did the latest value go?

3. Key Terms

4. Previous Chapter Connection

You have met every dirty-data mechanism separately: WriteBack sent a dirty line to memory (Chapter 8.6); ReadClean cleaned a dirty holder by writing back (Chapter 8.3); SnpUnique forwarded dirty data to a requester (Chapter 9.5); PassDirty handed off the writeback duty (Chapter 9.3); ownership transfer migrated the dirty owner (Chapter 9.6).

This chapter unifies them. Each is one of just two routes for the latest value — to memory or to another cache — chosen by the event. And each obeys one invariant: the value is never lost. So instead of memorizing which flow does what with dirty data, you can reason from the conservation law: whenever a dirty owner releases the line, the value must go somewhere durable, and there are only two somewheres.

5. Core Concept — the latest value takes one of two routes, and is never lost

A dirty line holds the only current value, so the protocol never lets it vanish. When the owner releases the line, the value travels to exactly one durable destination.

  • The value has one location. For a dirty line, the latest value lives only in the owner's cache; memory is stale (Chapter 9.5). There is exactly one current copy.
  • Route 1 — to memory (writeback). The owner writes the dirty value back to memory: on an eviction (WriteBack), on a clean-forcing read (ReadClean, SnpClean), or an invalidate-and-clean (CleanInvalid). Memory becomes current; the cache goes clean or invalid.
  • Route 2 — to another RN (forward). The owner sends the dirty value cache-to-cache to a new holder, on a forwarding snoop (SnpUnique forward): ownership migrates (Chapter 9.6), memory untouched.
  • Never nowhere. A dirty release must take route 1 or route 2. Releasing the line — downgrading, invalidating, or evicting the owner — without writing back or forwarding loses the value.

The synthesis:

A dirty line is the only copy of the latest value, so it is never lost. When the owner releases it, the value goes to exactly one durable destination: memory (writeback route) or another RN (forward route). Every dirty-data flow is one of these two, chosen by the event. The invariant — conservation of the latest value — is that a release always routes the data somewhere; never nowhere.

6. Engineering Mental Model — the only signed original

Think of the dirty line as the only signed original of a contract — memory holds just an old draft.

  • Because it is the only original, it can never be thrown away without first putting the signatures somewhere safe.
  • When you are done with it, you have exactly two safe options: file it in the records room (writeback to memory) so the official record is updated, or hand it directly to the next person who needs it (forward to another cache), who becomes its new keeper.
  • You may do one or the other — but you must do one. Shred the original without filing it or handing it over, and the signatures are gone; the draft in the records room never had them.

There is no third choice and no "set it down and walk away." The only original always ends up either in the records room or in someone's hands. That is dirty data.

7. Engineering Diagram — dirty data taking the forward route

Dirty data taking the forward route. RN0 sends a REQ ReadUnique to the Home Node. The Home Node snoops RN1, the dirty owner, with a SNP SnpUnique. RN1 returns its dirty data as a DAT SnpRespData with PassDirty set. The Home Node forwards the data to RN0 as a DAT CompData, and RN0 becomes the new owner in Unique Dirty. The latest value moved cache-to-cache without touching memory.Dirty data — the forward route, owner to new ownerRN0 · new ownerHN · homeRN1 · dirty ownerREQ: ReadUniqueSNP: SnpUniqueDAT: SnpRespData(dirty, PassDirty)DAT: CompData(dirty, install UD)
Figure 1 — the forward route. RN0's ReadUnique makes the Home Node snoop RN1, the dirty owner. RN1 returns its dirty data with PassDirty; the Home Node forwards it to RN0, which becomes the new owner (Unique Dirty). The latest value moved cache-to-cache — owner to new owner — without touching memory. The other route sends the same value to memory on a writeback.

Here the value took the forward route — RN1 to RN0, memory untouched. The other route would send the same value to memory on a writeback. Either way it lands somewhere durable; the diagram shows one of the two.

8. The Two Routes for Dirty Data

Every dirty-data event picks one route.

EventRouteDestination
WriteBack (evict dirty)writebackmemory
ReadClean / SnpCleanwritebackmemory (holder → SC)
CleanInvalidwritebackmemory (holder → I)
ReadUnique (forwarding)forwardanother RN (new owner)
ReadShared (from dirty)writebackmemory (holder → SC)
Evict (clean)none (no dirty data)

The rule to carry: dirty data goes to memory when the flow cleans the line (WriteBack, ReadClean, CleanInvalid — memory becomes current) and to another RN when the flow moves ownership (a forwarding ReadUnique — the value stays cached, migrated). A clean eviction has no dirty data to route. Every dirty-owner event is in the first two groups — memory or a peer — never a third.

9. Conservation of the Latest Value

The invariant that ties the routes together deserves its own statement.

  • One current copy, always. The latest value exists in exactly one place — the dirty owner's cache — until it is routed elsewhere.
  • A release must route it. Any event that ends the owner's hold on the dirty line (downgrade, invalidate, evict) must send the value to memory or a peer — a durable destination.
  • Two destinations, no more. There are only two durable places for the value: memory, or a cache that will itself write it back. A release picks one.
  • Never a gap. At no instant may the value exist nowhere. Route 1 lands it in memory before the cache goes clean; route 2 lands it in the new owner before the old one gives it up.

The point to carry:

Conservation of the latest value is the single law behind all of dirty-data handling: the modification is created once (a write) and thereafter only moved, never destroyed, until it is safely in memory. Every mechanism — writeback, forward, PassDirty, ownership transfer — is a way of moving it while preserving it. So the question to ask of any dirty-data flow is simply where did the value go? — and the answer must always be memory or a peer. An answer of "nowhere" is a lost update, and it is the only way dirty data is ever lost.

10. Walkthrough — dirty forwarded on a ReadUnique

RN1 owns a line in UD (value NEW; memory OLD). RN0 issues ReadUnique.

  1. One copy. The latest value NEW exists only in RN1 (UD). Memory holds OLD.
  2. Snoop the owner. The home snoops RN1 with SnpUnique — the owner of the dirty data.
  3. Route: forward. RN1 returns NEW with PassDirty set (Chapter 9.3). The value takes the forward route.
  4. New owner. The home forwards NEW to RN0, which installs UD — the new owner. NEW now lives in RN0; RN1 is I.
  5. Still one copy, still durable. The latest value is conserved — it moved RN1 → RN0, never touching memory, never absent. RN0 will eventually route it again (writeback or another forward).

The value was NEW throughout, in exactly one durable place at every step. Had RN1 gone to I without forwarding (and without a writeback), NEW would be gone — the DebugLab.

11. RTL / Hardware View — a dirty-data router

The home's dirty-data decision is: on an event acting on a dirty owner, route the value to memory or to a peer, and never to neither. Representative.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative dirty-data router (educational).
// When an event acts on a DIRTY owner, the latest value must be routed to exactly
// one durable destination: MEMORY (writeback) or a PEER (forward). Releasing the
// line with neither loses the value. 'preserved' is the conservation invariant.
module chi_dirty_router (
  input  logic owner_dirty,   // the owner holds the line dirty (latest value)
  input  logic to_memory,     // route: write the dirty value back to memory
  input  logic to_peer,       // route: forward the dirty value to another RN
  output logic preserved,     // the latest value has a durable destination
  output logic lost,          // released dirty with NO destination -> lost update
  output logic redundant      // routed to both (wasteful; should be one)
);
  // If a dirty owner is acted on, the value must go to memory or a peer.
  assign preserved = !owner_dirty || to_memory || to_peer;
  assign lost      = owner_dirty && !to_memory && !to_peer;
  assign redundant = owner_dirty && to_memory && to_peer;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative dirty-data router (Verilog-2001).
module chi_dirty_router (
  input  owner_dirty,
  input  to_memory,
  input  to_peer,
  output preserved,
  output lost,
  output redundant
);
  assign preserved = !owner_dirty || to_memory || to_peer;
  assign lost      = owner_dirty && !to_memory && !to_peer;
  assign redundant = owner_dirty && to_memory && to_peer;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative dirty-data router (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_dirty_router is
  port (
    owner_dirty : in  std_logic;
    to_memory   : in  std_logic;
    to_peer     : in  std_logic;
    preserved   : out std_logic;
    lost        : out std_logic;
    redundant   : out std_logic
  );
end entity;
 
architecture rtl of chi_dirty_router is
begin
  preserved <= '1' when (owner_dirty = '0' or to_memory = '1' or to_peer = '1') else '0';
  lost      <= '1' when (owner_dirty = '1' and to_memory = '0' and to_peer = '0') else '0';
  redundant <= '1' when (owner_dirty = '1' and to_memory = '1' and to_peer = '1') else '0';
end architecture;

All three assert preserved only when a dirty owner's value is routed to memory or a peer, and flag lost when it is routed to neither. The DebugLab is the lost case.

12. Verification View — a dirty release always routes the value

The properties that enforce conservation: a released dirty value always reaches a durable destination.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_dirty_router.
// 1. A dirty owner being acted on must route the value somewhere durable.
property p_dirty_preserved;
  @(*) owner_dirty |-> (to_memory || to_peer);
endproperty
 
// 2. 'lost' is asserted exactly when a dirty release routes to neither.
property p_lost_iff_nowhere;
  @(*) lost == (owner_dirty && !to_memory && !to_peer);
endproperty
 
// 3. A clean line (no dirty owner) needs no routing.
property p_clean_no_route;
  @(*) (!owner_dirty) |-> preserved;
endproperty

The system point, beyond the checks:

Conservation gives you a single audit for all of dirty-data handling: at every event, count the durable copies of the latest value, and require it to be at least one. That reduces a sprawl of flows — writeback, forward, downgrade, evict, migrate — to one question with a binary answer. The power of the framing is that it does not care which route was taken, only that some durable destination received the value; the routes are interchangeable ways of satisfying the same law. So a verification strategy for dirty data need not enumerate every flow — it can bind this one invariant everywhere a dirty owner is released and catch the lost update wherever it hides, because the only way to lose dirty data is to route it nowhere.

  • What it proves: a released dirty value always reaches memory or a peer; lost flags the gap.
  • What it does not prove: the value's correctness or PassDirty encoding — those are Chapters 9.3, 8.5.
  • Bug signature: lost asserted — a dirty owner released with no writeback and no forward.

13. Testbench — the routes and the loss

Drives the dirty-owner routing cases and checks preservation.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_dirty_router;
  logic owner_dirty, to_memory, to_peer, preserved, lost, redundant;
  int errors = 0;
 
  chi_dirty_router dut (.*);
 
  task automatic check(input logic od, tm, tp,
                       input logic exp_pres, exp_lost, input string name);
    owner_dirty = od; to_memory = tm; to_peer = tp; #1;
    if (preserved !== exp_pres || lost !== exp_lost) begin
      errors++; $display("FAIL %s: pres=%0b lost=%0b", name, preserved, lost);
    end else $display("PASS %s: pres=%0b lost=%0b", name, preserved, lost);
  endtask
 
  initial begin
    check(1'b1, 1'b1, 1'b0, 1'b1, 1'b0, "dirty -> memory (writeback): preserved");
    check(1'b1, 1'b0, 1'b1, 1'b1, 1'b0, "dirty -> peer (forward): preserved");
    check(1'b1, 1'b0, 1'b0, 1'b0, 1'b1, "dirty -> NOWHERE: LOST");
    check(1'b0, 1'b0, 1'b0, 1'b1, 1'b0, "clean -> no route needed: preserved");
 
    if (errors == 0) $display("ALL TESTS PASSED");
    else             $display("%0d FAILURE(S)", errors);
    $finish;
  end
endmodule

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
PASS dirty -> memory (writeback): preserved: pres=1 lost=0
PASS dirty -> peer (forward): preserved: pres=1 lost=0
PASS dirty -> NOWHERE: LOST: pres=0 lost=1
PASS clean -> no route needed: preserved: pres=1 lost=0
ALL TESTS PASSED

14. DebugLab — dirty data released with no destination

1

Dirty data released with no destination

DIRTY OWNER RELEASED WITH NO WRITEBACK AND NO FORWARD -> LATEST VALUE LOST
Symptom

A modification is silently lost — a later read returns stale data — whenever a dirty line is moved or dropped through a particular flow. Clean lines are unaffected; the loss tracks dirty lines being released.

Evidence

The latest value went nowhere:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
RN1 = UD (value NEW),  memory = OLD
event releases RN1's dirty line (e.g., a downgrade/invalidate)
  RN1 -> I (or SC)   -- gave up the dirty line
  to_memory = 0  (no writeback)
  to_peer   = 0  (no forward)
NEW existed only in RN1 -> discarded.  memory still OLD.
later read -> OLD  -> lost update

RN1 stopped being the dirty owner without the value going to memory or another cache.

First Divergence

The flow released the dirty owner — transitioned it out of UD/SD — without routing the value to memory (writeback) or to a peer (forward). From that instant the only copy of the latest value had no durable destination.

Root Cause

The latest value must be conserved — a dirty release routes it to memory or a peer, never nowhere. A dirty line is the sole copy of its value, so ending the owner's hold on it without a writeback or a forward discards it. This is the umbrella dirty-data failure: whatever the specific flow, the modification is lost the same way — released to a gap. Every event that touches a dirty owner must carry the value onward, because there is no third safe place for it and no moment it may exist nowhere.

Fix

Ensure every dirty release routes the value to exactly one durable destination: memory via a writeback (WriteBack, ReadClean, CleanInvalid) or a peer via a forward (a forwarding SnpUnique with PassDirty). The preserved invariant — owner_dirty implies to_memory or to_peer — must hold at every such event, so the latest value is always carried onward, never dropped.

15. Common Mistakes

  • Releasing dirty to nowhere. Assumption: a state change needs no data action. Bug: lost update (the DebugLab). Prevention: route to memory or a peer.
  • Reading a dirty line from memory. Assumption: memory is current. Bug: stale read (Chapter 9.5). Prevention: the owner's cache has the value.
  • Forwarding without PassDirty. Assumption: data alone suffices. Bug: ownerless dirty (Chapter 9.3). Prevention: set PassDirty on a forward.
  • Two durable owners. Assumption: copy the value. Bug: double writeback (Chapter 9.6). Prevention: one owner; move, do not copy.
  • Writing back a clean line. Assumption: all evictions write back. Bug: wasted bandwidth. Prevention: only dirty lines route to memory.
  • Losing the route on a downgrade. Assumption: downgrades are clean. Bug: a dirty downgrade drops data. Prevention: a dirty downgrade writes back or forwards.

16. Engineering Checklist

  • Treat a dirty line as the only copy of its latest value.
  • On any dirty release, route the value to memory or a peer — never nowhere.
  • Use the writeback route when the flow cleans the line (WriteBack / ReadClean / CleanInvalid).
  • Use the forward route when the flow moves ownership (a forwarding ReadUnique + PassDirty).
  • Keep exactly one durable copy — move the value, do not drop or duplicate it.
  • Verify conservation — a dirty owner acted on always routes the value.

17. Key Takeaways

  • A dirty line is the only copy of its latest value — memory is behind it.
  • Dirty data takes one of two routes when released: to memory (writeback) or to another RN (forward).
  • The writeback route cleans the line; the forward route moves ownership cache-to-cache.
  • The invariant is conservation of the latest value — a release routes it to a durable destination, never nowhere.
  • Releasing a dirty owner with no writeback and no forward loses the modification.
  • Two routes, one law: the value is always preserved; the model here is representative.

18. Quick Revision

Dirty data handling. A dirty line is the only copy of its latest value — memory holds the old one — so the protocol never lets it vanish. When the owner releases the line (downgrade, invalidate, evict), the value must travel to exactly one durable destination, and there are only two routes: to memory on a writeback (WriteBack, ReadClean, CleanInvalid — which clean the line) or to another RN on a forward (a forwarding SnpUnique with PassDirty — which moves ownership cache-to-cache). Every dirty-data flow in Modules 8 and 9 is one of these two, chosen by the event. The single law underneath is conservation of the latest value: the modification is created once by a write and thereafter only moved, never destroyed, until it reaches memory — so a dirty release must route the data somewhere, never nowhere. Release a dirty owner with no writeback and no forward, and the only copy is discarded — a silent lost update. Two routes, one conserved value; always ask where the latest value went. Representative model; 10.6 reads the spec's state-transition tables.

Coming Next

Chapter 10.6 — The CHI State Tables. You have the states, the transitions, ownership, sharing, and dirty-data routing; the next chapter shows how the specification itself encodes them. Chapter 10.6 teaches how to read the CHI spec's state-transition tables — the dense per-state, per-request grids that define legal behavior — turning the intuition you have built into fluency with the reference document engineers actually consult.