Address decode
You will learn
How many address bits a peripheral count costs, and what the bench of the bridge checks.
Addressing on APB is a question of bits: how many servants sit behind the bridge decides how many address bits the decode spends. The spec answers by count -- addr_bits_for_1_peripheral, addr_bits_for_4_peripherals, addr_bits_for_8_peripherals -- and its testbench drives the result: test_reset_state, test_apb_write, test_apb_read, test_write_read_roundtrip, test_multiple_apb_writes, eight tests. The recording lowers the APB bridge testbench spec to Verilog.
Try it
In the recording, find the bench tests; then in the spec frame compute the address bits the spec spends for 8 peripherals.

The APB bridge testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.
specs/fpga/testbench/apb_bridge_tb.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/testbench/apb_bridge_tb.t27
// APB Bridge Testbench
// Tests APB bus protocol: setup, access, wait states
// phi^2 + 1/phi^2 = 3 | TRINITY
module APB_Bridge_Testbench {
use fpga::apb_bridge::ApbBridge;
const CLK_PERIOD : u32 = 20;
const SIM_TIMEOUT : u32 = 5_000_000;
const APB_ADDR_WIDTH : u32 = 12;
const APB_DATA_WIDTH : u32 = 32;
var clk : bool = false;
var rst_n : bool = false;
var psel : bool = false;
var penable : bool = false;
var pwrite : bool = false;
var paddr : u32 = 0;
var pwdata : u32 = 0;
var prdata : u32 = 0;
var pready : bool = false;
var pslverr : bool = false;
var test_passed : u32 = 0;
var test_failed : u32 = 0;
fn tick() {
clk = false;
clk = true;
}
fn reset() {
rst_n = false;
tick();
tick();
rst_n = true;
tick();
}
fn apb_write(addr : u32, data : u32) -> bool {
paddr = addr;
pwdata = data;
pwrite = true;
psel = true;
penable = false;
tick();
penable = true;
tick();
while !pready { tick(); }
psel = false;
penable = false;
pwrite = false;
return !pslverr;
}
fn apb_read(addr : u32) -> u32 {
paddr = addr;
pwrite = false;
psel = true;
penable = false;
tick();
penable = true;
tick();
while !pready { tick(); }
var data : u32 = prdata;
psel = false;
penable = false;
return data;
}
test test_reset_state {
reset();
invariant pready == true || pready == false;
invariant pslverr == false;
}
test test_apb_write {
reset();
var ok : bool = apb_write(0x100, 0xDEAD);
invariant ok == true;
invariant pslverr == false;
}
test test_apb_read {
reset();
var val : u32 = apb_read(0x100);
invariant pslverr == false;
}
test test_write_read_roundtrip {
reset();
apb_write(0x200, 0xCAFEBABE);
var val : u32 = apb_read(0x200);
invariant val == 0xCAFEBABE;
}
test test_multiple_apb_writes {
reset();
var i : u32 = 0;
while i < 8 {
apb_write(0x000 + i * 4, i * 0x10);
i = i + 1;
}
i = 0;
while i < 8 {
var val : u32 = apb_read(0x000 + i * 4);
invariant val == i * 0x10;
i = i + 1;
}
}
test test_protocol_phases {
reset();
psel = true;
penable = false;
tick();
invariant psel == true;
penable = true;
tick();
invariant penable == true;
psel = false;
penable = false;
}
invariant addr_width_valid : APB_ADDR_WIDTH > 0;
invariant data_width_valid : APB_DATA_WIDTH == 32;
test test_tick_functionality {
reset();
var initial_clk : bool = clk;
tick();
var after_tick_clk : bool = clk;
invariant after_tick_clk != initial_clk;
}
bench bench_apb_throughput {
reset();
var i : u32 = 0;
while i < 64 {
apb_write(i * 4, i);
i = i + 1;
}
i = 0;
while i < 64 {
apb_read(i * 4);
i = i + 1;
}
}
test "tick_function" {
reset();
var initial_clk : bool = clk;
tick();
invariant initial_clk != clk;
}
}
All lessons
Module 1 · What a bus is
Why a bus exists at all: a conversation on wires, framed and addressed, and who is allowed to talk.
Module 2 · UART
The two-wire bus with no clock: the frame, the divisor that sets the rate, and the status a driver polls.
Module 3 · SPI
The clocked conversation: four modes, a prescaler ladder, and a chip select per servant.
Module 4 · APB
The register bus: PSEL and PENABLE, strobes and wait states, and how many address bits a peripheral count costs.
Module 5 · AXI4
The five channels: address, data and response in both directions, lite or full, bursts and IDs.
Module 6 · Memory
What sits on the far side of every bus: memory maps, port kinds, and latency that a wait state must cover.
Module 7 · Bridges
Why designs grow more than one bus, and the packet bridge that moves work between them.
Module 8 · Ethernet
Frames, the frame check sequence, RGMII timing, and the pre-registered steps of a real bring-up.
Module 9 · The bench
The discipline that guards real hardware: who holds the IO, taking and giving the lock, and what runs next.