t27.aiРусский

Strobes and waits

You will learn

What the strobe bytes mean, when a servant holds the bus, and how the IR sees it.

The strobe bytes say which lanes of the word carry data: strb_width_32bit and strb_width_16bit are two of the spec's eighteen tests, one word served whole and one half-word served. A slow servant holds the bus with a wait state instead of guessing. The recording shows the APB bridge as hardware IR: PSEL, PENABLE and the decode as the compiler structures them.

Try it

In the recording, find PSEL and PENABLE in the IR; then in the spec frame find the two strobe-width tests and say what a half-word transfer strobes.

Open the interactive lesson →

t27c debug-hir on apb_bridge.t27 -- the handshake as IR
t27c debug-hir on apb_bridge.t27 -- the handshake as IR ↗

The APB bridge spec lowered to hardware IR: PSEL, PENABLE and the decode as the compiler structures them.

specs/fpga/apb_bridge.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/apb_bridge.t27
// APB (Advanced Peripheral Bus) Bridge Specification for Trinity T27 FPGA HIR
// Register-mapped peripheral bridge for low-bandwidth peripherals
// Uses flat arrays + count fields (parser-compatible)
// phi^2 + 1/phi^2 = 3 | TRINITY

module ApbBridge {

    // === APB bus width constants ===

    pub const APB_ADDR_WIDTH : u32 = 32;
    pub const APB_DATA_WIDTH : u32 = 32;
    pub const APB_STRB_WIDTH : u32 = 4;

    // === APB transfer kind ===

    pub const ApbTransfer = enum(i8) {
        idle = 0,
        setup = 1,
        access = 2,
    }

    // === APB bridge configuration ===

    pub struct ApbConfig {
        name : &str,
        addr_width : u32,
        data_width : u32,
        num_peripherals : u32,
        base_addr : u32,
        addr_mask : u32,
        has_pslverr : bool,
        has_pprot : bool,
    }

    // === Peripheral address range ===

    pub struct PeripheralMap {
        name : &str,
        base_addr : u32,
        size : u32,
        index : u32,
    }

    // === APB read/write request (for simulation) ===

    pub struct ApbRequest {
        addr : u32,
        wdata : u32,
        write : bool,
        strb : u32,
        valid : bool,
    }

    // === APB response (for simulation) ===

    pub struct ApbResponse {
        rdata : u32,
        ready : bool,
        slverr : bool,
    }

    // === Constructor helpers ===

    fn apb_bridge(name: &str, addr_width: u32, data_width: u32, num_peripherals: u32) -> ApbConfig {
        return ApbConfig{
            .name = name,
            .addr_width = addr_width,
            .data_width = data_width,
            .num_peripherals = num_peripherals,
            .base_addr = 0,
            .addr_mask = 0,
            .has_pslverr = false,
            .has_pprot = false,
        };
    }

    fn apb_bridge_with_error(name: &str, addr_width: u32, data_width: u32, num_peripherals: u32) -> ApbConfig {
        return ApbConfig{
            .name = name,
            .addr_width = addr_width,
            .data_width = data_width,
            .num_peripherals = num_peripherals,
            .base_addr = 0,
            .addr_mask = 0,
            .has_pslverr = true,
            .has_pprot = true,
        };
    }

    fn peripheral_map(name: &str, base_addr: u32, size: u32, index: u32) -> PeripheralMap {
        return PeripheralMap{
            .name = name,
            .base_addr = base_addr,
            .size = size,
            .index = index,
        };
    }

    fn apb_read_request(addr: u32) -> ApbRequest {
        return ApbRequest{
            .addr = addr,
            .wdata = 0,
            .write = false,
            .strb = 15,
            .valid = true,
        };
    }

    fn apb_write_request(addr: u32, data: u32, strb: u32) -> ApbRequest {
        return ApbRequest{
            .addr = addr,
            .wdata = data,
            .write = true,
            .strb = strb,
            .valid = true,
        };
    }

    fn apb_ok_response(data: u32) -> ApbResponse {
        return ApbResponse{
            .rdata = data,
            .ready = true,
            .slverr = false,
        };
    }

    fn apb_error_response() -> ApbResponse {
        return ApbResponse{
            .rdata = 0,
            .ready = true,
            .slverr = true,
        };
    }

    // === Query functions ===

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

    fn addr_bits_for_peripherals(cfg: ApbConfig) -> u32 {
        var n : u32 = cfg.num_peripherals;
        if (n <= 1) {
            return 0;
        }
        var bits : u32 = 0;
        while (n > 1) {
            bits = bits + 1;
            n = n / 2;
        }
        return bits;
    }

    fn peripheral_addr_offset(cfg: ApbConfig, periph_index: u32) -> u32 {
        var offset_bits : u32 = addr_bits_for_peripherals(cfg);
        return periph_index << offset_bits;
    }

    fn is_read(req: ApbRequest) -> bool {
        return req.valid and req.write == false;
    }

    fn is_write(req: ApbRequest) -> bool {
        return req.valid and req.write;
    }

    fn apb_port_count(cfg: ApbConfig) -> u32 {
        var count : u32 = 0;
        count = count + 1;
        count = count + 1;
        count = count + cfg.addr_width;
        count = count + cfg.data_width;
        count = count + cfg.data_width / 8;
        count = count + cfg.data_width;
        count = count + 1;
        if (cfg.has_pslverr) {
            count = count + 1;
        }
        if (cfg.has_pprot) {
            count = count + 3;
        }
        return count;
    }

    fn select_peripheral(cfg: ApbConfig, addr: u32, maps: [16]PeripheralMap, map_count: u32) -> u32 {
        var i : u32 = 0;
        while (i < map_count) {
            var base : u32 = maps[i].base_addr;
            var size : u32 = maps[i].size;
            if (addr >= base and addr < base + size) {
                return i;
            }
            i = i + 1;
        }
        return 65535;
    }

    // === Validation ===

    fn validate_apb(cfg: ApbConfig) -> 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.num_peripherals == 0) {
            errors = errors + 1;
        }
        return errors;
    }

    fn validate_peripheral_map(m: PeripheralMap) -> u32 {
        var errors : u32 = 0;
        if (m.name == "") {
            errors = errors + 1;
        }
        if (m.size == 0) {
            errors = errors + 1;
        }
        return errors;
    }

    // === Tests ===

    test apb_bridge_creation
        given cfg = apb_bridge("apb0", 32, 32, 4)
        then cfg.name == "apb0"
        and cfg.addr_width == 32
        and cfg.data_width == 32
        and cfg.num_peripherals == 4
        and cfg.has_pslverr == false

    test apb_bridge_with_error
        given cfg = apb_bridge_with_error("apb1", 32, 32, 8)
        then cfg.has_pslverr == true
        and cfg.has_pprot == true

    test strb_width_32bit
        given cfg = apb_bridge("apb0", 32, 32, 4)
        then strb_width(cfg) == 4

    test strb_width_16bit
        given cfg = apb_bridge("apb0", 16, 16, 4)
        then strb_width(cfg) == 2

    test addr_bits_for_1_peripheral
        given cfg = apb_bridge("apb0", 32, 32, 1)
        then addr_bits_for_peripherals(cfg) == 0

    test addr_bits_for_4_peripherals
        given cfg = apb_bridge("apb0", 32, 32, 4)
        then addr_bits_for_peripherals(cfg) == 2

    test addr_bits_for_8_peripherals
        given cfg = apb_bridge("apb0", 32, 32, 8)
        then addr_bits_for_peripherals(cfg) == 3

    test read_request
        given req = apb_read_request(256)
        then is_read(req) == true
        and is_write(req) == false
        and req.addr == 256

    test write_request
        given req = apb_write_request(256, 42, 15)
        then is_read(req) == false
        and is_write(req) == true
        and req.wdata == 42

    test ok_response
        given resp = apb_ok_response(99)
        then resp.rdata == 99
        and resp.ready == true
        and resp.slverr == false

    test error_response
        given resp = apb_error_response()
        then resp.slverr == true

    test validate_ok
        given cfg = apb_bridge("apb0", 32, 32, 4)
        then validate_apb(cfg) == 0

    test validate_empty_name
        given cfg = apb_bridge("", 32, 32, 4)
        then validate_apb(cfg) > 0

    test validate_zero_addr
        given cfg = apb_bridge("apb0", 0, 32, 4)
        then validate_apb(cfg) > 0

    test validate_zero_peripherals
        given cfg = apb_bridge("apb0", 32, 32, 0)
        then validate_apb(cfg) > 0

    test validate_peripheral_map_ok
        given m = peripheral_map("uart0", 4096, 256, 0)
        then validate_peripheral_map(m) == 0

    test validate_peripheral_map_no_name
        given m = peripheral_map("", 4096, 256, 0)
        then validate_peripheral_map(m) > 0

    test apb_port_count_basic
        given cfg = apb_bridge("apb0", 32, 32, 4)
        then apb_port_count(cfg) > 0

    // === Invariants ===

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

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

    invariant strb_matches_data
        given cfg = apb_bridge("inv", 32, 32, 4)
        assert strb_width(cfg) == cfg.data_width / 8

    invariant num_peripherals_positive
        given cfg = apb_bridge("inv", 32, 32, 4)
        assert cfg.num_peripherals > 0

    invariant validate_non_negative
        given cfg = apb_bridge("inv", 32, 32, 4)
        assert validate_apb(cfg) >= 0

    // === Benchmarks ===

    bench validate_latency
        measure: nanoseconds to validate_apb(apb_bridge("b", 32, 32, 4))
        target: < 100ns
}

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

Open the lesson's spec in the player ↗

All lessons