Skip to content

AMBA CHI · Module 8 · Request Flows

WriteBack Flow

WriteUnique wrote a line the requester did not own; WriteBack is the opposite — a cache evicting a line it already holds dirty, returning the data to memory. It is the natural end of a line's residence in a cache. The flow uses the write handshake — request, DBID grant, the dirty data as CopyBackWrData — and the cache ends Invalid. Two things make it critical: it applies only to a dirty owned line (a clean line needs no writeback), and the evicting cache holds the only copy of that dirty data, so it must keep the line until the writeback is sent and accepted — drop it early and the modification is gone. Representative model, not the specification.

Intermediate15 min readAMBA CHIWriteBackEvictionDirtyCopyBack

Module 8 · Chapter 8.6 · Request Flows

Project thread — 8.5 wrote a line you did not own (WriteUnique). This chapter evicts a line you do own dirty (WriteBack). 8.7 is Evict — dropping a clean line, which needs no writeback.

1. Learning Outcomes

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

  • State that WriteBack evicts a line the requester owns dirty (UD/SD), returning its data to memory.
  • Trace the flow — REQ → DBID grant → CopyBackWrData → done — with the cache ending Invalid.
  • Explain why WriteBack applies only to a dirty line, not a clean one.
  • State the retention rule: hold the dirty line until the writeback data is sent and accepted.
  • Diagnose why invalidating the line early loses the modified data.
  • Implement a representative WriteBack FSM in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

Caches are finite, so lines constantly get evicted to make room. When an evicted line is dirty — it holds the only up-to-date value — it cannot simply be dropped; its data must be written back to memory first. WriteBack is that operation, and it runs constantly in any real system. Every cache-capacity conflict, every context switch that floods the cache, ends in a wave of WriteBacks.

It is marked critical for a blunt reason: a WriteBack is the point where a cache gives up the only copy of modified data. Everything about the flow is arranged to make sure that data lands in memory before the cache lets go. Miss the retention rule — invalidate the line before the data is safely handed off — and you lose a write with no error, no trace, just a stale value in memory. Understanding WriteBack is understanding how a cache safely relinquishes what only it holds.

3. Key Terms

4. Previous Chapter Connection

Chapter 8.5's WriteUnique wrote a line the requester did not own — a write-through, leaving the requester with no cached copy. WriteBack is the mirror on the ownership axis: the requester does own the line, holds it dirty, and is now giving it up.

The mechanics reuse what you already have: the write handshake of Chapter 7.2 — request, DBID grant, WriteData, done — and the grant-before-data rule. What is new is the reason and the end state: WriteBack exists to evict, so the cache transitions to Invalid, and the data it sends is the dirty line it owned. No snoop is needed — the requester is the dirty owner, so no other coherent copy conflicts. It is the write that ends a line's life in a cache.

5. Core Concept — evict a dirty owned line to memory

A WriteBack returns a dirty line the requester owns to memory and frees the cache entry. It is a write, so it uses the DBID handshake, and it ends with the cache Invalid.

  • Request. The cache needs to evict a line it holds in UD (or is the SD owner). It sends WriteBackFull (or WriteBackPtl) to the home. This is valid only because the line is dirty — a clean line would use Evict (Chapter 8.7).
  • Grant. The home returns a DBID (via CompDBIDResp) — the cache may send the data only after this (Chapter 7.2).
  • Send the dirty data. The cache sends CopyBackWrData, tagged with the DBID — the dirty line's data. Only now, as the data leaves and is accepted, does the cache transition the line to I.
  • Home writes memory. The home writes the data back to memory. The line is now clean in memory and gone from the cache. No snoop was needed.

The synthesis:

WriteBack evicts a dirty owned line to memory. It uses the write handshake — REQ → DBID → CopyBackWrData — and ends the cache in I. It is valid only for a dirty line, and the cache must retain that line until the data is sent and accepted, because it holds the only copy. No snoop is involved; the requester is the dirty owner giving the line up.

6. Engineering Mental Model — return the annotated book, file your edits

You have been keeping the office's reference book with your handwritten edits (a dirty line you own), and now you need your desk space back.

  • You cannot just toss it — your edits are the only up-to-date version. You must return it to the records office so your changes are filed (the writeback).
  • You request a return slot (the DBID grant), then hand over the annotated book (CopyBackWrData). Only once the office has taken it do you clear it off your desk (transition to Invalid).
  • If the book had no edits (a clean copy), you would not bother filing it — you would simply recycle your copy (that is Evict, next chapter).

The danger is clearing your desk before the office has the book in hand. Shred your annotated copy the instant you decide to return it, and if the handover falls through, your edits are gone — and they existed nowhere else. Hold the book until it is safely received.

7. Engineering Diagram — a WriteBackFull

A WriteBackFull flow evicting a line RN0 owns dirty. RN0 sends a REQ WriteBackFull to the Home Node. The Home Node returns a RSP CompDBIDResp granting a data buffer. RN0 sends the dirty line as a DAT CopyBackWrData tagged with the DBID and transitions from Unique Dirty to Invalid. The Home Node writes the data back to memory at the Subordinate Node. No snoop is needed because RN0 is the dirty owner.WriteBackFull — evict dirty, grant, copyback, write memoryRN0 · owner (UD)HN · homeSN · memoryREQ: WriteBackFullRSP: CompDBIDResp(grant)DAT: CopyBackWrData(UD to I)write memory
Figure 1 — a WriteBackFull evicting a line RN0 owns dirty (UD). RN0 requests on REQ; the Home Node grants a DBID via CompDBIDResp; RN0 sends the dirty line as CopyBackWrData tagged with that DBID and transitions to Invalid; the Home Node writes the data back to memory. No snoop is needed — RN0 is the dirty owner. The line ends in memory, clean, and gone from RN0's cache.

Read top to bottom: request, the DBID grant, the dirty data (as the line goes to I), then the write to memory. The cache held the line until the data left; no snoop was required.

8. WriteBack versus WriteUnique versus Evict

Three neighbouring operations, distinguished by ownership and dirtiness.

OperationRequester owns line?Line dirty?Sends data?Cache ends
WriteUnique (8.5)nowrites new datayes (new)I (write-through)
WriteBack (8.6)yesyesyes (the dirty line)I
Evict (8.7)yesno (clean)noI

The rule to carry: WriteBack is the dirty-eviction path. You write back because the line is dirty — its data must reach memory before the entry is freed. WriteUnique writes a line you never owned; Evict drops a clean line you own, sending no data (memory already has it). Choosing WriteBack for a clean line wastes bandwidth; choosing Evict for a dirty line loses data.

9. Hold the Dirty Line Until Handoff — the retention rule

The critical rule of WriteBack deserves its own section, because it is where dirty data is most easily lost.

  • The cache holds the only copy. A dirty line's latest value exists only in the owning cache — memory is stale. Until the writeback lands, nothing else has it.
  • Retain until sent and accepted. The cache must keep the line valid — holding its dirty data — until the CopyBackWrData is sent and accepted by the home. Only then may it transition to I and reuse the entry.
  • No early invalidation. Marking the line Invalid, or reusing the entry for a new line, before the data is handed off overwrites or discards the only copy of the modification.
  • Grant-before-data still applies. As with any write, the data goes out only after the DBID (Chapter 7.2) — so the retention window spans from the request through the accepted data.

The point to carry:

A WriteBack is a handoff of the only copy of modified data. The entire flow is arranged so the cache does not let go until memory has it: request, wait for the grant, send the data, and only on acceptance invalidate. The line's dirty data must survive the whole window — treat it like a baton that cannot be dropped until the next runner's hand is closed around it. Invalidate a beat too early and the modification exists nowhere.

10. Flow Walkthrough — WriteBackFull evicting UD

RN0 holds a line in UD and must evict it to make room; the line is homed at HN, backed by memory (SN).

  1. REQ. RN0 needs the entry. Because the line is dirty, it sends WriteBackFull — a clean line would use Evict instead. The line stays valid in RN0's cache.
  2. Grant. HN returns CompDBIDResp with a DBID — RN0 may now send the data. RN0 still holds the dirty line.
  3. CopyBackWrData. RN0 sends the dirty line's data, tagged with the DBID. As the data is sent and accepted, RN0 transitions the line UD → I and frees the entry.
  4. Write memory. HN writes the data to memory. The value RN0 modified is now safely in memory; RN0's cache no longer holds it.

End state: memory holds the latest value, RN0 is I, the entry is free for a new line. The retention was the crux: RN0 kept the dirty line through steps 1–3 and dropped it only when the data was accepted. Invalidate at step 1 and the DebugLab's data loss follows.

11. RTL / Hardware View — a WriteBack FSM

The requester's WriteBack is a small machine: issue the request (only if dirty), wait for the DBID, send the dirty data, and invalidate the line only once the data is accepted. Representative and sequential.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative requester-side WriteBack FSM (educational).
// Evict a DIRTY owned line: REQ (only if dirty) -> WAIT for DBID -> send
// CopyBackWrData -> invalidate ONLY after the data is accepted. holds_dirty keeps
// the line valid through the whole handoff, so the only copy is never dropped early.
module chi_writeback_txn (
  input  logic       clk, rst_n,
  input  logic       evict,          // this cache wants to evict the line
  input  logic       is_dirty,       // line is UD/SD -> WriteBack applies (else Evict)
  input  logic       dbid_valid,     // DBID grant arrived
  input  logic [3:0] dbid_in,        // the granted DBID
  input  logic       data_accepted,  // home accepted the CopyBackWrData
  output logic       req_valid,      // issue WriteBack (only for a dirty line)
  output logic       data_valid,     // drive CopyBackWrData (tagged dbid_q)
  output logic [3:0] data_dbid,
  output logic       holds_dirty,    // line still valid (dirty retained)
  output logic       invalidated     // line dropped to I (after data accepted)
);
  typedef enum logic [1:0] { IDLE, WAIT_DBID, SEND_DATA } state_t;
  state_t state, next;
  logic [3:0] dbid_q;
 
  always_comb begin
    next = state; req_valid = 1'b0; data_valid = 1'b0;
    case (state)
      IDLE:      if (evict && is_dirty) begin req_valid = 1'b1; next = WAIT_DBID; end
      WAIT_DBID: if (dbid_valid)                                next = SEND_DATA;
      SEND_DATA: begin data_valid = 1'b1; if (data_accepted)   next = IDLE; end
    endcase
  end
 
  assign data_dbid   = dbid_q;
  assign holds_dirty = (state != IDLE);                        // retain until handoff done
  assign invalidated = (state == SEND_DATA) && data_accepted;  // drop to I only after accept
 
  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n)                                dbid_q <= '0;
    else if (state == WAIT_DBID && dbid_valid) dbid_q <= dbid_in;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative requester-side WriteBack FSM (Verilog-2001).
module chi_writeback_txn (
  input clk, rst_n, evict, is_dirty, dbid_valid, data_accepted,
  input [3:0] dbid_in,
  output req_valid, data_valid, holds_dirty, invalidated,
  output [3:0] data_dbid
);
  localparam IDLE = 2'd0, WAIT_DBID = 2'd1, SEND_DATA = 2'd2;
  reg [1:0] state, next;
  reg [3:0] dbid_q;
 
  always @* begin
    next = state;
    case (state)
      IDLE:      next = (evict && is_dirty) ? WAIT_DBID : IDLE;
      WAIT_DBID: next = dbid_valid ? SEND_DATA : WAIT_DBID;
      SEND_DATA: next = data_accepted ? IDLE : SEND_DATA;
      default:   next = IDLE;
    endcase
  end
 
  assign req_valid   = (state == IDLE) && evict && is_dirty;
  assign data_valid  = (state == SEND_DATA);
  assign data_dbid   = dbid_q;
  assign holds_dirty = (state != IDLE);
  assign invalidated = (state == SEND_DATA) && data_accepted;
 
  always @(posedge clk or negedge rst_n)
    if (!rst_n)                                dbid_q <= 4'd0;
    else if (state == WAIT_DBID && dbid_valid) dbid_q <= dbid_in;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative requester-side WriteBack FSM (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_writeback_txn is
  port (
    clk, rst_n    : in  std_logic;
    evict         : in  std_logic;
    is_dirty      : in  std_logic;
    dbid_valid    : in  std_logic;
    dbid_in       : in  std_logic_vector(3 downto 0);
    data_accepted : in  std_logic;
    req_valid     : out std_logic;
    data_valid    : out std_logic;
    data_dbid     : out std_logic_vector(3 downto 0);
    holds_dirty   : out std_logic;
    invalidated   : out std_logic
  );
end entity;
 
architecture rtl of chi_writeback_txn is
  type state_t is (IDLE, WAIT_DBID, SEND_DATA);
  signal state : state_t := IDLE;
  signal dbid_q : std_logic_vector(3 downto 0) := (others => '0');
begin
  process (clk, rst_n)
  begin
    if rst_n = '0' then
      state  <= IDLE;
      dbid_q <= (others => '0');
    elsif rising_edge(clk) then
      case state is
        when IDLE      => if evict = '1' and is_dirty = '1' then state <= WAIT_DBID; end if;
        when WAIT_DBID => if dbid_valid = '1' then
                            dbid_q <= dbid_in;
                            state  <= SEND_DATA;
                          end if;
        when SEND_DATA => if data_accepted = '1' then state <= IDLE; end if;
      end case;
    end if;
  end process;
 
  req_valid   <= '1' when (state = IDLE and evict = '1' and is_dirty = '1') else '0';
  data_valid  <= '1' when state = SEND_DATA else '0';
  data_dbid   <= dbid_q;
  holds_dirty <= '0' when state = IDLE else '1';
  invalidated <= '1' when (state = SEND_DATA and data_accepted = '1') else '0';
end architecture;

All three issue the WriteBack only when the line is dirty, keep holds_dirty asserted through the handoff, and set invalidated only on acceptance — so the dirty line is never dropped early. The DebugLab shows the data loss when it is.

12. Verification View — dirty until accepted, and only for dirty lines

The properties that keep a WriteBack safe: it is issued only for dirty lines, and the line is retained until the data is accepted.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_writeback_txn.
// 1. WriteBack is issued only for a DIRTY line — a clean line uses Evict instead.
property p_only_dirty;
  @(posedge clk) disable iff (!rst_n)
    req_valid |-> is_dirty;
endproperty
 
// 2. The dirty line is retained until the writeback data is accepted — the line
//    is invalidated only after acceptance, never before.
property p_hold_until_accepted;
  @(posedge clk) disable iff (!rst_n)
    ($fell(holds_dirty)) |-> $past(data_accepted);
endproperty
 
// 3. Data goes out only after the DBID grant (grant-before-data).
property p_data_after_dbid;
  @(posedge clk) disable iff (!rst_n)
    $rose(data_valid) |-> $past(dbid_valid);
endproperty

The system point, beyond the checks:

A WriteBack is the one moment a cache voluntarily surrenders the sole copy of modified data, so the flow's correctness is entirely about never having a gap where that data exists nowhere. The line must be dirty to justify the writeback; the data must go out under a grant; and — the crux — the line must stay valid until the data is accepted, so at every instant the modification lives either in the cache or in the home's accepted buffer, never in the void between. The transition to Invalid is the last step, gated on acceptance, precisely because it is the point of no return: after it, the cache has nothing, and only a completed handoff makes that safe.

  • What it proves: WriteBack is dirty-only, data follows the DBID, and the line is held until accepted.
  • What it does not prove: the home actually wrote memory — that is the home/memory path (Chapter 7.2).
  • Bug signature: the line invalidated before data_accepted — the only copy of the modification dropped.

13. Testbench — retain through the handoff; skip clean lines

Evicts a dirty line and checks the line is held until acceptance, then confirms a clean line issues no WriteBack.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_writeback_txn;
  logic clk = 0, rst_n = 0;
  logic evict = 0, is_dirty = 0, dbid_valid = 0, data_accepted = 0;
  logic [3:0] dbid_in = 4'd0;
  logic req_valid, data_valid, holds_dirty, invalidated;
  logic [3:0] data_dbid;
  int errors = 0;
 
  chi_writeback_txn dut (.*);
  always #5 clk = ~clk;
 
  initial begin
    @(posedge clk) rst_n = 1;
 
    // Clean line: no WriteBack.
    @(posedge clk) begin evict = 1; is_dirty = 0; end
    @(posedge clk) if (req_valid) begin errors++; $display("FAIL WriteBack issued for clean line"); end
                   else $display("PASS clean line: no WriteBack");
    evict = 0;
 
    // Dirty line: WriteBack issued, line held.
    @(posedge clk) begin evict = 1; is_dirty = 1; end
    @(posedge clk) begin evict = 0; is_dirty = 1; end
    if (!holds_dirty) begin errors++; $display("FAIL dirty line not held"); end
    else $display("PASS dirty line: WriteBack issued, holds dirty");
 
    // Grant DBID.
    @(posedge clk) begin dbid_valid = 1; dbid_in = 4'd5; end
    @(posedge clk) dbid_valid = 0;
 
    // In SEND_DATA, before acceptance: still held, not invalidated.
    if (!data_valid || !holds_dirty || invalidated) begin
      errors++; $display("FAIL not holding dirty before acceptance"); end
    else $display("PASS holds dirty until data accepted");
 
    // Accept the data -> invalidate.
    @(posedge clk) data_accepted = 1;
    if (!invalidated) begin errors++; $display("FAIL not invalidated on acceptance"); end
    else $display("PASS invalidated after data accepted");
    @(posedge clk) data_accepted = 0;
    if (holds_dirty) begin errors++; $display("FAIL still holding after handoff"); end
    else $display("PASS line released after handoff");
 
    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 clean line: no WriteBack
PASS dirty line: WriteBack issued, holds dirty
PASS holds dirty until data accepted
PASS invalidated after data accepted
PASS line released after handoff
ALL TESTS PASSED

14. DebugLab — invalidating the line before the writeback is accepted

1

Invalidating the line before the writeback is accepted

ENTRY INVALIDATED/REUSED BEFORE WRITEBACK ACCEPTED -> DIRTY DATA LOST
Symptom

Under heavy cache pressure, writes are silently lost — a later read returns a stale value for a line that was definitely modified. It only happens when evictions and fills are rapid; a lightly loaded cache never shows it.

Evidence

The entry was reused before the writeback landed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
RN0 line L = UD (value = NEW),  memory = OLD
RN0 issues WriteBack(L) -> and immediately marks L's entry Invalid
RN0 reuses the entry for incoming line M -> writes M's data over NEW
later: CopyBackWrData for L is sent -> carries M's data or garbage, not NEW
memory = OLD (or M's data), NEW is gone  -> lost write

The dirty data NEW was overwritten in the cache before the writeback carried it out.

First Divergence

The cache transitioned the line to Invalid (and reused the entry) on issuing the WriteBack, not on the writeback data being accepted. From that point the only copy of the modification sat in an entry free to be overwritten.

Root Cause

A WriteBack surrenders the only copy of dirty data, so the line must be retained until the handoff completes. The modification lives solely in the owning cache until memory has it; freeing the entry before the CopyBackWrData is sent and accepted opens a window where a new fill overwrites it. This is a retention failure specific to eviction — distinct from the send-side grant-before-data ordering (Chapter 7.2): the data path was fine; the cache simply let go of the line too soon.

Fix

Keep the dirty line validholds_dirty asserted, the entry not reused — until the CopyBackWrData is accepted, then transition to I and free the entry. The FSM invalidates only in SEND_DATA on data_accepted, so the modification is always either in the cache or in the home's accepted buffer, never in a reusable-but-unwritten entry.

15. Common Mistakes

  • Invalidating before acceptance. Assumption: issuing the WriteBack frees the entry. Bug: dirty data lost (the DebugLab). Prevention: retain until data accepted.
  • WriteBack for a clean line. Assumption: all evictions write back. Bug: wasted bandwidth. Prevention: clean lines use Evict (Chapter 8.7).
  • Sending data before the DBID. Assumption: data follows the request. Bug: dropped write (Chapter 7.2). Prevention: wait for the grant.
  • Expecting a snoop. Assumption: the home snoops on a WriteBack. Bug: mis-modeled flow. Prevention: the owner writes back directly; no snoop.
  • Not going to Invalid. Assumption: the cache keeps the line. Bug: stale copy after eviction. Prevention: WriteBack ends the cache in I.
  • Reusing the entry mid-flight. Assumption: the entry is free once requested. Bug: overwritten dirty data. Prevention: reuse only after handoff.

16. Engineering Checklist

  • Issue WriteBack only for a dirty owned line (UD/SD) — clean lines use Evict.
  • Wait for the DBID grant before sending CopyBackWrData (Chapter 7.2).
  • Retain the dirty line until the data is sent and accepted.
  • Transition the line to I — and reuse the entry — only after acceptance.
  • Expect no snoop — the requester is the dirty owner.
  • Confirm memory holds the value before considering the line gone.

17. Key Takeaways

  • WriteBack evicts a line the requester owns dirty (UD/SD), returning its data to memory; the cache ends I.
  • The flow is the write handshake — REQ → DBID → CopyBackWrData — with no snoop (the owner writes back).
  • It applies only to dirty lines; a clean line needs Evict, which sends no data.
  • The cache holds the only copy of the dirty data, so it must retain the line until the writeback is accepted.
  • Invalidating or reusing the entry early loses the modification — silent, with no error.
  • Dirty-only, grant-before-data, retain-until-accepted; the model here is representative.

18. Quick Revision

WriteBack flow. WriteBack evicts a dirty owned line (UD, or the SD owner) back to memory, freeing the cache entry — the mirror of WriteUnique, which wrote a line you did not own. It uses the write handshake: REQ WriteBackFull → DBID grant (CompDBIDResp) → CopyBackWrData (the dirty data, tagged with the DBID) → the home writes memory. No snoop is needed, because the requester is the dirty owner. Two rules make it critical. It is valid only for a dirty line — a clean line uses Evict (8.7), sending no data. And the cache holds the only copy of the dirty data, so it must retain the line until the CopyBackWrData is sent and accepted, transitioning to I only then; invalidating or reusing the entry earlier overwrites the modification and loses the write silently. Dirty-only, grant-before-data, retain-until-accepted. Representative model; 8.7 is the Evict flow — dropping a clean line with no writeback.

Coming Next

Chapter 8.7 — Evict Flow. WriteBack returned a dirty line to memory; Evict is its lightweight sibling — dropping a clean line that needs no writeback, because memory already holds the value. Chapter 8.7 walks the flow: how a cache notifies the home it is dropping a clean line so the directory stays accurate, why no data moves, and when Evict rather than WriteBack is the correct way to free a cache entry — completing the eviction pair.