Пять каналов
Вы узнаете
Что несут пять независимых каналов AXI4 и что компилятор порождает для них.
AXI4 несёт пять независимых каналов: адрес записи, данные записи, отклик записи, адрес чтения, данные чтения. Спека заявляет ширины — AXI_ADDR_WIDTH 32, AXI_DATA_WIDTH 32, AXI_STRB_WIDTH 4, AXI_ID_WIDTH 4, AXI_LEN_WIDTH 8, AXI_SIZE_WIDTH 3 — под MAX_BUS_PORTS равным 32. Запись опускает спеку AXI4 в синтезируемый Verilog: разводку каналов и рукопожатия как порождённые провода.
Попробовать
В записи найдите каналы в порождённых проводах; затем в спеке перечислите шесть ширин AXI и скажите, что покрывает 8-битный LEN.

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
Все уроки
Модуль 1 · Что такое шина
Зачем вообще существует шина: разговор по проводам, с кадрами и адресами, и кому позволено говорить.
Модуль 2 · Разговор по UART
Двухпроводная шина без такта: кадр, делитель, задающий скорость, и статус, который опрашивает драйвер.
Модуль 3 · SPI под тактом
Разговор под тактом: четыре режима, лестница предделителей и выбор кристалла на каждого подчинённого.
Модуль 4 · Регистровая шина APB
Шина регистров: PSEL и PENABLE, строобы и ожидания, и сколько адресных бит стоит количество периферии.
Модуль 5 · Пять каналов AXI4
Пять каналов: адрес, данные и отклик в обе стороны, lite или full, пакеты и идентификаторы.
Модуль 6 · Память
Что находится на дальнем конце каждой шины: карты памяти, типы портов и задержка, которую должно покрывать ожидание.
Модуль 7 · Мосты
Почему в дизайне появляется больше одной шины и пакетный мост, переносящий работу между ними.
Модуль 8 · Ethernet: кадры и тайминг
Кадры, контрольная сумма кадра, тайминг RGMII и заранее зарегистрированные шаги настоящего запуска.
Модуль 9 · Стенд
Дисциплина, оберегающая настоящее железо: кто держит IO, как взять и вернуть захват, и что запускается дальше.