t27.aiРусский

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.

Open the interactive lesson →

t27c gen-verilog on axi4_tb.t27 -- the testbench as RTL
t27c gen-verilog on axi4_tb.t27 -- the testbench as RTL ↗

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;
        }
    }
}

Open the lesson's spec in the player ↗

All lessons