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