t27.aiРусский

Five channels

You will learn

What the five AXI4 channels carry, and what the compiler emits for them.

AXI4 carries five independent channels: write address, write data, write response, read address, read data. The spec states the widths -- AXI_ADDR_WIDTH 32, AXI_DATA_WIDTH 32, AXI_STRB_WIDTH 4, AXI_ID_WIDTH 4, AXI_LEN_WIDTH 8, AXI_SIZE_WIDTH 3 -- under a MAX_BUS_PORTS of 32. The recording lowers the AXI4 spec to synthesizable Verilog: the channels' wiring and handshakes as generated wires.

Try it

In the recording, find the channels in the emitted wires; then in the spec frame list the six AXI widths and say what the 8-bit LEN covers.

Open the interactive lesson →

t27c gen-verilog on axi4.t27 -- spec to RTL
t27c gen-verilog on axi4.t27 -- spec to RTL ↗

The AXI4 spec lowered to synthesizable Verilog: the five channels' wiring and handshakes as generated wires.

specs/fpga/axi4.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/axi4.t27
// AXI4-Lite and AXI4-Full Bus Interface Specification for Trinity T27 FPGA HIR
// Defines bus port groups for AW/AR/W/R/B channels
// Uses flat arrays + count fields (parser-compatible)
// phi^2 + 1/phi^2 = 3 | TRINITY

module Axi4 {

    // === Bus kind ===

    pub const AxiKind = enum(i8) {
        axi4_lite = 0,
        axi4_full = 1,
    }

    // === AXI4 channel signals (described as flat arrays) ===

    pub const MAX_BUS_PORTS : u32 = 32;
    pub const AXI_ADDR_WIDTH : u32 = 32;
    pub const AXI_DATA_WIDTH : u32 = 32;
    pub const AXI_STRB_WIDTH : u32 = 4;
    pub const AXI_ID_WIDTH : u32 = 4;
    pub const AXI_LEN_WIDTH : u32 = 8;
    pub const AXI_SIZE_WIDTH : u32 = 3;
    pub const AXI_BURST_WIDTH : u32 = 2;
    pub const AXI_RESP_WIDTH : u32 = 2;
    pub const AXI_CACHE_WIDTH : u32 = 4;
    pub const AXI_PROT_WIDTH : u32 = 3;
    pub const AXI_QOS_WIDTH : u32 = 4;
    pub const AXI_REGION_WIDTH : u32 = 4;
    pub const AXI_USER_WIDTH : u32 = 1;

    // === Bus port configuration ===

    pub struct AxiBusConfig {
        name : &str,
        kind : i8,
        addr_width : u32,
        data_width : u32,
        id_width : u32,
        has_region : bool,
        has_cache : bool,
        has_prot : bool,
        has_qos : bool,
        has_user : bool,
        has_lock : bool,
    }

    // === Bus port group (flat) ===

    pub struct BusPort {
        name : &str,
        direction : i8,
        width : u32,
        channel : i8,
    }

    // Channel codes
    pub const CH_AW : i8 = 0;
    pub const CH_AR : i8 = 1;
    pub const CH_W : i8 = 2;
    pub const CH_R : i8 = 3;
    pub const CH_B : i8 = 4;

    // === Constructor helpers ===

    fn axi4_lite_slave(name: &str, addr_width: u32, data_width: u32) -> AxiBusConfig {
        return AxiBusConfig{
            .name = name,
            .kind = 0,
            .addr_width = addr_width,
            .data_width = data_width,
            .id_width = 0,
            .has_region = false,
            .has_cache = false,
            .has_prot = true,
            .has_qos = false,
            .has_user = false,
            .has_lock = false,
        };
    }

    fn axi4_lite_master(name: &str, addr_width: u32, data_width: u32) -> AxiBusConfig {
        return AxiBusConfig{
            .name = name,
            .kind = 0,
            .addr_width = addr_width,
            .data_width = data_width,
            .id_width = 0,
            .has_region = false,
            .has_cache = false,
            .has_prot = true,
            .has_qos = false,
            .has_user = false,
            .has_lock = false,
        };
    }

    fn axi4_full_slave(name: &str, addr_width: u32, data_width: u32, id_width: u32) -> AxiBusConfig {
        return AxiBusConfig{
            .name = name,
            .kind = 1,
            .addr_width = addr_width,
            .data_width = data_width,
            .id_width = id_width,
            .has_region = true,
            .has_cache = true,
            .has_prot = true,
            .has_qos = true,
            .has_user = false,
            .has_lock = true,
        };
    }

    fn axi4_full_master(name: &str, addr_width: u32, data_width: u32, id_width: u32) -> AxiBusConfig {
        return AxiBusConfig{
            .name = name,
            .kind = 1,
            .addr_width = addr_width,
            .data_width = data_width,
            .id_width = id_width,
            .has_region = true,
            .has_cache = true,
            .has_prot = true,
            .has_qos = true,
            .has_user = false,
            .has_lock = true,
        };
    }

    // === Query functions ===

    fn is_lite(cfg: AxiBusConfig) -> bool {
        return cfg.kind == 0;
    }

    fn is_full(cfg: AxiBusConfig) -> bool {
        return cfg.kind == 1;
    }

    fn strb_width(cfg: AxiBusConfig) -> u32 {
        return cfg.data_width / 8;
    }

    fn is_master(cfg: AxiBusConfig) -> bool {
        return true;
    }

    // === Port count calculation ===

    fn slave_port_count(cfg: AxiBusConfig) -> u32 {
        var count : u32 = 0;
        // AW channel inputs
        count = count + 1;
        if cfg.id_width > 0 {
            count = count + 1;
        }
        count = count + 1;
        count = count + 1;
        if cfg.has_cache {
            count = count + 1;
        }
        if cfg.has_prot {
            count = count + 1;
        }
        if cfg.has_qos {
            count = count + 1;
        }
        if cfg.has_region {
            count = count + 1;
        }
        if cfg.has_lock {
            count = count + 1;
        }
        // AWREADY output
        count = count + 1;
        // W channel
        count = count + 1;
        count = count + 1;
        // WREADY output
        count = count + 1;
        // B channel
        count = count + 1;
        // BRESP output
        count = count + 1;
        if cfg.id_width > 0 {
            count = count + 1;
        }
        // BREADY input
        count = count + 1;
        // AR channel inputs
        count = count + 1;
        if cfg.id_width > 0 {
            count = count + 1;
        }
        count = count + 1;
        count = count + 1;
        if cfg.has_cache {
            count = count + 1;
        }
        if cfg.has_prot {
            count = count + 1;
        }
        if cfg.has_qos {
            count = count + 1;
        }
        if cfg.has_region {
            count = count + 1;
        }
        if cfg.has_lock {
            count = count + 1;
        }
        // ARREADY output
        count = count + 1;
        // R channel outputs
        count = count + 1;
        count = count + 1;
        if cfg.id_width > 0 {
            count = count + 1;
        }
        // RREADY input
        count = count + 1;
        return count;
    }

    fn total_bus_bits(cfg: AxiBusConfig) -> u32 {
        var bits : u32 = 0;
        bits = bits + cfg.addr_width;
        bits = bits + cfg.data_width;
        bits = bits + cfg.data_width / 8;
        bits = bits + 2;
        bits = bits + 2;
        if cfg.id_width > 0 {
            bits = bits + cfg.id_width * 3;
        }
        if cfg.has_cache {
            bits = bits + 4 * 2;
        }
        if cfg.has_prot {
            bits = bits + 3 * 2;
        }
        if cfg.has_qos {
            bits = bits + 4 * 2;
        }
        if cfg.has_region {
            bits = bits + 4 * 2;
        }
        if cfg.has_lock {
            bits = bits + 2;
        }
        bits = bits + 8;
        bits = bits + 3;
        bits = bits + 2;
        bits = bits + 8;
        return bits;
    }

    // === Validation ===

    fn validate_axi(cfg: AxiBusConfig) -> u32 {
        var errors : u32 = 0;
        if (cfg.name == "") {
            errors = errors + 1;
        }
        if (cfg.addr_width == 0) {
            errors = errors + 1;
        }
        if (cfg.data_width == 0) {
            errors = errors + 1;
        }
        if (cfg.data_width % 8 != 0) {
            errors = errors + 1;
        }
        if (cfg.kind == 1 and cfg.id_width == 0) {
            errors = errors + 1;
        }
        return errors;
    }

    // === Tests ===

    test axi4_lite_is_lite
        given cfg = axi4_lite_slave("s0", 32, 32)
        then is_lite(cfg) == true
        and is_full(cfg) == false

    test axi4_full_is_full
        given cfg = axi4_full_slave("s1", 32, 64, 4)
        then is_full(cfg) == true
        and is_lite(cfg) == false

    test strb_width_32bit
        given cfg = axi4_lite_slave("s0", 32, 32)
        then strb_width(cfg) == 4

    test strb_width_64bit
        given cfg = axi4_full_slave("s1", 32, 64, 4)
        then strb_width(cfg) == 8

    test validate_lite_ok
        given cfg = axi4_lite_slave("s0", 32, 32)
        then validate_axi(cfg) == 0

    test validate_full_ok
        given cfg = axi4_full_slave("s1", 32, 64, 4)
        then validate_axi(cfg) == 0

    test validate_empty_name
        given cfg = axi4_lite_slave("", 32, 32)
        then validate_axi(cfg) > 0

    test validate_zero_addr
        given cfg = axi4_lite_slave("s0", 0, 32)
        then validate_axi(cfg) > 0

    test validate_zero_data
        given cfg = axi4_lite_slave("s0", 32, 0)
        then validate_axi(cfg) > 0

    test validate_non_byte_data
        given cfg = AxiBusConfig{.name = "s0", .kind = 0, .addr_width = 32, .data_width = 12, .id_width = 0, .has_region = false, .has_cache = false, .has_prot = true, .has_qos = false, .has_user = false, .has_lock = false}
        then validate_axi(cfg) > 0

    test validate_full_no_id
        given cfg = axi4_full_slave("s1", 32, 32, 0)
        then validate_axi(cfg) > 0

    test slave_port_count_lite
        given cfg = axi4_lite_slave("s0", 32, 32)
        then slave_port_count(cfg) > 0

    test total_bus_bits_positive
        given cfg = axi4_lite_slave("s0", 32, 32)
        then total_bus_bits(cfg) > 0

    test lite_no_id
        given cfg = axi4_lite_slave("s0", 32, 32)
        then cfg.id_width == 0

    test full_has_id
        given cfg = axi4_full_slave("s1", 32, 64, 4)
        then cfg.id_width == 4

    test full_has_extras
        given cfg = axi4_full_slave("s1", 32, 64, 4)
        then cfg.has_cache == true
        and cfg.has_region == true
        and cfg.has_qos == true
        and cfg.has_lock == true

    // === Invariants ===

    invariant addr_width_positive
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert cfg.addr_width > 0

    invariant data_width_byte_aligned
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert cfg.data_width % 8 == 0

    invariant strb_width_matches_data
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert strb_width(cfg) == cfg.data_width / 8

    invariant lite_has_zero_id
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert cfg.id_width == 0

    invariant full_has_nonzero_id
        given cfg = axi4_full_slave("inv", 32, 32, 4)
        assert cfg.id_width > 0

    invariant validate_non_negative
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert validate_axi(cfg) >= 0

    invariant total_bus_bits_positive
        given cfg = axi4_lite_slave("inv", 32, 32)
        assert total_bus_bits(cfg) > 0

    // === Benchmarks ===

    bench validate_latency
        measure: nanoseconds to validate_axi(axi4_lite_slave("b", 32, 32))
        target: < 100ns

    bench port_count_latency
        measure: nanoseconds to slave_port_count(axi4_full_slave("b", 32, 64, 4))
        target: < 200ns
}

// phi^2 + 1/phi^2 = 3 | TRINITY

Open the lesson's spec in the player ↗

All lessons