AMBA AXI · Module 8
Same-ID Ordering Rules
AXI's same-ID ordering guarantee — transactions sharing an AxID complete in issue order — what it does and does not cover (different IDs, read-vs-write), and why same-ID across multiple slaves can serialize and hurt performance.
Chapter 8.2 leaned on a guarantee repeatedly: same ID means in order. Now we state it precisely, because it's the rule that makes outstanding transactions predictable — and the boundaries of what it does and doesn't promise are exactly where ordering bugs and performance traps live. The rule is narrow on purpose: it orders transactions sharing an ID, and nothing else. It says nothing about different IDs (they may reorder — Chapter 8.4) and nothing about reads versus writes (a separate concern). And it has a sharp performance edge: same-ID transactions spread across multiple slaves can force the interconnect to serialize. This chapter pins down the guarantee and its limits.
1. The Rule
The same-ID ordering rule, stated for each direction:
- Reads: read transactions issued with the same
ARIDreturn their data in the order the addresses were issued. Read data for an earlier same-ID read arrives before a later same-ID read's data. - Writes: write transactions issued with the same
AWIDcomplete — theirBresponses return — in the order the addresses were issued.
That's the whole guarantee: for a given ID, completion order equals issue order. A subordinate or interconnect may have the later transaction's result ready first, but it must hold it back and deliver the earlier same-ID one first. This is what lets a manager that needs ordering simply reuse one ID and trust the sequence.
2. A Same-ID Read Stream on the Wire
Three same-ID reads return their data strictly in issue order, regardless of which the subordinate finished first:
same-id-order — three ARID=3 reads return in issue order
7 cycles3. What the Rule Does Not Cover
The guarantee is deliberately narrow. Two things it explicitly does not promise:
- Different IDs are not ordered. Transactions with different
AxIDs have no ordering relationship — the system may complete them in any order (Chapter 8.4). The rule only orders within one ID. - Reads and writes are not ordered relative to each other.
ARIDandAWIDlive in separate ID spaces; a read and a write — even with the same numeric ID value — have no ordering guarantee between them. If a manager needs a write to land before a dependent read (or vice versa), it must enforce that itself, typically by waiting for the first transaction's response before issuing the second. AXI's IDs order reads among reads and writes among writes, never reads against writes.
Missing either limit is a classic bug source: assuming different-ID transactions stay ordered, or assuming a same-valued read and write are ordered. Neither holds.
4. Same ID Across Multiple Slaves — The Serialization Trap
Here's the performance edge. The interconnect must preserve same-ID ordering even when same-ID transactions target different slaves. But different slaves have different latencies and respond independently — so to guarantee that same-ID response 0 (from a slow slave) precedes same-ID response 1 (from a fast slave), the interconnect must hold back the fast slave's response until the slow one is done. Effectively, same-ID traffic to multiple slaves serializes at the rate of the slowest, and the interconnect needs buffering/blocking to enforce it.
The consequence: reusing one ID across transactions that hit different slaves can badly hurt throughput (head-of-line blocking across slaves). Many systems therefore follow a guideline — keep one ID to one slave, or use distinct IDs for transactions that can target different slaves — so the ordering constraint never forces cross-slave serialization. This is the practical reason ID assignment must consider not just ordering need but where transactions go.
5. The Write-Data Ordering Rule AXI4 Added
There is a third ordering constraint that belongs on this page and is routinely missed, because it is not an ID rule at all — it is a rule about the W channel that AXI4 introduced by deleting something.
AXI3 had a WID signal. Write data beats carried their own ID, which meant a manager could interleave the data of two different write transactions on the W channel and the subordinate would sort them out by WID.
AXI4 removed WID entirely and removed write-data interleaving with it. The consequence is a hard ordering requirement:
A manager must send write data in the same order as it issued the write addresses. Not per-ID — globally, across every outstanding write. The Nth
AWhandshake owns the Nth write-data burst on the W channel.
The read side has the symmetric story — AXI4 also dropped read-data interleaving, so all beats of one read burst return contiguously. That is developed in read data interleaving; the executable check for it appears in §6 below, because "beats of one burst arrive contiguously" is an ordering property this page can assert directly.
6. The Rules as an Executable Checker
Section 7 below describes a per-ID scoreboard in prose. Here it is as code — because "completion order equals issue order" is a claim a checker can actually enforce, and a page that only describes the scoreboard leaves the reader to invent the hard part.
The design is a per-ID FIFO. Every accepted address pushes; every accepted completion pops. Ordering violations then surface two ways: a completion for an ID with nothing outstanding, and — the real detector — a completion whose payload does not correspond to the transaction at the head of that ID's queue.
// ─────────────────────────────────────────────────────────────────────────────
// Same-ID ordering checker (AMBA AXI4 §A5.3).
//
// Bind to a manager, subordinate, or interconnect port:
// bind axi_slave axi_same_id_order_checker #(.ID_W(4)) u_ord (.*);
//
// WHAT MAKES THE ORDER CHECK REAL: a FIFO pop alone cannot detect reordering —
// pop the head and any arriving response "looks" in order. Detection needs the
// payload to identify WHICH transaction completed. This checker uses the
// address-as-data convention (the memory model returns rdata == araddr), which
// costs nothing in a testbench and makes a swapped pair of same-ID reads show
// up immediately as a data mismatch. Drive it against a real memory model and
// set CHECK_DATA=0; you keep the outstanding checks and lose only the
// reordering detector, which the scoreboard then owns instead.
// ─────────────────────────────────────────────────────────────────────────────
module axi_same_id_order_checker #(
parameter int ID_W = 4,
parameter int ADDR_W = 32,
parameter int DATA_W = 32,
parameter bit CHECK_DATA = 1 // 0 when a real memory model owns data checking
) (
input logic aclk,
input logic aresetn,
// Read address / read data
input logic arvalid, arready,
input logic [ID_W-1:0] arid,
input logic [ADDR_W-1:0] araddr,
input logic rvalid, rready, rlast,
input logic [ID_W-1:0] rid,
input logic [DATA_W-1:0] rdata,
// Write address / write response
input logic awvalid, awready,
input logic [ID_W-1:0] awid,
input logic [ADDR_W-1:0] awaddr,
input logic bvalid, bready,
input logic [ID_W-1:0] bid
);
localparam int N_ID = 1 << ID_W;
typedef logic [ADDR_W-1:0] addr_t;
// One issue-order queue per ID. This IS the same-ID rule: the guarantee is
// that each queue drains front-first, so the head is the only transaction
// allowed to complete next for that ID.
addr_t rd_pending [N_ID][$];
addr_t wr_pending [N_ID][$];
int unsigned rd_checked, wr_checked, rd_errors, wr_errors;
// ── Issue side ────────────────────────────────────────────────────────────
always_ff @(posedge aclk) begin
if (!aresetn) begin
for (int i = 0; i < N_ID; i++) begin
rd_pending[i].delete();
wr_pending[i].delete();
end
rd_checked = 0; wr_checked = 0; rd_errors = 0; wr_errors = 0;
end else begin
if (arvalid && arready) rd_pending[arid].push_back(araddr);
if (awvalid && awready) wr_pending[awid].push_back(awaddr);
// ── Read completion ────────────────────────────────────────────────
if (rvalid && rready && rlast) begin
if (rd_pending[rid].size() == 0) begin
rd_errors++;
$error("[%0t] RLAST for ARID=%0h with no outstanding read", $time, rid);
end else begin
addr_t expect_addr = rd_pending[rid].pop_front();
rd_checked++;
// The reordering detector. Under address-as-data, the head of this
// ID's queue predicts the payload exactly; a swap shows up here.
if (CHECK_DATA && rdata !== DATA_W'(expect_addr)) begin
rd_errors++;
$error("[%0t] SAME-ID ORDER VIOLATION, ARID=%0h: expected data for addr %0h, got %0h",
$time, rid, expect_addr, rdata);
end
end
end
// ── Write completion ───────────────────────────────────────────────
if (bvalid && bready) begin
if (wr_pending[bid].size() == 0) begin
wr_errors++;
$error("[%0t] B response for AWID=%0h with no outstanding write", $time, bid);
end else begin
void'(wr_pending[bid].pop_front());
wr_checked++;
end
end
end
end
// ── AXI4 has no read-data interleaving: once a burst starts, every beat
// until RLAST carries the same RID. This is a pure-protocol ordering
// property, checkable with no model at all.
logic burst_active;
logic [ID_W-1:0] active_id;
always_ff @(posedge aclk or negedge aresetn) begin
if (!aresetn) burst_active <= 1'b0;
else if (rvalid && rready) begin
if (rlast) burst_active <= 1'b0;
else if (!burst_active) begin
burst_active <= 1'b1;
active_id <= rid;
end
end
end
a_no_read_interleaving:
assert property (@(posedge aclk) disable iff (!aresetn)
(rvalid && rready && burst_active) |-> (rid == active_id))
else $error("[%0t] read data interleaved: RID=%0h arrived mid-burst of RID=%0h",
$time, rid, active_id);
// ── Covers. Every check above is conditional, so a quiet bus passes them
// all while proving nothing. These say the scenarios actually happened.
c_same_id_depth_2: cover property (@(posedge aclk) disable iff (!aresetn)
(arvalid && arready) ##0 (rd_pending[arid].size() >= 1));
c_multi_beat_read: cover property (@(posedge aclk) disable iff (!aresetn)
(rvalid && rready && !rlast));
c_write_outstanding: cover property (@(posedge aclk) disable iff (!aresetn)
(awvalid && awready) ##0 (wr_pending[awid].size() >= 1));
final begin
$display("[same-id checker] reads checked=%0d writes checked=%0d errors=%0d",
rd_checked, wr_checked, rd_errors + wr_errors);
end
endmoduleWhat each check means, and what a failure tells you.
| Check | The claim | A failure tells you |
|---|---|---|
SAME-ID ORDER VIOLATION | The head of an ID's queue is the next transaction that may complete | A subordinate or interconnect let a ready-early same-ID transaction overtake an earlier one — the exact violation §1 forbids |
no outstanding read/write | A completion exists only for an accepted address | A response was manufactured, replayed, or tagged with the wrong ID. Often an interconnect ID-remap bug |
a_no_read_interleaving | All beats of one read burst are contiguous | AXI3-era interleaving behaviour on an AXI4 port |
c_same_id_depth_2 etc. | The scenarios ran at all | If these sit at zero, every check above passed vacuously and the run is not evidence |
The last row is the one to internalise. Each ordering check is an implication over a condition that a lightly-loaded bus never creates. A same-ID checker that never saw two outstanding same-ID transactions has proven nothing, and the cover is the only artifact that distinguishes "ordering held" from "ordering was never tested."
7. Common Misconceptions
8. Debugging Lab — Throughput Halved After an "Unrelated" ID Change
A one-line ID assignment change halves DMA throughput with no functional failure
SAME-ID-CROSS-SLAVE-SERIALIZATIONA DMA engine streaming to two memories drops from ~1.9 GB/s to ~0.95 GB/s. Every test passes. No assertion fires, no scoreboard mismatch, no protocol violation. The only change in the commit was ID assignment: a cleanup that consolidated several AWID/ARID values onto one ID "for simpler tracking."
Expected. Two independent memories, each capable of ~1 GB/s, accessed concurrently → roughly additive throughput.
Actual. Aggregate throughput equals what the slower memory alone can sustain. Waveforms show the DMA issuing addresses at full rate and the fast memory producing responses promptly — but those responses sitting in the interconnect, not reaching the manager.
The signature that separates this from ordinary backpressure: the fast slave's response is complete and the interconnect is not forwarding it. Ordinary backpressure would show the manager withholding RREADY; here RREADY is high and the data still does not arrive.
Three questions localise it:
- Are the stalled transactions sharing an ID? Check
ARID/AWIDon the delayed pair. If they differ, this is not the trap — go look at arbitration. - Do they target different slaves? Same ID to one slave is ordinary head-of-line blocking; same ID across different slaves is the serialization case (§4).
- Does the delay track the slow slave's latency? Measure the fast response's held time. If it consistently equals the slow slave's outstanding latency, the interconnect is holding it to preserve issue order — which is the interconnect being correct.
The same-ID rule requires completion in issue order, and the interconnect must honour it even across slaves it does not control the latency of. With both memories' traffic on one ID, a response from the fast memory that is ready first cannot be delivered first — the interconnect must buffer it until the earlier same-ID response returns from the slow memory.
The consolidation "for simpler tracking" imposed a total order on transactions that had no ordering requirement. Nothing is broken: the manager asked for ordering, and it got it, at the cost of the concurrency the two independent memories existed to provide.
Give traffic to different slaves different IDs, so no cross-slave ordering constraint exists:
before: memory A traffic → AWID/ARID = 0
memory B traffic → AWID/ARID = 0 ← one ID spanning two slaves
after: memory A traffic → AWID/ARID = 0
memory B traffic → AWID/ARID = 1 ← ordering only where it is neededThe rule of thumb §4 gives — one ID per slave, or distinct IDs for traffic that can target different slaves — exists precisely to make this unrepresentable. Note what the fix does not do: it does not relax any ordering the design actually depended on, because the two streams were independent to begin with.
Treat ID assignment as an architectural decision, not bookkeeping. Reusing an ID is a request for serialization, and it is free only when the transactions genuinely must be ordered and reach the same slave.
Two practical guards. Add a throughput regression to CI — a functional suite cannot catch this, because nothing functional is wrong; only a performance metric can. And add a cover for same-ID transactions in flight to different slaves: if it fires, either the ordering is intentional and documented, or it is this bug waiting for a latency asymmetry to expose it.
9. Debugging Insight
10. Verification Insight
11. Interview Questions
12. Summary
The same-ID ordering rule is AXI's core ordering guarantee, and it's narrow by design: for a given AxID, completion order equals issue order — same-ARID reads return data in issue order, same-AWID writes return B responses in issue order, with the system holding back any ready-early later transaction to preserve it. The limits are as important as the rule: different IDs are not ordered (they may reorder — 8.4), and reads and writes are not ordered relative to each other (separate ID spaces — the manager must enforce any read/write dependency by waiting on responses). And there's a performance edge: same-ID transactions across multiple slaves force the interconnect to serialize (holding fast responses behind slow ones), so one-ID-per-slave or distinct IDs avoid cross-slave head-of-line blocking.
The discipline this yields: use one ID to order same-direction accesses (accepting in-order, possibly serialized completion), distinct IDs for independent accesses (throughput via reordering), and explicit response-waits for read/write dependencies (which IDs can't express). Bugs are either assumed ordering that wasn't guaranteed (data hazards) or over-imposed ordering that wasn't needed (lost throughput). Next: the flip side — different-ID and out-of-order completion — the reordering this rule deliberately permits, and the hazards it introduces.
13. Where This Is Specified
The same-ID ordering guarantee is normative text in the Arm AMBA AXI Protocol Specification — transaction ordering in §A5 (Transaction identifiers), with the write-response ordering rule in §A5.3 and the ID rules themselves in §A5.1. Arm publishes it on the AMBA AXI documentation page.
Two things are worth reading in the specification rather than taking from any tutorial, because both are commonly reconstructed wrongly from memory. First, that ARID and AWID are separate ID spaces with no ordering between them — the source of the stale-read-after-write bug in §8. Second, the AXI4 change list, where WID and write-data interleaving were removed: that deletion is what turns "send write data in address order" into a protocol requirement (§5), and it is invisible to anyone who learned ordering from AXI3 material.
The SVA used in §6 is IEEE Std 1800 (SystemVerilog) clause 16 — see concurrent assertions for the sampling and implication semantics, and for why every check there is paired with a cover.
Related lessons. ID mechanics are in AxID and transaction IDs; the concurrency this rule constrains is in why outstanding transactions exist; the reordering it permits is different-ID ordering. For the channel-level detail behind §5–§6, see read data interleaving, write ordering and WLAST, and the B response. For the verification build-out, AXI scoreboards and AXI assertions.
14. What Comes Next
You've got the ordering guarantee; next, the reordering it permits:
- 8.4 — Different-ID & Out-of-Order Completion (coming next) — how different-ID transactions reorder, the throughput it buys, and the hazards to manage.
Previous: 8.2 — Transaction IDs. Related: 6.3 — AxID for ID mechanics, and 8.1 — Why Outstanding Transactions Exist for the concurrency this orders. For the broader protocol catalog, see the AMBA family overview doc.