Skip to content

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.

A legal burst stays within one 4 KB region; a burst crossing a 0x1000 boundary is illegal and must be split.4 KB region0x1000-alignedLegal burstrange within one region0x1000 boundaryregion edgeCrossing burstspans two regions —ILLEGAL12
Figure 1 — the 4 KB boundary rule. A burst must fit entirely inside one 4 KB-aligned region (boundaries at every 0x1000). A burst whose byte range stays below the next 0x1000 boundary is legal; one whose range would extend past it is illegal and must be split. The rule binds start address and length together.

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 AxADDR and 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.

Page N maps to slave A, page N+1 maps to slave B; a burst within one page hits one slave, a crossing burst would need two.Page N (4 KB)decodes to Slave ASlave Aone response pathPage N+1 (4 KB)decodes to Slave BSlave Bdifferent destination12
Figure 2 — why 4 KB. Adjacent 4 KB pages can map to different slaves (the decode changes at the boundary). A burst confined to one 4 KB region always decodes to a single slave with a single response path; a crossing burst would need to split across two slaves — impossible for one transaction with one AxADDR. The rule guarantees single-slave routing.

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, covering 0xFF8 … 0xFFF = 8 bytes (2 beats) — fills up to the boundary.
  • Burst 2: start 0x1000, covering 0x1000 … 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 cycles
Two write address handshakes: burst 1 at 0xFF8 covering up to the 0x1000 boundary, burst 2 at 0x1000 covering the remainder.burst 1 → 0xFF8..0xFFFburst 1 →0xFF8..0…burst 2 → 0x1000..0x1007burst 2 →0x1000..…burst 1: up to 0x1000 boundaryburst 1: up to 0x1000boundaryburst 2: remainder past boundaryburst 2: remainder pastboundaryaclkawvalidawreadyawaddr0FF80FF80FF8100010001000awlen1 (2b)1 (2b)1 (2b)1 (2b)1 (2b)1 (2b)t0t1t2t3t4t5
Figure 3 — 4kb-split: a 16-byte transfer at 0xFF8 that would cross 0x1000 is issued as two bursts. Burst 1 (AWADDR=0xFF8, 8 bytes) fills to the boundary; burst 2 (AWADDR=0x1000, 8 bytes) carries the remainder. Two address handshakes, each burst confined to one 4 KB region — the data is identical to the would-be single crossing burst.

4. 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:

axi_4kb.svh — exact span, crossing test, and the split point
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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);
endfunction

The / 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:

the split — first burst fills to the boundary, second carries the remainder
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
endfunction

Applied 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.

axi_4kb_check.sv — passive; bind to an AW or AR channel
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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));
endmodule

The error message prints the computed last byte deliberately, because the two ways this goes wrong are told apart by that number:

the two classic miscalculations, and what the checker reports
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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:

tb_4kb.sv — the boundary cases, explicit PASS/FAIL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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
endmodule

Case 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.

Master checks if the transfer crosses a 4 KB boundary; if yes, split at the boundary; if no, issue one burst. The issuer is responsible.noyesIntendedtransferRange crossesa 4 KBboundary?Issue as oneburstSplit atboundary intolegal bursts
Figure 4 — enforcement flow. The burst-generating master computes whether the intended transfer crosses a 4 KB boundary; if so it splits at the boundary into legal bursts, otherwise it issues one. The obligation is on the issuer — a crossing burst is a protocol violation, and downstream splitting (by some interconnects) must not be assumed.

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 0xFF80xFF8..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:

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.