An AXI bus in TxHDL: links, routers and bridges

#AXI#FPGA#Rust#TxHDL#auto

Complex designs in TxHDL communicate over AXI4 interfaces. The Vreteno RISC-V processor, the Razboj rasteriser, DDR3 memory controllers, Ethernet MACs, and HDMI video controllers all use AXI interconnects. This post describes the AXI components in the TxHDL standard library. The library provides high-level channel abstractions, hardware protocol trackers, address routers, and bridges to AXI-Lite and Wishbone. All components lower to synthesizable Verilog and VHDL, verified against randomized bus fuzzing on every build.

The AXI4 protocol specifies five independent signaling channels: write address, write data, write response, read address, and read data. Managing these channels directly requires tracking address-data correlations, burst beat counts, last boundary flags, and transaction identifiers. Manual management introduces frequent protocol errors.

TxHDL encapsulates this complexity through the axi() constructor. Like standard message-passing channels, axi() initializes all five channels and returns connected host and peripheral endpoints:

  • The host endpoint exposes two primary methods. read accepts an address and beat count. write accepts an address and a slice of payload beats. Both operations return transaction handles that the client awaits for completion. Clients can hold multiple outstanding handles and await them in arbitrary order.
  • The peripheral endpoint exposes an accept method. It waits for complete transactions, returning decoded requests with payload buffers already assembled. Transactions respond directly with ok, err, or data results.

A serve helper allows multiple concurrent processes to share a peripheral endpoint. Worker processes claim incoming transactions as they become idle, returning responses as computation completes.

Protocol trackers

Two synthesizable hardware units manage protocol sequencing between endpoints:

  • AxiHost converts client requests into valid address phases. It allocates transaction identifiers, streams write data beats, and routes completed responses back to client handles.
  • AxiPer arbitrates read and write address channels into a unified request stream. It forwards write data beats to peripheral logic and formats responses into valid AXI beats.

Both trackers compile into clean synthesizable netlists. The standard five-channel bus exposed between them connects directly to AXI4 interconnect routers.

Identifiers remain reserved until the client collects the completed response. Reclaiming identifiers immediately upon response arrival can cause race conditions: if an identifier is reallocated before the client reads the previous response, both transactions risk colliding.

The address router

Parameterized routers (Router2 through Router8) connect a single host to multiple peripheral slaves. Each peripheral’s address range is configured via base and mask parameters encoded directly into the router type. The address map is part of the static type signature, producing optimized decode logic during lowering.

The router implements three critical protocol rules:

  • Strict write-beat ordering: because write data beats contain no transaction identifiers, they follow the oldest uncompleted write address phase. The router tracks active write targets, queuing secondary writes until the preceding transaction asserts last.
  • Atomic read bursts: the router selects a target peripheral at the first read beat and locks the route until the last flag asserts.
  • Unmapped address handling: access to unmapped addresses returns a DecErr response. For unmapped writes, the router drains payload beats before returning DecErr. For unmapped reads, it generates DecErr responses across the requested burst length.

Protocol bridges

Simpler peripherals do not require burst capability or concurrent transaction identifiers. The LiteBridge series translates full AXI4 bursts into sequential AXI-Lite single-beat transactions. A single burst can push an entire Ethernet packet to an AXI-Lite transmit FIFO register.

For existing open-source IP blocks, TxHDL provides AxiWb. AxiWb bridges AXI4 transactions to pipelined Wishbone host interfaces. Our board infrastructure uses this bridge to communicate with open-source DDR3 controllers.

Randomized bus fuzzing

Directed unit tests verify individual protocol scenarios under predefined timings. Randomized fuzz testing verifies protocol compliance across arbitrary edge cases and concurrent stalls.

The test harness places configurable delay relays across all fifteen internal channels. Each relay inserts pseudo-random stalls ranging from single-cycle delays to 47-cycle pauses. A randomized host issues mixed read and write bursts of one to sixteen beats, tracking up to four concurrent operations. A mock peripheral processes four requests concurrently with randomized response latencies, returning simulated errors across selected address ranges.

The test harness asserts several formal invariants:

  • Every issued burst receives exactly one response.
  • Read operations return the data most recently written to that address.
  • The last flag asserts on the final burst beat and nowhere else.
  • No transaction identifier is reissued while its response remains uncollected.
  • Execution completes within a bounded cycle limit.

Continuous integration runs 24 random seeds per commit, and nightly builds evaluate 1,024 seeds. Fuzz testing uncovered two critical concurrency bugs during initial bringup: channel contention between multi-worker peripheral processes, and head-of-line blocking in host identifier allocation. Both issues passed directed testing but failed under randomized bus contention.

Complete implementation code and testbenches are available in the AXI chapter of the TxHDL documentation, with sources in the repository.