Skip to content

AMBA CHI · Module 7 · CHI Transaction Model

The Write Transaction

The read pulled a line toward a requester; the write pushes one back — and it inverts the handshake. In a read, data returns unsolicited as CompData. In a write, nothing moves until the home grants a place to put it: the requester sends the request, the home returns a DBID naming where to send the data, and only then does the requester drive the write-data beats, tagged with that DBID. Sending data before the DBID arrives — the habit from AXI, where write data follows the address freely — drops it on the floor, because the home has no buffer for it. Learn the write's structure, its opcodes, and the grant-before-data rule. Representative model, not the specification.

Foundation15 min readAMBA CHIWrite TransactionWriteBackDBIDWrite Data

Module 7 · Chapter 7.2 · CHI Transaction Model

Project thread — 7.1 walked the read: data returns as CompData, closed by CompAck. This chapter walks the write, whose handshake runs the other way — the home grants a buffer before any data moves. 7.3 does the atomic.

1. Learning Outcomes

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

  • Trace a write end to end: REQ → DBID grant → WriteData → Comp.
  • Explain the grant-before-data rule and how it inverts the read's handshake.
  • State that write data is correlated to the home's buffer by DBID, not TxnID.
  • Name the common write opcodes and what each does — WriteBack, WriteClean, WriteUnique, WriteNoSnp.
  • Contrast the CHI write handshake with AXI, where write data follows the address without a per-transaction grant.
  • Implement a representative requester-side write-transaction FSM in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

The write is the read's mirror, and the mirror is not symmetric. A read asks and the data comes back on its own. A write cannot just send — the requester has to be told where to put the data first. That single difference, grant-before-data, is the heart of the write transaction, and it is exactly where engineers coming from AXI stumble: in AXI you push write data right behind the address; in CHI you must wait for the DBID.

Get this right and every write opcode — evicting a dirty line, pushing a partial update, streaming to a non-cached region — is the same shape with a different intent. Get it wrong and the data lands nowhere: the home never allocated a buffer for it, and there is no DBID to correlate it. Understanding the write means understanding why you wait.

3. Key Terms

4. Previous Chapter Connection

Chapter 7.1 established the transaction as an ordered exchange and walked the read: request, the home resolves by snoop or memory, CompData returns, and CompAck closes it. The read's data was unsolicited — it arrived because the requester asked, with no separate permission step.

The write reverses that. The requester still opens with a REQ, but it cannot send the data yet — it must first receive a DBID from the home (the write-data handshake you met at the channel level in Module 5). Only then does it drive the data, tagged with that DBID. Same transaction discipline as the read — allocate a tracker, correlate carefully, close cleanly — but the data flows after a grant, not before.

5. Core Concept — the write, end to end

A write transaction moves a line of data from a requester to the home (and on to memory), leaving the coherence state consistent. It has four beats, and the second is the one that matters.

  • Request. The Request Node (RN) sends a REQ with a write opcode — say WriteBackFull, evicting a dirty line — targeting the Home Node (HN). It allocates a tracker with its TxnID. It does not send data yet.
  • Grant (DBID). The HN allocates a data buffer and returns a DBID on the RSP channel — via DBIDResp, or CompDBIDResp which also carries completion. The DBID names the slot the data must go to.
  • Send data. Now the RN drives the WriteData beats on the DAT channel, each tagged with the DBID (not the TxnID) and carrying byte-enables for partial writes. The home matches the data to its buffer by DBID.
  • Complete. The HN, having the data, writes it to the Subordinate Node (SN) — memory — as needed and signals Comp (if not already combined into CompDBIDResp). The transaction closes.

The synthesis:

A write is request → DBID grant → WriteData → complete. The requester asks, the home says here is where to put it (the DBID), and only then does the data move — tagged with the DBID so the home files it in the buffer it allocated. Read data arrives unsolicited; write data waits for a grant. Grant-before-data is the write's defining rule.

6. Engineering Mental Model — shipping to an assigned dock

You have a pallet to ship back to a warehouse. You do not just send the truck.

  • You call ahead with a shipment notice (the REQ) — "I have a pallet to return."
  • The warehouse assigns you a dock and a bay number (the DBID) — "back up to bay 12." Until they do, there is nowhere for the truck to unload.
  • Now you send the truck, and the driver writes bay 12 on the paperwork (the WriteData, tagged with the DBID) so the goods land in the right slot.
  • The warehouse confirms receipt (Comp) and shelves the pallet (writes memory).

Send the truck before you have a bay number and it idles at a closed door — or unloads in the wrong place. The bay assignment has to come first. That assignment is the DBID.

7. Engineering Diagram — a WriteBackFull, grant then data

A WriteBackFull write transaction across the CHI channels. RN0 sends a REQ WriteBackFull to the Home Node. The Home Node returns a RSP CompDBIDResp granting a data buffer identified by a DBID. RN0 then sends the DAT CopyBackWrData tagged with that DBID to the Home Node. The Home Node writes the line to memory at the Subordinate Node. The grant precedes the data.WriteBackFull — the DBID grant comes before the dataRN0 · requesterHN · homeSN · memoryREQ: WriteBackFullRSP: CompDBIDRespDAT: CopyBackWrDatawrite memory
Figure 1 — a WriteBackFull write transaction. RN0 requests on REQ; the Home Node grants a data buffer and returns a DBID on RSP (CompDBIDResp); only then does RN0 send the write data on DAT, tagged with that DBID; and the Home Node writes the line to memory. The grant precedes the data — the inverse of the read, where data returns unsolicited.

Read top to bottom: request, then the grant, then the data tagged with the DBID, then the line lands in memory. The data never precedes the grant.

8. Write Opcodes and What They Do

Like reads, writes are a family; the opcode names the intent.

Write opcodeWhat it doesTypical use
WriteBackFullreturn a whole dirty line to the homeevicting a UD line from a cache
WriteBackPtlreturn part of a dirty line, with byte-enablespartial dirty eviction
WriteCleanFullpush a dirty line to memory but keep a clean copyflush without losing the line
WriteUniquewrite to a line without first owning it coherentlyproducer writing shared data
WriteEvictFullevict a clean-but-owned linemaking room, no data lost
WriteNoSnpwrite to a non-coherent region, no snoopsstreaming to a device / memory buffer

The rule to carry: the opcode sets what the home must do — write back a dirty eviction, flush but retain, invalidate other copies for a WriteUnique, or skip coherence entirely for WriteNoSnp — but the handshake is the same for all of them: request, DBID grant, data, complete. Intent varies; grant-before-data does not.

9. The Grant-Before-Data Inversion

Put the read and the write side by side and the asymmetry is the whole lesson.

  • Read — data is pulled, unsolicited. The requester sends REQ; the home returns CompData on its own initiative; the requester acknowledges with CompAck (Chapter 7.1). The requester never asks permission to receive.
  • Write — data is pushed, but only when granted. The requester sends REQ; the home returns a DBID; only then does the requester send WriteData. The requester must be granted a destination before it can send.
  • Correlation differs too. Read data (CompData) echoes the requester's TxnID. Write data is tagged with the home's DBID — because it is going to the home's buffer, and the home decides which buffer by handing out the DBID.

The point to carry:

The direction of the data decides the handshake. Data coming to the requester needs no permission — the requester asked for it. Data going from the requester into the home's buffer needs a grant, because only the home knows when a buffer is free and which one to use. That grant is the DBID, and it must arrive before the data. This is precisely where AXI intuition misleads: AXI lets write data follow the address without a per-transaction grant; CHI does not.

10. Transaction Walkthrough — a WriteBackFull

RN0 (Node 0) evicts a dirty line (UD) to its home HN (Node 4), which writes it to memory (SN, Node 6).

  1. REQ. RN0 allocates tracker TxnID 7 and sends WriteBackFull: SrcID 0, TgtID 4. No data yet.
  2. Grant. HN allocates a data buffer, assigns it DBID 3, and returns CompDBIDResp (SrcID 4, TgtID 0) — completion plus the DBID.
  3. WriteData. RN0 now sends CopyBackWrData on DAT, tagged with DBID 3, carrying the line and its byte-enables. RN0's cached copy transitions out of UD (to I for an eviction).
  4. Match by DBID. HN matches the incoming data to its buffer by DBID 3 — not TxnID — and accepts the line.
  5. Memory. HN writes the line to the SN (memory). The transaction is complete.

Four beats, and the ordering is fixed: the DBID (step 2) came before the data (step 3). Reverse them and the data has no buffer to land in.

11. RTL / Hardware View — a requester-side write FSM

The requester's write side is a small machine that must not drive data until it has captured a DBID. Issue the request, wait for the DBID, send the tagged data, retire. Representative and sequential.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative requester-side WRITE transaction FSM (educational).
// Issue REQ -> WAIT for DBID -> only THEN drive WriteData tagged with the DBID.
// Grant-before-data: data_valid can never assert before a DBID is captured.
module chi_rn_write_txn (
  input  logic       clk, rst_n,
  input  logic       start,          // launch a write
  input  logic       dbid_valid,     // DBID grant arrived
  input  logic [3:0] dbid_in,        // the granted DBID
  output logic       req_valid,      // drive the REQ
  output logic       data_valid,     // drive the WriteData (tagged with dbid_q)
  output logic [3:0] data_dbid,      // the tag carried on the write data
  output logic       busy
);
  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 (start)       begin req_valid = 1'b1; next = WAIT_DBID; end
      WAIT_DBID: if (dbid_valid)                          next = SEND_DATA;   // grant first
      SEND_DATA: begin data_valid = 1'b1;                 next = IDLE;      end // data, tagged
    endcase
  end
 
  assign data_dbid = dbid_q;                 // write data carries the granted DBID
  assign busy      = (state != IDLE);
 
  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;   // capture the grant
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative requester-side WRITE transaction FSM (Verilog-2001).
module chi_rn_write_txn (
  input clk, rst_n, start, dbid_valid,
  input [3:0] dbid_in,
  output req_valid, data_valid, busy,
  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:      if (start)      next = WAIT_DBID;
      WAIT_DBID: if (dbid_valid) next = SEND_DATA;
      SEND_DATA:                 next = IDLE;
      default:                   next = IDLE;
    endcase
  end
 
  assign req_valid  = (state == IDLE) && start;
  assign data_valid = (state == SEND_DATA);
  assign data_dbid  = dbid_q;
  assign busy       = (state != IDLE);
 
  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 WRITE transaction FSM (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_rn_write_txn is
  port (
    clk, rst_n : in  std_logic;
    start      : in  std_logic;
    dbid_valid : in  std_logic;
    dbid_in    : in  std_logic_vector(3 downto 0);
    req_valid  : out std_logic;
    data_valid : out std_logic;
    data_dbid  : out std_logic_vector(3 downto 0);
    busy       : out std_logic
  );
end entity;
 
architecture rtl of chi_rn_write_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 start = '1'      then state <= WAIT_DBID; end if;
        when WAIT_DBID => if dbid_valid = '1'  then
                            dbid_q <= dbid_in;                     -- capture the grant
                            state  <= SEND_DATA;
                          end if;
        when SEND_DATA =>                          state <= IDLE;
      end case;
    end if;
  end process;
 
  req_valid  <= '1' when (state = IDLE and start = '1') else '0';
  data_valid <= '1' when state = SEND_DATA else '0';
  data_dbid  <= dbid_q;
  busy       <= '0' when state = IDLE else '1';
end architecture;

All three drive data_valid only in SEND_DATA, which is reachable only after dbid_valid — grant-before-data, enforced by the state graph. The DebugLab shows what happens when that ordering is assumed away.

12. Verification View — data never precedes the grant

The property that defines the write handshake: write data is only ever driven after a DBID has been captured, and it carries that DBID.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_rn_write_txn.
// 1. WriteData is only asserted after a DBID grant has been seen — never before.
property p_data_after_dbid;
  @(posedge clk) disable iff (!rst_n)
    data_valid |-> $past(dbid_valid);
endproperty
 
// 2. The write data carries exactly the DBID that was granted.
property p_data_tagged_with_dbid;
  @(posedge clk) disable iff (!rst_n)
    data_valid |-> (data_dbid == $past(dbid_in));
endproperty

The system point, beyond the checks:

A write's correctness rests on an ordering and a tag. The ordering — DBID before data — exists because the home owns the buffers; it must allocate one and name it before the requester can aim data at it. The tag — the DBID on every write-data beat — exists because the home files incoming data by buffer, not by transaction: many writes may be in flight, and the DBID says which buffer this beat belongs to. Break the ordering and the data has no home; drop the tag and the home cannot place it. The read needed neither, because its data flowed the other way, correlated by the requester's own TxnID.

  • What it proves: write data follows the DBID grant and carries the granted DBID.
  • What it does not prove: correct byte-enables or memory update — those are the data payload's and the home's concern.
  • Bug signature: write data asserted before a DBID — a beat the home has no buffer for.

13. Testbench — drive a write, grant-before-data

Launches a write, checks no data is driven before the DBID, then delivers the DBID and checks the data is sent tagged with it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_rn_write_txn;
  logic clk = 0, rst_n = 0, start = 0, dbid_valid = 0;
  logic [3:0] dbid_in = 4'd0;
  logic req_valid, data_valid, busy; logic [3:0] data_dbid;
  int errors = 0;
 
  chi_rn_write_txn dut (.*);
  always #5 clk = ~clk;
 
  initial begin
    @(posedge clk) rst_n = 1;
 
    // Launch the write.
    @(posedge clk) start = 1;
    @(posedge clk) start = 0;
    if (!busy) begin errors++; $display("FAIL not busy after request"); end
    else $display("PASS busy asserted while outstanding");
 
    // No write data may appear before the DBID grant.
    repeat (2) @(posedge clk)
      if (data_valid) begin errors++; $display("FAIL WriteData before DBID"); end
 
    // Grant DBID 3.
    @(posedge clk) begin dbid_valid = 1; dbid_in = 4'd3; end
    @(posedge clk) dbid_valid = 0;
 
    // Now the data should be sent, tagged with DBID 3.
    if (!data_valid) begin errors++; $display("FAIL no WriteData after DBID"); end
    else if (data_dbid !== 4'd3) begin errors++; $display("FAIL wrong DBID tag %0d", data_dbid); end
    else $display("PASS WriteData sent tagged with DBID %0d", data_dbid);
 
    @(posedge clk);
    if (busy) begin errors++; $display("FAIL still busy after data"); end
    else $display("PASS transaction retired after data");
 
    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 busy asserted while outstanding
PASS WriteData sent tagged with DBID 3
PASS transaction retired after data
ALL TESTS PASSED

14. DebugLab — sending write data before the DBID

1

Sending write data before the DBID

WRITE DATA SENT BEFORE DBID GRANT -> DATA LOST
Symptom

Writes are silently lost — memory keeps stale values though the requester believes it wrote, and there is no error, no retry, no timeout. It happens under pipelined writes and never when a write sits alone with slack before it.

Evidence

The write-data beats appear on DAT before the DBID arrives on RSP:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
t0  RN sends REQ WriteBackFull, TxnID 7
t1  RN drives WriteData beats            <-- before any DBID
t2  HN returns CompDBIDResp (DBID 3)     <-- grant arrives AFTER the data
    home had no buffer at t1 -> beats at t1 dropped
    home now waits for data tagged DBID 3 that RN already "sent"

The data that mattered was on the wire before the home had a place for it; the beats the home is now waiting for never come.

First Divergence

The requester's data launch was gated on the request being sent, not on the DBID being received. From that point the beats raced ahead of the grant, and the home — which allocates the buffer only when it issues the DBID — had nowhere to put them.

Root Cause

This is AXI intuition applied to CHI. In AXI, write data (W) may be issued with or right after the address (AW); there is no per-transaction grant. CHI inverts this: the home must allocate a buffer and return its DBID before the requester sends data, because CHI write data is filed by DBID, into a home-owned buffer — not streamed behind an address. No DBID means no buffer and no correlation, so the beats have no destination.

Fix

Gate the write data on the DBID grant: send data only after capturing the DBID, and tag every beat with it — exactly the SEND_DATA-after-WAIT_DBID ordering the FSM enforces. Grant-before-data is not a performance nicety; it is how the home knows the data is coming and where to store it. Consume the AXI reflex to push W behind AW — CHI writes wait for the DBID.

15. Common Mistakes

  • Sending data before the DBID. Assumption: data follows the request (AXI habit). Bug: data lost (the DebugLab). Prevention: wait for the DBID grant.
  • Tagging write data with TxnID. Assumption: TxnID correlates everything. Bug: the home cannot place the data. Prevention: tag write data with the DBID.
  • Choosing the wrong write opcode. Assumption: any write works. Bug: wrong coherence effect (e.g. keeping a copy you meant to evict). Prevention: WriteBack to evict dirty, WriteClean to flush-and-keep, WriteNoSnp for non-coherent.
  • Forgetting byte-enables on partial writes. Assumption: writes are always full-line. Bug: corrupting bytes you did not mean to write. Prevention: drive byte-enables on WriteBackPtl / WriteUniquePtl.
  • Assuming the write completes at the request. Assumption: fire-and-forget. Bug: reusing the tracker while data is outstanding. Prevention: hold the transaction until the data is sent (and Comp seen).
  • Ignoring the separate DBID buffer pool. Assumption: unlimited outstanding writes. Bug: stalls when the home's buffers are exhausted. Prevention: respect DBID availability as flow control.

16. Engineering Checklist

  • Issue the write with the opcode that matches intent (WriteBack / WriteClean / WriteUnique / WriteNoSnp).
  • Allocate a tracker; send the REQ without data.
  • Wait for the DBID grant (DBIDResp / CompDBIDResp) before driving data.
  • Tag every write-data beat with the DBID — never the TxnID.
  • Drive byte-enables for partial writes.
  • Hold the transaction open until data is sent and Comp is seen; then retire.

17. Key Takeaways

  • A write pushes a line from the requester to the home; its four beats are REQ → DBID grant → WriteData → Comp.
  • Grant-before-data is the defining rule — the home must return a DBID before the requester sends data.
  • Write data is filed by DBID into a home-owned buffer, so every beat is tagged with the DBID, not the TxnID.
  • The opcode sets intent — WriteBack (evict dirty), WriteClean (flush-and-keep), WriteUnique, WriteNoSnp — but the handshake is identical.
  • CHI inverts AXI: there is no sending write data behind the address; you wait for the DBID.
  • Wait for the DBID, tag data with it, drive byte-enables for partials; the model here is representative.

18. Quick Revision

The write transaction. A write pushes a line from the requester to the home, in four beats: the requester sends a REQ with a write opcode (WriteBackFull to evict a dirty line, WriteCleanFull to flush-and-keep, WriteUnique, WriteNoSnp) and no data; the Home Node allocates a data buffer and returns a DBID on RSP (DBIDResp / CompDBIDResp); only then does the requester drive the WriteData beats on DAT, each tagged with the DBID and carrying byte-enables; and the home writes the line to memory and signals Comp. The defining rule is grant-before-data — data waits for the DBID, because the home owns the buffers and files incoming data by DBID, not TxnID. This inverts the read (unsolicited CompData) and inverts AXI (where write data follows the address with no per-transaction grant). Wait for the DBID, tag the data, drive byte-enables. Representative model; 7.3 does the atomic.

Coming Next

Chapter 7.3 — The Atomic Transaction. Reads pull, writes push — atomics do both, indivisibly. Chapter 7.3 walks CHI's atomic transactions — Compare-and-Swap, AtomicLoad, AtomicStore — where a single transaction reads a location, computes on it, and writes it back with no other agent able to intervene in between. It builds on the read's completion and the write's DBID grant, showing how CHI fuses them into one atomic operation and where the home performs the compute.