A memory controller is returning a 16-beat burst for one requester when, halfway through, the next beat it needs is not ready (its memory bank is busy). Another requester's data is ready now. If a burst must go out in one unbroken run, the read-data channel sits idle until the bank recovers. If beats from different bursts may be mixed, the channel stays busy.
May the beats of different transactions share a channel in mixed order, and what does that cost?
The obvious rule is that a burst, once started, runs to the end: beats 0 to 15 back to back. It keeps the receiver simple: everything between the first beat and the last belongs to one transaction. But a sender that hits a delay mid-burst must idle the channel, even though it has other data ready. Unbroken bursts trade channel utilisation for receiver simplicity.
Interleaving lets the sender switch between transactions beat by beat. Each beat carries its transaction's ID, and each transaction's last beat is marked. The receiver keeps per-ID state (how many beats it has, where they go) and reassembles each transaction independently.
Unbroken bursts | Interleaved beats | |
|---|---|---|
Channel use when one burst stalls | Idle | Filled with another transaction's beats |
Receiver state | One transaction at a time | One beat counter and destination per ID in progress |
Each beat carries | Data (and a last marker) | Data, ID, and a last marker |
Within one transaction | In order | In order (only the mixing between transactions changes) |
A target returns two 3-beat reads, ID 0 (data 0x01, 0x02, 0x03) and ID 1 (0x11, 0x12, 0x13), mixing their beats. The receiver keeps a beat counter per ID and checks each read arrives complete and in order.
// Read data from two transactions interleaved beat by beat. A target returns a
// 3-beat read for ID 0 (data 0x01, 0x02, 0x03) and a 3-beat read for ID 1 (data
// 0x11, 0x12, 0x13), alternating between them. Every beat carries its ID, and
// `last` marks each transaction's final beat. The receiver keeps one beat counter
// per ID and checks each transaction arrives complete and in order.
module tb;
logic clk = 0;
always #5 clk = ~clk;
initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
int cyc = 0;
logic vld, last, id;
logic [7:0] data;
always_comb begin
vld = 1; id = 0; data = 0; last = 0;
case (cyc)
1: begin id = 0; data = 8'h01; end
2: begin id = 1; data = 8'h11; end
3: begin id = 0; data = 8'h02; end
4: begin id = 1; data = 8'h12; end
5: begin id = 1; data = 8'h13; last = 1; end
6: begin id = 0; data = 8'h03; last = 1; end
default: vld = 0;
endcase
end
int beats [0:1];
initial begin beats[0] = 0; beats[1] = 0; end
int errors = 0, finished = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
if (vld) begin
if (data != {3'b0, id, 4'(beats[id] + 1)}) errors++; // beat n of ID k is 0xk(n+1)
if (last != (beats[id] == 2)) errors++;
beats[id] <= last ? 0 : beats[id] + 1;
if (last) finished++;
end
end
initial begin
#120;
if (errors == 0 && finished == 2) $display("PASS: two interleaved reads reassembled by ID, each complete and in order");
else $display("FAIL: errors %0d finished %0d", errors, finished);
$finish;
end
endmoduleThe receiver's whole extra cost is the per-ID counter, beats[id]: it knows which beat of which transaction each word is.
How to read it, slot by slot:
Slots 0 to 3: the beats alternate between the two IDs (id 0, 1, 0, 1), carrying 0x01, 0x11, 0x02, 0x12.
Slot 4: ID 1 sends its third beat, 0x13, with last high: ID 1 is complete, though ID 0 is not.
Slot 5: ID 0's last beat, 0x03, arrives with last. Each read's beats arrived in order; only the mixing between them changed.
For read data, the target sends the beats and the requester must reassemble them; requesters that issue several reads already track them by ID, so interleaving costs them little. For write data, the roles flip: the requester sends the beats and the target must reassemble them, and every target, including the simplest memory, would need per-ID state for every write it might receive interleaved. Few requesters gained from interleaving writes, and every target paid. AXI4 removed write data interleaving: write data must arrive in the order of the write addresses, which also made the write-data ID signal unnecessary.
Protocol | Interleaving |
|---|---|
AXI4 | Read data of transactions with different IDs may be interleaved beat by beat. Write data may not: it arrives in write-address order (AXI4 removed write interleaving and the WID signal). |
AXI3 | Allowed write data interleaving, with a WID on each beat. |
PCI Express | Each completion is a whole packet; different completions may be mixed in any order, but a packet is never split across others. |
A requester (illustrative) assumed read bursts arrive unbroken and used a single beat counter for all reads. It worked against every memory in its test bench, none of which interleaved. Connected to a real interconnect, which interleaved read data from two memories, it delivered beats to the wrong buffers. Any requester that issues reads with different IDs must keep per-ID state, because the protocol allows the target to interleave.
Per ID in progress: a beat count (or address) and where the data goes, until each transaction's last beat.
ID 1, in slot 4: its third beat arrives with last high.
Interleaving mixes beats of different transactions so a channel need not idle when one stalls.
Each beat carries its ID and the receiver keeps per-transaction state; order within a transaction is unchanged.
AXI4 allows read-data interleaving but removed write-data interleaving, sparing every target that state.