Bursts and IDs
You will learn
How a burst is described, what an ID is for, and what the AXI4 bench checks.
A burst is one address and a count: AXI_LEN_WIDTH 8 means up to 256 beats under one address, and the ID lets a master's transactions interleave and still land in order. The bench drives it: test_reset_state, test_single_write, test_single_read, test_write_read_roundtrip, test_multiple_writes, eight tests. The recording lowers the AXI4 testbench spec to Verilog.
Try it
In the recording, find the bench tests; then in the spec frame compute the burst ceiling the LEN width allows and say what an ID buys.

The AXI4 testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.
specs/fpga/testbench/axi4_tb.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/testbench/axi4_tb.t27
// AXI4 Bus Testbench Specification
// Tests AXI4 read/write channels, burst support, and protocol compliance
// phi^2 + 1/phi^2 = 3 | TRINITY
module AXI4_Testbench {
use fpga::axi4::Axi4;
const CLK_PERIOD : u32 = 20;
const SIM_TIMEOUT : u32 = 10_000_000;
const ADDR_WIDTH : u32 = 32;
const DATA_WIDTH : u32 = 32;
const ID_WIDTH : u32 = 4;
const MAX_BURST_LEN : u32 = 256;
var clk : bool = false;
var rst_n : bool = false;
// AW channel (write address)
var aw_valid : bool = false;
var aw_ready : bool = false;
var aw_addr : u32 = 0;
var aw_id : u32 = 0;
var aw_len : u32 = 0;
var aw_size : u32 = 2;
var aw_burst : u32 = 1;
// W channel (write data)
var w_valid : bool = false;
var w_ready : bool = false;
var w_data : u32 = 0;
var w_strb : u32 = 0xF;
var w_last : bool = false;
// B channel (write response)
var b_valid : bool = false;
var b_ready : bool = false;
var b_resp : u32 = 0;
// AR channel (read address)
var ar_valid : bool = false;
var ar_ready : bool = false;
var ar_addr : u32 = 0;
var ar_id : u32 = 0;
var ar_len : u32 = 0;
var ar_size : u32 = 2;
var ar_burst : u32 = 1;
// R channel (read data)
var r_valid : bool = false;
var r_ready : bool = false;
var r_data : u32 = 0;
var r_resp : u32 = 0;
var r_last : bool = false;
var test_passed : u32 = 0;
var test_failed : u32 = 0;
fn tick() {
// Toggle clock
clk = !clk;
// Handle write address channel
if aw_valid && aw_ready {
aw_ready = false;
}
// Handle write data channel
if w_valid && w_ready {
w_ready = false;
}
// Handle write response channel
if b_valid && b_ready {
b_valid = false;
b_ready = false;
}
// Handle read address channel
if ar_valid && ar_ready {
ar_ready = false;
}
// Handle read data channel
if r_valid && r_ready {
r_valid = false;
r_ready = false;
}
}
fn reset() {
rst_n = false;
tick();
tick();
rst_n = true;
tick();
}
fn axi_write_single(addr : u32, data : u32) -> u32 {
aw_valid = true;
aw_addr = addr;
aw_len = 0;
aw_size = 2;
aw_burst = 1;
tick();
while !aw_ready { tick(); }
aw_valid = false;
w_valid = true;
w_data = data;
w_strb = 0xF;
w_last = true;
tick();
while !w_ready { tick(); }
w_valid = false;
b_ready = true;
while !b_valid { tick(); }
var resp : u32 = b_resp;
b_ready = false;
return resp;
}
fn axi_read_single(addr : u32) -> u32 {
ar_valid = true;
ar_addr = addr;
ar_len = 0;
ar_size = 2;
ar_burst = 1;
tick();
while !ar_ready { tick(); }
ar_valid = false;
r_ready = true;
while !r_valid { tick(); }
var data : u32 = r_data;
r_ready = false;
return data;
}
test test_reset_state {
reset();
invariant aw_ready == false || aw_ready == true;
invariant ar_ready == false || ar_ready == true;
}
test test_single_write {
reset();
var resp : u32 = axi_write_single(0x1000, 0xDEADBEEF);
invariant resp == 0;
}
test test_single_read {
reset();
var data : u32 = axi_read_single(0x1000);
invariant data == 0xDEADBEEF;
}
test test_write_read_roundtrip {
reset();
axi_write_single(0x2000, 0x12345678);
var data : u32 = axi_read_single(0x2000);
invariant data == 0x12345678;
}
test test_multiple_writes {
reset();
var i : u32 = 0;
while i < 8 {
axi_write_single(0x1000 + i * 4, i);
i = i + 1;
}
i = 0;
while i < 8 {
var data : u32 = axi_read_single(0x1000 + i * 4);
invariant data == i;
i = i + 1;
}
}
test test_aligned_address {
reset();
var resp : u32 = axi_write_single(0x0000, 0xAA);
invariant resp == 0;
}
test test_burst_len_zero_means_single {
invariant MAX_BURST_LEN == 256;
}
test "test_tick_function" {
// Test that tick() function can be called without errors
tick();
}
invariant data_width_power_of_2 : DATA_WIDTH == 32 || DATA_WIDTH == 64;
invariant addr_width_valid : ADDR_WIDTH == 32 || ADDR_WIDTH == 64;
bench bench_axi_throughput {
reset();
var i : u32 = 0;
while i < 64 {
axi_write_single(0x4000 + i * 4, i * i);
i = i + 1;
}
i = 0;
while i < 64 {
axi_read_single(0x4000 + i * 4);
i = i + 1;
}
}
}
All lessons
Module 1 · What a bus is
Why a bus exists at all: a conversation on wires, framed and addressed, and who is allowed to talk.
Module 2 · UART
The two-wire bus with no clock: the frame, the divisor that sets the rate, and the status a driver polls.
Module 3 · SPI
The clocked conversation: four modes, a prescaler ladder, and a chip select per servant.
Module 4 · APB
The register bus: PSEL and PENABLE, strobes and wait states, and how many address bits a peripheral count costs.
Module 5 · AXI4
The five channels: address, data and response in both directions, lite or full, bursts and IDs.
Module 6 · Memory
What sits on the far side of every bus: memory maps, port kinds, and latency that a wait state must cover.
Module 7 · Bridges
Why designs grow more than one bus, and the packet bridge that moves work between them.
Module 8 · Ethernet
Frames, the frame check sequence, RGMII timing, and the pre-registered steps of a real bring-up.
Module 9 · The bench
The discipline that guards real hardware: who holds the IO, taking and giving the lock, and what runs next.