Skip to content

AMBA CHI · Module 18 · CHI Debugging

Data Corruption

A missing snoop delivers stale-but-intact data; data corruption delivers mangled data. The symptom is a read matching neither the last write nor any known-stale value — garbage, because the bytes changed between source and reader. Unlike a destroyed value (lost ownership) or a stale one (missing snoop), corruption is a value mangled in transit, so the diagnosis follows the data source to sink; the stage where it first diverges from the source is the bug. The signature is that divergence — a beat whose value at the sink differs from the source. The root cause is a transit-stage failure: beats reassembled by arrival not DataID, a misroute, stripped poison, or mishandled byte-enables. Representative model, not the specification.

Advanced16 min readAMBA CHIDebuggingData CorruptionBeat ReassemblyPoison

Module 18 · Chapter 18.4 · CHI Debugging

Project thread — 18.3 debugged missing snoops. 18.4 debugs data corruption; 18.5 debugs directory corruption.

1. Learning Outcomes

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

  • Distinguish data corruption (mangled) from lost ownership (destroyed) and stale data (intact-but-old).
  • Recognize the symptom — a value matching neither the last write nor a known-stale value.
  • Read the waveform signature — a beat whose value at the sink differs from the source.
  • Diagnose by following the data source-to-sink to find the diverging stage.
  • Classify the root cause — reassembly, misroute, stripped poison, or byte-enables.
  • Implement a representative data-divergence detector in SystemVerilog, Verilog-2001, and VHDL.

2. Why Should I Learn This?

Data corruption is the failure where the delivered bytes are wrong — not old, not missing, but mangled. It is distinct from the previous families in a way that guides the debug: lost ownership destroys the value (Chapter 18.1), a missing snoop serves an intact-but-stale value (Chapter 18.3), but data corruption delivers a value that was changed in transit. The symptom is a read matching neither the current write nor any prior value — a tell-tale that the bytes were transformed, not merely aged.

Because the corruption happens somewhere between the source and the reader, the diagnosis is a spatial trace: follow the data from where it originated (memory or a cache) to where it was consumed, comparing the value at each hop, and the first stage where the value diverges from the source is the bug. This localizes the corruption to a specific transit stage — beat reassembly, sink routing, poison handling, or byte-enables — each with a known fix. Learning to follow the data and spot the divergence turns "the read is garbage" into a pinpointed stage. This chapter is that trace.

3. Key Terms

4. Previous Chapter Connection

This chapter debugs the data-transport mechanisms of Module 13. The root causes are ones you built: beat reassembly by DataID (Chapter 13.1), sink routing by opcode (Chapter 13.3), poison propagation (Chapter 13.7), and byte-enables (Chapter 8.5). Data corruption is what happens when one of these transit stages transforms the value it should have carried unchanged.

The debug method is the data-path analog of the ownership trace (Chapter 18.1). There, you followed ownership backward; here, you follow the data forward — from source to sink — comparing the value at each stage. The scoreboard of Chapter 17.3 detects the wrong value (actual ≠ expected); this chapter locates where the value was corrupted. And the distinction from Chapters 18.1/18.3 is the class of value error — destroyed vs stale vs mangled — which the symptom itself reveals and which points the trace at the transport stages rather than the coherence ones. This chapter adds the source-to-sink data trace to the toolkit.

5. Core Concept — follow the data to the diverging stage

Data corruption is a value mangled in transit; the diagnosis is to follow the data from source to sink and find the first stage where the value diverges from the source.

  • The value is mangled, not stale or lost. The delivered bytes match neither the last write nor a known-stale value — they were transformed on the way (scrambled, misrouted, poisoned-but-used, partial).
  • Corruption is spatial. The value was correct at the source and wrong at the sink — so the bug is at some stage on the path between them.
  • Follow the data. Compare the value at each hop: source → reassembly → routing → sink. The first hop where the value differs from the source is the corrupting stage.
  • The stage names the root cause. A divergence at reassembly means DataID handling (Chapter 13.1); at routing, sink selection (Chapter 13.3); at poison, propagation (Chapter 13.7); at byte-enables, partial writes (Chapter 8.5).

The synthesis:

Data corruption delivers a value that was mangled in transit — matching neither the last write nor a known-stale value. Because the value was right at the source and wrong at the sink, the diagnosis is to follow the data hop by hop and find the first stage where it diverges from the source. That stage names the root cause: beat reassembly (13.1), sink routing (13.3), poison (13.7), or byte-enables (8.5).

6. Engineering Mental Model — a relay race with a garbled baton message

Think of a relay where a message (the data) is passed hand to hand, and it arrives garbled at the finish.

  • The message started correct at the first runner (the source) and is wrong at the last (the sink). Somewhere along the chain, a runner altered it.
  • To find the culprit, you do not interrogate the finish line — you walk the chain, asking each runner what they received and what they passed on.
  • The first runner whose received message was correct but whose passed-on message was wrong is the one who garbled it — you have found the exact hand-off that corrupted the message.
  • The kind of garbling tells you how: reordered words (reassembly by arrival), delivered to the wrong person (misroute), a "damaged" stamp ignored (stripped poison), or only half the words written (byte-enables).

Walking the chain is following the data source-to-sink; the runner who received-right but passed-wrong is the diverging stage; the kind of garbling names the fix. You localize by comparing received vs passed at each hop, not by staring at the garbled finish.

7. Engineering Diagram — the corruption in transit

A data-corruption failure in transit. The source supplies the correct value; an intermediate stage, here beat reassembly by arrival order instead of DataID, mangles it; the sink receives and consumes the wrong value. The value was correct at the source and wrong at the sink, so the bug is the stage in between where it diverged.Source (correct data)Reassembly stageSink (requester)beats: correct valuereassemble byARRIVAL (bug)scrambled valuedeliveredconsumes wrong bytes
Figure 1 — a data-corruption failure in transit. The source supplies the correct value; an intermediate stage — here beat reassembly by arrival order instead of DataID — mangles it; the sink receives and consumes the wrong value. The value was correct at the source and wrong at the sink, so the bug is the stage in between where it diverged.

The value was correct at the source and scrambled at the sink — the reassembly stage transformed it (placing beats by arrival, not DataID, Chapter 13.1). The debugger localizes by comparing the value into and out of each stage; the reassembly stage is where in-correct became out-wrong. That is the bug.

8. Waveform Signature

Data corruption: value diverges between source and sink

6 cycles
Data corruption: value diverges between source and sinkcorrect at sourcecorrect at sourcecorrupted at sinkcorrupted at sinksource value correct (0xAA55)source value correct(0xAA55)sink value scrambled (0x55AA)sink value scrambled(0x55AA)CLKsrc.data0AA55AA55AA55AA55AA55sink.data00055AA55AA55AAdiverge000111t0t1t2t3t4t5
Figure 2 — the waveform signature of data corruption. The data value at the source is correct (0xAA55), but the value delivered at the sink is scrambled (0x55AA) — the beats were reassembled in the wrong order. The diverge signal marks the stage where the value first differed from the source. A value that is right at the source and wrong at the sink is corruption in transit.

The signature is src.datasink.data — the value was 0xAA55 at the source but 0x55AA at the sink (halves swapped: a reassembly-by-arrival scramble). The diverge signal marks where they first differed. A value right at the source, wrong at the sink is corruption in transit, and the kind of difference (swapped halves) points at the reassembly stage.

9. Diagnosis Path

The methodical source-to-sink trace.

StepActionWhat it finds
1. Symptomvalue matches neither write nor staleconfirm mangled, not stale/lost
2. Sourcefind where the data originated (memory / cache)the correct value
3. Trace forwardcompare the value at each hop (reassembly, routing, sink)the per-stage values
4. First divergencethe first hop where value ≠ sourcethe corrupting stage
5. Kind of errorhow it differs (scrambled, wrong bytes, poisoned)the mechanism
6. Root causemap stage + kind to the fix13.1 / 13.3 / 13.7 / 8.5

The rule to carry: the symptom's kind tells you the family; the stage of divergence tells you the fix. First, distinguish mangled from stale (18.3) and destroyed (18.1) — a value matching no known value means corruption. Then follow the data to find where it changed, and the kind of change (halves swapped, wrong region, known-bad-used) names the stage and its fix. This two-part read — kind then stage — is what makes data-corruption debug systematic rather than a hunt through garbage.

10. Tracing a Data-Corruption Bug — a worked trace

A read returns 0x55AA for a line last written 0xAA55; no prior value was 0x55AA.

  1. Symptom: mangled. The read value 0x55AA matches neither the write (0xAA55) nor any known-stale value. It looks like 0xAA55 with its halves swapped — a scramble, so corruption, not stale or lost.
  2. Find the source. The data was sourced from memory as 0xAA55correct at the source. So the corruption is downstream.
  3. Trace forward. The line was two beats (0xAA high, 0x55 low). Into the reassembly stage: beats correct. Out of reassembly: 0x55AA — halves swapped. The value diverged at reassembly.
  4. First divergence: reassembly. In was 0xAA55 (correct), out was 0x55AA (wrong) — the reassembly stage is the corrupting stage.
  5. Kind: swapped halves. The beats were placed by arrival order, not DataID — so a reordered delivery swapped the high and low halves (Chapter 13.1).
  6. Root cause and fix. Reassemble each beat at its DataID position, not arrival order (Chapter 13.1). Re-run: source and sink values match.

The garbage read localized to the reassembly stage via the source-to-sink trace and the swapped-halves signature. The DebugLab formalizes this.

11. Detector View — a data-divergence detector

Compare the delivered value against the source value per beat; flag where they diverge. Representative.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative data-divergence detector (educational).
// Data is CORRECT at the source; corruption is when the delivered (sink) value differs.
// Compare per beat (indexed by DataID): if delivered != source for a beat, that beat
// diverged -> corruption at the stage between. Localizes the corrupting beat/stage.
module chi_data_diverge #(parameter DW = 64, parameter NBEAT = 4) (
  input  logic                       beat_valid,
  input  logic [$clog2(NBEAT)-1:0]   beat_id,        // which beat (DataID position)
  input  logic [DW-1:0]              src_data,        // value at the SOURCE for this beat
  input  logic [DW-1:0]              sink_data,       // value DELIVERED at the sink
  output logic                       diverge,          // this beat's value was corrupted
  output logic [$clog2(NBEAT)-1:0]   diverge_beat      // which beat diverged
);
  // A beat diverges when its delivered value differs from its source value.
  assign diverge      = beat_valid && (sink_data != src_data);
  assign diverge_beat = beat_id;
endmodule

The same behavior in Verilog-2001:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Representative data-divergence detector (Verilog-2001).
module chi_data_diverge #(parameter DW = 64, parameter IDW = 2) (
  input              beat_valid,
  input  [IDW-1:0]   beat_id,
  input  [DW-1:0]    src_data, sink_data,
  output             diverge,
  output [IDW-1:0]   diverge_beat
);
  assign diverge      = beat_valid & (sink_data != src_data);
  assign diverge_beat = beat_id;
endmodule

And in VHDL:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- Representative data-divergence detector (VHDL).
library ieee;
use ieee.std_logic_1164.all;
 
entity chi_data_diverge is
  generic ( DW : integer := 64; IDW : integer := 2 );
  port (
    beat_valid   : in  std_logic;
    beat_id      : in  std_logic_vector(IDW-1 downto 0);
    src_data     : in  std_logic_vector(DW-1 downto 0);
    sink_data    : in  std_logic_vector(DW-1 downto 0);
    diverge      : out std_logic;
    diverge_beat : out std_logic_vector(IDW-1 downto 0)
  );
end entity;
 
architecture rtl of chi_data_diverge is
begin
  diverge      <= '1' when (beat_valid = '1' and sink_data /= src_data) else '0';
  diverge_beat <= beat_id;
end architecture;

All three flag diverge when a beat's delivered value differs from its source value, naming the diverging beat. Placed at the sink with the source value threaded through, it fires on the corrupted beat — and the kind of difference (which beat, how it differs) points at the stage. The DebugLab's corruption is a swapped-halves reassembly.

12. Assertion View — delivered data equals source data

The properties formalize the invariant: data is carried unchanged from source to sink.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind to chi_data_diverge (with the source value threaded to the sink).
// 1. A delivered beat's value equals its source value (no corruption in transit).
property p_data_unchanged;
  @(posedge clk) disable iff (!rst_n)
    beat_valid |-> (sink_data == src_data);
endproperty
 
// 2. Each beat is delivered at its DataID position (no reassembly scramble).
//    (bind to reassembly: line[beat_id] == the beat written for beat_id)
 
// 3. A diverge flag names exactly the beat whose value differs.
property p_diverge_names_beat;
  @(posedge clk) disable iff (!rst_n)
    diverge |-> (sink_data != src_data);
endproperty

The system point, beyond the checks:

Data corruption is the failure family where the symptom itself carries diagnostic information — the kind of wrongness is a fingerprint of the stage. A value with its halves swapped is a reassembly-by-arrival scramble (13.1); a value that is some other line's data is a sink misroute (13.3); a value that is byte-granular wrong is a byte-enable bug (8.5); a value used despite being known-bad is stripped poison (13.7). So an experienced debugger reads the corruption pattern and jumps most of the way to the root cause before tracing — the trace then confirms the stage. This is why classifying the value error precisely (destroyed vs stale vs mangled, and then the kind of mangling) is the highest-leverage first step: it collapses the search space from "somewhere in the data path" to "the reassembly stage" in one read. The general lesson, recurring across the module, is that failure families have signatures, and learning the signatures — not just the mechanisms — is what makes debugging fast: the mechanism (Module 13) tells you how the bug works, but the signature tells you, from the symptom alone, which mechanism to suspect.

  • What it proves: each delivered beat equals its source value (no in-transit corruption).
  • What it does not prove: the source value was correct — that is upstream (memory/cache).
  • Bug signature: diverge asserted — the named beat's value changed between source and sink.

13. Testbench — a scrambled beat must be flagged

Feeds correct source data and a scrambled sink value and checks the detector names the diverging beat.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_chi_data_diverge;
  localparam DW = 16, NBEAT = 4, IDW = 2;
  logic beat_valid;
  logic [IDW-1:0] beat_id, diverge_beat;
  logic [DW-1:0] src_data, sink_data;
  logic diverge;
  int errors = 0;
 
  chi_data_diverge #(.DW(DW), .IDW(IDW)) dut (.*);
 
  initial begin
    // Correct: delivered value equals source value.
    beat_valid = 1; beat_id = 2'd0; src_data = 16'hAA55; sink_data = 16'hAA55; #1;
    if (diverge) begin errors++; $display("FAIL false divergence on matching data"); end
    else $display("PASS beat 0 matches source -> no divergence");
 
    // CORRUPTION: sink value is scrambled (halves swapped) vs source -> diverge.
    sink_data = 16'h55AA; #1;
    if (!diverge) begin errors++; $display("FAIL scrambled beat NOT detected"); end
    else $display("PASS divergence detected at beat %0d: src=%04h sink=%04h", diverge_beat, src_data, sink_data);
 
    // Another correct beat.
    beat_id = 2'd1; src_data = 16'h1234; sink_data = 16'h1234; #1;
    if (diverge) begin errors++; $display("FAIL false divergence on beat 1"); end
    else $display("PASS beat 1 matches source");
 
    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 beat 0 matches source -> no divergence
PASS divergence detected at beat 0: src=aa55 sink=55aa
PASS beat 1 matches source
ALL TESTS PASSED

14. DebugLab — a scrambled line traced to beat reassembly

1

A scrambled line traced to beat reassembly

READ VALUE MATCHES NO KNOWN VALUE (HALVES SWAPPED) -> DATA DIVERGES AT REASSEMBLY -> BEATS PLACED BY ARRIVAL NOT DATAID
Symptom

A read returns garbage — a value matching neither the last write nor any prior value the line held. Inspection shows it is the written value with its halves swapped. It correlates with multi-beat transfers under load (out-of-order beat delivery); single-beat and lightly-loaded transfers are fine.

Evidence

Following the data forward finds it diverges at reassembly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
symptom: read X = 0x55AA; last write X = 0xAA55; no prior value was 0x55AA
  -> value = written value with halves swapped -> MANGLED (corruption, not stale/lost)
step 2 - source: memory supplied 0xAA55 (correct at source)
step 3 - trace: into reassembly = 0xAA55 (correct); OUT of reassembly = 0x55AA (WRONG)
  -> first divergence = REASSEMBLY stage
step 5 - kind: high/low halves swapped -> beats placed by ARRIVAL, not DataID
  -> out-of-order beat delivery swapped the two halves (Chapter 13.1)
correct: place each beat at its DataID position -> 0xAA55 regardless of arrival

The value was right at the source and swapped leaving reassembly — the corrupting stage.

First Divergence

The value entering the reassembly stage was correct (0xAA55), and the value leaving it was scrambled (0x55AA) — the reassembly stage transformed a correct value into a wrong one.

Root Cause

Data corruption is a value mangled in transit, so it is diagnosed by following the data source-to-sink to the first diverging stage, and the kind of mangling names the stage's bug — here, halves swapped means beats reassembled by arrival order, not DataID. The value was correct at the source and wrong at the sink, so the bug is a transit stage; comparing the value into and out of each stage localizes it to reassembly; and the swapped-halves signature identifies the mechanism as beat placement by arrival rather than by DataID (Chapter 13.1). The symptom's kind is a fingerprint: swapped halves → reassembly, wrong region → misroute (13.3), used-known-bad → stripped poison (13.7), byte-granular → byte-enables (8.5). Reading the fingerprint jumps most of the way to the fix before the trace confirms it.

Fix

Reassemble each beat at the position its DataID specifies, not in arrival order — as Chapter 13.1 requires — so the line is reconstructed correctly regardless of beat arrival order. Confirm with the detector that the delivered value equals the source value per beat. Match the fix to the diverging stage: reassembly (13.1), routing (13.3), poison (13.7), or byte-enables (8.5).

15. Common Mistakes

  • Not classifying the value error. Assumption: all wrong data is the same. Bug: wrong family. Prevention: mangled vs stale vs destroyed.
  • Debugging the sink. Assumption: the bug is where it is consumed. Bug: it is a transit stage. Prevention: follow the data from the source.
  • Ignoring the corruption pattern. Assumption: garbage is random. Bug: the pattern names the stage. Prevention: read the fingerprint.
  • Assuming the source is correct without checking. Assumption: corruption is downstream. Bug: a bad source. Prevention: verify the source value.
  • Overlooking poison. Assumption: data is valid. Bug: known-bad used (13.7). Prevention: check the poison bit.
  • Confusing with stale data. Assumption: same as missing snoop. Bug: mangled vs old. Prevention: a value matching no prior value is corruption.

16. Engineering Checklist

  • Classify the value error — mangled (corruption) vs stale (18.3) vs destroyed (18.1).
  • Find the source and confirm its value is correct.
  • Follow the data forward, comparing the value at each hop.
  • Find the first stage where the value diverges from the source.
  • Read the kind of corruption to identify the mechanism.
  • Map stage + kind to the fix — reassembly (13.1), routing (13.3), poison (13.7), byte-enables (8.5).

17. Key Takeaways

  • Data corruption delivers a value mangled in transit — matching no known value.
  • It differs from destroyed (18.1) and stale (18.3) — the symptom reveals which.
  • The signature is a value right at the source, wrong at the sink.
  • Follow the data to the first diverging stage — that stage is the bug.
  • The kind of corruption names the mechanism (reassembly, misroute, poison, byte-enables).
  • Read the fingerprint, then trace; the model here is representative.

18. Quick Revision

Data corruption. Data corruption delivers mangled bytes — a read value matching neither the last write nor any known-stale value, because the bytes were transformed somewhere between their source and the reader. It is distinct from lost ownership (Chapter 18.1, value destroyed) and a missing snoop (Chapter 18.3, value stale but intact), and the symptom itself reveals which. Because the value was correct at the source and wrong at the sink, the diagnosis is spatial: follow the data hop by hop — source → reassembly → routing → sink — comparing the value at each stage, and the first stage where it diverges from the source is the corrupting one. The kind of corruption is a fingerprint of the stage: halves swapped means beats reassembled by arrival order not DataID (Chapter 13.1); the wrong region means a sink misroute (Chapter 13.3); known-bad data used means stripped poison (Chapter 13.7); byte-granular wrong means byte-enable mishandling (Chapter 8.5). So an experienced debugger reads the pattern and jumps most of the way to the root cause before the trace confirms the stage. The detector compares delivered value against source value per beat, firing on the corrupted beat. Classify the value error first (kind), then trace to the stage — the two-part read makes data-corruption debug systematic. Representative model; 18.5 debugs directory corruption.

Coming Next

Chapter 18.5 — Directory Corruption. Data corruption mangles a value; directory corruption mangles the coherence bookkeeping. Chapter 18.5 covers directory corruption — the directory's recorded state disagreeing with the actual caches, the signature of a directory sharer set that does not match the caches holding the line, and the diagnosis of comparing the directory against an aggregate of the real cache states to find the divergent entry.