AMBA AXI · Module 7
The 4KB Boundary Rule
AXI's hard rule that no burst may cross a 4 KB address boundary — why it exists (minimum page size and address decode), how to check whether a burst crosses, and how masters split transfers at the boundary.
There is one placement rule in AXI that is absolute and that every master must respect: a burst must not cross a 4 KB address boundary. The entire address range a burst touches has to fall within a single 4 KB-aligned region. This isn't a performance guideline — it's a correctness requirement rooted in how memory is paged and decoded, and violating it produces some of the nastiest, hardest-to-trace bugs in a system. This chapter explains the rule, why 4 KB specifically, how to test whether a burst crosses, and how masters (and interconnects) split a transfer at the boundary to obey it.
1. The Rule
A single AXI burst's address range — from its first byte to its last — must lie entirely within one 4 KB (4096-byte) region aligned to a 4 KB boundary. 4 KB boundaries sit at every multiple of 0x1000. Formally, for a burst starting at Start_Address and covering Total_Bytes:
The burst is legal only if
INT(Start_Address / 0x1000) == INT((Start_Address + Total_Bytes − 1) / 0x1000)— i.e., the first and last bytes are in the same 4 KB region.
If the first and last byte fall in different 4 KB regions, the burst crosses a boundary and is illegal. This applies to the burst's full byte span ((AxLEN+1) × 2^AxSIZE for INCR), so it's really a joint constraint on the start address and the length.
2. Why 4 KB — Pages and Address Decode
The rule exists because 4 KB is the minimum page size in essentially all memory-management units, and therefore the smallest granularity at which the address map can change. Two consequences:
- A burst could otherwise span two slaves. Address decode (which subordinate an address routes to) can differ on either side of any 4 KB boundary, because two adjacent 4 KB pages may map to entirely different physical destinations. A single AXI transaction has one
AxADDRand routes to one subordinate with one response path — it cannot be half at slave A and half at slave B. Confining a burst to one 4 KB region guarantees the whole burst decodes to a single slave. - It bounds the work an interconnect must do. Because no burst crosses a 4 KB boundary, an interconnect's decode/routing logic only ever has to send a burst to one place — it never has to split or fork a transaction mid-flight to two destinations. The rule pushes that responsibility to the side that shapes the burst.
So 4 KB is not arbitrary: it's the architectural unit at which mapping changes, and the rule keeps every burst inside one mappable unit.
3. Checking and Splitting at the Boundary
To check a burst: compute its last byte Start_Address + Total_Bytes − 1 and see if it lands in the same 4 KB region as the start. If it doesn't, the transfer must be split at the 4 KB boundary into two (or more) bursts, each within one region.
Worked example. Suppose a master wants to transfer 16 bytes starting at 0xFF8, 4 bytes/beat. The range is 0xFF8 … 0x1007 — it crosses 0x1000. Split at the boundary:
- Burst 1: start
0xFF8, covering0xFF8 … 0xFFF= 8 bytes (2 beats) — fills up to the boundary. - Burst 2: start
0x1000, covering0x1000 … 0x1007= 8 bytes (2 beats) — the remainder, in the next region.
Each sub-burst is legal (within one region), and together they cover the same 16 bytes. The split point is always the 4 KB boundary; the bytes before it go in one burst, the bytes from it onward in the next.
4kb-split — one crossing transfer issued as two boundary-aligned bursts
6 cycles4. The Arithmetic, Exactly
§3 stated the check as "compute Start_Address + Total_Bytes − 1." That form is safe — it never misses a real crossing — but it is not exact for an unaligned start, and the difference matters when you build a splitter rather than a checker.
For an INCR burst, beat 0 covers from the start address to the end of its aligned container, and every later beat is aligned. So the highest byte the burst touches is measured from the aligned base, not the raw start:
localparam int PAGE = 4096;
function automatic longint unsigned aligned_base(
input longint unsigned addr, input logic [2:0] axsize);
int nbytes = 1 << axsize;
return (addr / nbytes) * nbytes;
endfunction
// The highest byte address the burst accesses.
// beat 0 : start_addr .. aligned+nbytes-1
// beats 1..N-1: aligned + n*nbytes .. aligned + (n+1)*nbytes - 1
// so the last byte is measured from the ALIGNED base, not the raw start.
function automatic longint unsigned last_byte_addr(
input longint unsigned start_addr,
input logic [7:0] axlen,
input logic [2:0] axsize);
int nbytes = 1 << axsize;
int nbeats = int'(axlen) + 1; // the +1 encoding
return aligned_base(start_addr, axsize) + longint'(nbeats) * nbytes - 1;
endfunction
// A burst crosses a 4 KB boundary iff its first and last bytes sit in
// different 4 KB pages. Comparing page numbers is exact and needs no
// special case for a burst that ends exactly ON a boundary.
function automatic bit crosses_4kb(
input longint unsigned start_addr,
input logic [7:0] axlen,
input logic [2:0] axsize);
return (start_addr / PAGE) != (last_byte_addr(start_addr, axlen, axsize) / PAGE);
endfunctionThe / PAGE comparison is worth preferring over subtracting addresses: it is exact at the awkward case where a burst ends on the last byte of a page (0x...FFF), which is legal and must not be reported as a crossing. A formula that compares start + total against the boundary instead of start + total − 1 gets precisely that case wrong — a classic off-by-one that rejects legal traffic.
The splitter. When a transfer would cross, the split point is always the boundary itself. For an aligned INCR burst the arithmetic is short:
// Scope: aligned INCR only. Unaligned starts need the first beat handled
// separately, and FIXED/WRAP never cross by construction (a WRAP container
// is a power of two no larger than its own length).
function automatic int beats_before_boundary(
input longint unsigned start_addr, input logic [2:0] axsize);
int nbytes = 1 << axsize;
longint unsigned boundary = ((start_addr / PAGE) + 1) * PAGE;
return int'((boundary - start_addr) / nbytes); // beats that fit in this page
endfunctionApplied to §3's worked example — 16 bytes from 0xFF8, 4 bytes per beat — this returns (0x1000 − 0xFF8) / 4 = 2 beats, so the first burst is AxLEN=1 at 0xFF8 and the second is AxLEN=1 at 0x1000. That is exactly the split Figure 3 draws, now computed rather than asserted.
5. Checking It, and the Bug It Catches
The rule is a one-line assertion on the address channel, and it is worth binding to every master interface you integrate.
module axi_4kb_check (
input logic aclk, aresetn,
input logic axvalid, axready,
input logic [63:0] axaddr,
input logic [7:0] axlen,
input logic [2:0] axsize,
input logic [1:0] axburst
);
`include "axi_4kb.svh"
// NORMATIVE (AMBA AXI): a burst must not cross a 4 KB address boundary.
// The obligation is on the ISSUER -- see the enforcement discussion below.
property p_no_4kb_crossing;
@(posedge aclk) disable iff (!aresetn)
(axvalid && axready && axburst == 2'b01) // INCR is the crossing risk
|-> !crosses_4kb(axaddr, axlen, axsize);
endproperty
a_no_4kb : assert property (p_no_4kb_crossing)
else $error("burst at 0x%0h (AxLEN=%0d, AxSIZE=%0d) ends at 0x%0h - crosses a 4 KB boundary",
axaddr, axlen, axsize, last_byte_addr(axaddr, axlen, axsize));
endmoduleThe error message prints the computed last byte deliberately, because the two ways this goes wrong are told apart by that number:
Intended: 16 bytes from 0xFF8, 4 bytes/beat -> AxLEN=3, AxSIZE=2
BUG 1 - forgot the +1 encoding (drove AxLEN=4, meaning 5 beats):
last byte = 0xFF8 aligned (0xFF8) + 5*4 - 1 = 0x100B -> CROSSES
the burst was one beat longer than intended, and that extra beat
is what pushed it over the boundary.
BUG 2 - used beats instead of bytes (treated AxSIZE=2 as "2 bytes"):
believed span = 4 beats * 2 = 8 bytes, "ends at 0xFFF, safe"
actual span = 4 beats * 4 = 16 bytes, ends at 0x1007 -> CROSSES
the master computed a legal-looking burst and issued an illegal one.Both produce the same protocol violation from opposite errors, and in both the checker's reported last byte immediately contradicts the master's assumption — which is the fastest way to tell a length bug from a size bug.
6. Proving the Cases
Four cases matter, and only one of them is a crossing:
module tb_4kb;
`include "axi_4kb.svh"
int errors = 0;
task automatic chk(input bit got, exp, input string what);
if (got !== exp) begin
errors++;
$error("[FAIL] %s: got %b expected %b", what, got, exp);
end
endtask
initial begin
// 1. comfortably inside one page: 4 beats x 4 bytes at 0x1000 -> 0x100F
chk(crosses_4kb(64'h1000, 8'd3, 3'd2), 1'b0, "well inside the page");
// 2. ends EXACTLY on the last byte of the page (0x1FFF) - legal
chk(crosses_4kb(64'h1FF0, 8'd3, 3'd2), 1'b0, "ends exactly at 0x1FFF");
// 3. one beat further - first byte of the next page - ILLEGAL
chk(crosses_4kb(64'h1FF4, 8'd3, 3'd2), 1'b1, "crosses into the next page");
// 4. the section-3 example: 16 bytes from 0xFF8 - ILLEGAL, must be split
chk(crosses_4kb(64'h0FF8, 8'd3, 3'd2), 1'b1, "0xFF8 + 16 bytes crosses");
// and the split it implies
chk(beats_before_boundary(64'h0FF8, 3'd2) == 2, 1'b1, "split gives 2 beats first");
if (errors == 0) $display("PASS - boundary arithmetic correct on all four cases");
else $display("FAIL - %0d case(s) wrong", errors);
$finish;
end
endmoduleCase 2 is the one that justifies the whole exercise. A burst ending at 0x1FFF touches the final byte of the page and is completely legal — but a checker written with start + total instead of start + total − 1 reports it as a crossing and rejects correct traffic. Boundary rules are where off-by-one errors are least forgiving, because both failure directions are expensive: too loose and illegal bursts reach the interconnect, too strict and legal ones are refused.
The burst quantities these functions consume — AxLEN + 1, 2^AxSIZE, and the per-type limits — are established in burst fundamentals, and the lane behaviour of an unaligned first beat is narrow and unaligned transfers.
7. Who Enforces It
The rule is the issuing side's responsibility — a manager must not drive a burst that crosses a 4 KB boundary; doing so is a protocol violation. In practice the split happens in one of these places:
- The manager / DMA shapes its bursts to respect the boundary (the common case — burst-generation logic checks the boundary and splits).
- An interconnect / bridge may split a crossing burst it receives (some do, as a safety/convenience feature), but relying on this is risky — not all do, and the spec puts the obligation on the master.
- A protocol checker / VIP flags any burst whose range crosses a 4 KB boundary as an error.
The safe design stance: shape bursts at the source so they never cross, and verify it. Never assume something downstream will fix a crossing burst.
8. Common Misconceptions
9. Debugging Insight
10. Verification Insight
11. Interview Questions
12. Summary
The 4 KB boundary rule is AXI's one absolute placement constraint: no burst may cross a 4 KB boundary — its full byte range (Start … Start + Total_Bytes − 1) must lie within a single 0x1000-aligned region. The reason is architectural: 4 KB is the minimum page size, so the address map — and which slave an address decodes to — can change at any 4 KB boundary. Keeping each burst inside one region guarantees it routes to a single slave with one response path, which a single-AxADDR transaction requires. The check is INT(Start/0x1000) == INT((Start+Total−1)/0x1000); a transfer that would cross is split at the boundary into legal sub-bursts (e.g., 16 bytes at 0xFF8 → 0xFF8..0xFFF + 0x1000..0x1007).
Enforcement is the master's job — a crossing burst is a protocol violation, and downstream splitting must not be assumed. Only INCR can actually cross (FIXED can't move; WRAP stays in its block). Its bug signature is unmistakable: tail-of-transfer corruption or DECERR, often intermittent with buffer placement — diagnosed by the same region arithmetic and fixed by boundary-aware burst shaping. Verify with the always-on "no crossing" assertion plus directed split tests right at the boundary. Next: narrow and unaligned transfers — the partial-beat behavior that the unaligned cases from Chapter 7.5 set up.
Where this is specified. The 4 KB boundary rule, the burst address definitions it depends on, and the placement of the obligation on the issuing master are all in Arm's AMBA AXI Protocol Specification (IHI 0022).
13. What Comes Next
You've got the placement rule; next, the partial-beat mechanics:
- 7.7 — Narrow & Unaligned Transfers (coming next) — narrow transfers (
AxSIZEbelow bus width) and unaligned start addresses, and the partial beats they produce. - 7.8 — Strobe Behavior in Bursts (coming soon) — how
WSTRBevolves across narrow and unaligned burst beats.
Previous: 7.5 — Burst Address Calculation. Related: 7.3 — INCR Bursts, the only type that can cross a boundary. For the broader protocol catalog, see the AMBA family overview doc.