Autopsy of Vivado AXI-Lite Templates (2021.2 vs. 2025.2): Protocol Defects and Design Traps
When creating an AXI4-Lite peripheral with AMD/Xilinx Vivado’s Create and Package New IP wizard, Vivado generates a top-level wrapper and an AXI subordinate module. This article compares the archived 32-bit, four-register examples:
- Vivado 2021.2:
example_v1_0.vandexample_v1_0_S00_AXI.v. - Vivado 2025.2:
example.vandexample_slave_lite_v1_0_S00_AXI.v.
The analysis below refers to these specific subordinate snapshots; generated RTL can differ with Vivado version, wizard settings, and customization. Source line numbers below refer to the archived subordinate files listed above.
Nominal register reads and writes are only part of the evaluation. The channel handshakes also need to remain correct when address and data arrive in different cycles and when a manager applies response backpressure. Those cases expose important differences between these two generated snapshots.
aw_en-based ready logic; the 2025.2 snapshot uses explicit Idle, Waddr, and Wdata write states. Findings are attributed to the version in which the relevant code appears, not generalized across all Vivado releases. Verify the RTL emitted by your own version and configuration.1. The AMBA AXI4-Lite Protocol Contract
To evaluate any AXI subordinate, we must first establish the ground truth defined by the ARM AMBA AXI and ACE Protocol Specification (ARM IHI 0022H).
The five AXI4-Lite channels and the direction of their signals are:
| Channel | Manager to Subordinate | Subordinate to Manager |
|---|---|---|
| Write address (AW) | AWADDR, AWPROT, AWVALID | AWREADY |
| Write data (W) | WDATA, WSTRB, WVALID | WREADY |
| Write response (B) | BREADY | BRESP, BVALID |
| Read address (AR) | ARADDR, ARPROT, ARVALID | ARREADY |
| Read data (R) | RREADY | RDATA, RRESP, RVALID |
S_AXIThe Handshake Rules
- Independent Handshakes: A transfer on any channel completes strictly on the rising clock edge where both
VALIDandREADYare asserted:
$$\text{aw\_fire} = \text{AWVALID} \land \text{AWREADY}$$
$$\text{w\_fire} = \text{WVALID} \land \text{WREADY}$$ - Separate Write Channels (Section A3.4.1):
- The Write Address (
AW) and Write Data (W) channels have separate handshakes; the protocol does not require them to complete in the same cycle. - A subordinate must associate each accepted address with its corresponding accepted data, whether those handshakes arrive in either order or together.
- Response Prerequisite (Section A3.4.1):
- A subordinate must assert
BVALIDonly after bothaw_fireandw_firehave occurred for that transaction.
- A subordinate must assert
- No Speculative Response Dependencies (Section A3.4.4):
- A subordinate must not wait for the manager to assert
BREADYbefore assertingBVALID. The manager is permitted to wait forBVALIDbefore assertingBREADY.
- A subordinate must not wait for the manager to assert
- Early WREADY is Legal:
- Section A3.4.4 explicitly permits a subordinate to assert
WREADYbeforeAWVALIDorWVALID. However, earlyWREADYrequires the subordinate to safely buffer write data until the corresponding write address has been accepted.
- Section A3.4.4 explicitly permits a subordinate to assert
Now, let us examine how the generated template implements these rules.
2. Write-Channel Pairing: Different Behavior by Version
Dan Gisselquist’s formal analysis of an earlier Vivado AXI-Lite example is useful background, but it is not a substitute for checking later generated RTL. These archived 2021.2 and 2025.2 snapshots handle separated write channels differently.
Vivado 2021.2: AW and W acceptance are coupled
In the Vivado 2021.2 example_v1_0_S00_AXI.v, both ready signals are raised only when both valid signals are present; the register-write enable also requires both channel handshakes. The excerpts below are from ready generation (lines 141-205) and the write-enable assignment (line 217):
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
axi_awready <= 1'b1;
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en)
axi_wready <= 1'b1;
assign slv_reg_wren = axi_wready && S_AXI_WVALID
&& axi_awready && S_AXI_AWVALID;This snapshot does not consume a W-only transfer using a stale address: WREADY is held low until the address is also valid, and register writes are gated by both ready/valid pairs. The tradeoff is coupled acceptance and reduced flexibility: the implementation depends on both valid channels being presented and held until it can accept them together. Treat that as a version-specific integration constraint, not the 2025.2 stale-address defect described next.
Vivado 2025.2: W-before-AW can write to a stale address
The Vivado 2025.2 subordinate, example_slave_lite_v1_0_S00_AXI.v, initializes both ready signals high in its Idle state (lines 142-148):
axi_awready <= 1'b1;
axi_wready <= 1'b1;
state_write <= Waddr;Early WREADY is legal only if accepted write data is safely held until it can be paired with its address. In this snapshot, the register write block runs on S_AXI_WVALID and selects the current address only if S_AXI_AWVALID is also high (lines 213-215):
if (S_AXI_WVALID)
begin
case ( (S_AXI_AWVALID) ? S_AXI_AWADDR[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] : axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
// register cases follow
endcase
endIf WVALID arrives while AWVALID is low, WREADY can already be high, so the W transfer completes. The write block nevertheless uses the old axi_awaddr and writes the data to that stale register. The FSM’s Waddr state does not record the W-only handshake; if the manager drops WVALID after that handshake, the later address moves the FSM to Wdata, where it waits for data that has already been consumed. This can corrupt a register and prevent the intended write response (FSM lines 154-182).
The following trace illustrates this 2025.2-specific failure when the previous latched address targeted Register 0 and the next write targets Register 3:
Consider an interconnect pipeline where the write data path has lower latency than the write address path:
- Transaction 1 previously targeted Register 0. Consequently, the internal register
axi_awaddrretains address0x0. - Transaction 2 targets Register 3 with data
0xCAFEBABE. - In Cycle 2, the interconnect presents
S_AXI_WVALID = 1, butS_AXI_AWVALID = 0. Becauseaxi_wreadyis 1, a valid transfer occurs on the write data channel:
$$\text{w\_fire} = 1 \land 1 = 1$$ - At the rising edge of Cycle 2, the register write block executes:
if (S_AXI_WVALID)evaluates to true.- It decodes the target register using the ternary expression:
(S_AXI_AWVALID) ? S_AXI_AWADDR[...] : axi_awaddr[...] - Because
S_AXI_AWVALIDis 0, it falls back toaxi_awaddr. axi_awaddrstill holds the address of the PREVIOUS transaction (Register 0)!
- The fault:
slv_reg0is overwritten with0xCAFEBABE! - In Cycle 3, when
S_AXI_AWVALIDfinally arrives targeting Register 3, the write data handshake has already completed in the previous cycle, leaving the subordinate in an inconsistent state.
WDATA immediately using a stale address. A one-entry repair can hold AW and W independently and commit only after both have been accepted. AXI4-Lite has no transaction IDs, but that does not require every subordinate to allow only one outstanding request; multiple outstanding requests can be supported with ordered buffering.3. Read-Response Backpressure: 2021.2 vs. 2025.2
The Vivado 2021.2 example_v1_0_S00_AXI.v raises ARREADY whenever ARVALID is seen while its ready register is low; this logic does not check whether an earlier read response is still pending (lines 320-331):
if (~axi_arready && S_AXI_ARVALID)
begin
axi_arready <= 1'b1;
axi_araddr <= S_AXI_ARADDR;
end
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
endThe response-valid generation and read-data capture are at lines 351-398. When RVALID is held high because RREADY is low, additional read-address handshakes can still occur. The design has only one axi_araddr and one axi_rdata register, not a queue for those accepted requests; an extra accepted address can overwrite the pending address, while ~axi_rvalid prevents the corresponding response from being captured. The request acceptance must be gated while the one-entry response slot is occupied, or the implementation must add buffering.
The Vivado 2025.2 state_read FSM instead stays in Rdata until RVALID && RREADY, then re-enables ARREADY (lines 278-298). Its read side serializes requests and does not have this same acceptance-while-response-pending behavior.
4. Write-Response Backpressure: BVALID Is Not the Defect
The protocol requires a subordinate to assert BVALID only after accepting both parts of a write, and not to wait for BREADY before asserting it. Neither archived snapshot conditions the initial assertion of BVALID on BREADY; the earlier draft’s claim of a BVALID/BREADY dependency was incorrect.
The 2021.2 aw_en logic prevents a new write from being accepted until the current response is consumed (example_v1_0_S00_AXI.v, lines 141-153 and 286-300). In the 2025.2 FSM, the Waddr branch raises BVALID independently of BREADY and can keep AWREADY asserted while BVALID is pending. The following excerpt is from example_slave_lite_v1_0_S00_AXI.v, lines 154-168:
if (S_AXI_AWVALID && S_AXI_AWREADY)
begin
axi_awaddr <= S_AXI_AWADDR;
if(S_AXI_WVALID)
begin
axi_awready <= 1'b1;
state_write <= Waddr;
axi_bvalid <= 1'b1;
end
else
begin
axi_awready <= 1'b0;
state_write <= Wdata;
if (S_AXI_BREADY && axi_bvalid) axi_bvalid <= 1'b0;
end
endThe full FSM has no !axi_bvalid condition on that acceptance path and stores no response queue. If BREADY remains low and another write is accepted, a single pending BVALID cannot represent both responses. Thus, the 2025.2 concern is response accounting under backpressure and subsequent writes, not waiting for BREADY to assert BVALID. A single-outstanding implementation may instead hold both write-ready signals low until the response handshake (FSM lines 152-190).
5. The OPT_MEM_ADDR_BITS Address-Scaling Trap
When expanding the peripheral beyond the wizard’s default 4 registers, designers commonly add slv_reg4, slv_reg5, and so on. However, the template’s address decoding logic relies on a fixed localparam:
// Vivado 2021.2, lines 101-102; Vivado 2025.2, lines 100-101:
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;The register decode address slice is calculated as:
axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS : ADDR_LSB]For a 32-bit data bus (C_S_AXI_DATA_WIDTH = 32) in these snapshots:
ADDR_LSB = (32/32) + 1 = 2(ignoring byte-offset bits[1:0]).ADDR_LSB + OPT_MEM_ADDR_BITS = 2 + 1 = 3.- Therefore, the decoded bit slice is
axi_awaddr[3:2].
A 2-bit field ([3:2]) can only address $2^2 = 4$ registers (2'b00 through 2'b11).
The Aliasing Failure Mode:
If an engineer adds slv_reg4 at byte offset 0x10 (5'b1_0000), the bit slice [3:2] is 2'b00.
Because OPT_MEM_ADDR_BITS was not manually incremented:
- A write to Register 4 (
0x10) decodes as2'b00, silently overwriting Register 0 (0x00)! - A read from Register 4 returns the value of Register 0.
Requested Byte Offset: 0x10 (Binary: 0001_0000)
Decoded Bits [3:2]: 2'b00 (Routes to slv_reg0!)The archived default template has exactly four registers, so this is not an out-of-box defect in either snapshot. Aliasing occurs if the register bank is extended without also widening the decode and adding the new register cases. Neither snapshot derives this width from a register-count parameter or checks that the configured bank fits the decode (2021.2 write decode at line 231 and read decode at lines 372-378; 2025.2 write decode at lines 215-248 and read decode at line 303).
6. Limitation: No Application-Side Latency Contract
Both generated examples expose fixed register banks, not a variable-latency application-side interface. They provide no user-logic completion handshake for downstream memories or peripherals, and their AXI response logic has no configurable wait state for such logic.
In production FPGA systems, control and status interfaces frequently need to bridge to:
- Internal Block RAMs / UltraRAMs: Synchronous memories require 1 to 2 clock cycles of read pipeline latency.
- Clock Domain Crossing (CDC) FIFO / Synchronizers: Accessing registers in a different clock domain requires multi-cycle handshake synchronization.
- Slow Downstream Peripherals: Reading hardware counters, status flags, or SPI/I2C controllers requires variable-latency wait-states.
For example, replacing the register read mux with a synchronous memory without changing the AXI response timing can assert RVALID before the memory’s new data is available. Supporting variable downstream latency requires changing the response logic to wait for the application-side result; it is not just a register-array substitution.
7. System Policy: Timeouts and Bus-Hang Defense
Neither generated subordinate includes a transaction timeout or watchdog. This is not, by itself, an AXI protocol violation: timeout policy is a system-level choice.
Protocol vs. System Policy:
SLVERR. In an idealized AXI interconnect, a transaction remains active until both parties complete their handshakes.In an extended design, downstream logic that never completes can stall a transaction if custom interface logic propagates that wait to AXI. The snapshots analyzed here have no application-side wait handshake, so such a stall would come from added integration logic, not the generated register banks themselves. A system-level timeout or watchdog may be appropriate, but it must be placed where it can observe and terminate the stalled transaction.
- Error Response Ownership:
DECERR(2'b11): Typically generated by the interconnect when a transaction targets an address not mapped to any physical subordinate.SLVERR(2'b10): Generated by the subordinate when an access reaches a valid device, but the subordinate encounters an uncorrectable error, unaligned access, or internal timeout.
The Engineering Solution:
For interfaces that bridge to variable-latency or fallible downstream logic, integrating an optional hardware watchdog timer is an established industry best practice (seen, for example, in AMD’s own bridge IP cores like the AXI-to-Avalon MM Bridge PG258). If internal logic fails to respond within a parameterized threshold (e.g., 256 clock cycles), the subordinate terminates the transaction gracefully with SLVERR and records diagnostic status in a sticky register.
8. Maintainability: Protocol and Register Logic Share a Module
Finally, consider the maintainability of the generated RTL.
In both versions, the subordinate module combines AXI handshake logic, address decoding, example-register storage, and byte-strobe handling. The user-logic region is a placeholder; application logic added there will share the protocol module unless it is deliberately factored out. The archived filenames are example_v1_0_S00_AXI.v (2021.2) and example_slave_lite_v1_0_S00_AXI.v (2025.2).
Whenever an engineering team:
- Adds or removes register fields,
- Implements Write-1-to-Clear (W1C) or Read-Only (RO) registers,
- Adds interrupt status/mask registers, or
- Connects hardware signals,
they are forced to directly edit the file that manages AXI protocol timing. A single misplaced assignment can introduce bus handshake deadlocks that jeopardize the entire SoC.
Summary: Version-Specific Findings
| Design Aspect | Vivado 2021.2 snapshot | Vivado 2025.2 snapshot | Assessment |
|---|---|---|---|
| AW/W pairing | Ready and write-enable logic require both valid channels; no W-only stale-address commit | WREADY is high early and register storage writes on WVALID, selecting a stale address when AWVALID is low | 2021.2 couples acceptance; 2025.2 has a write-pairing defect |
| Read backpressure | Can accept more AR handshakes while a prior R response is stalled, with only one response slot | Read FSM waits for RVALID && RREADY before accepting another address | 2021.2 can lose accepted reads under backpressure |
| Write-response backpressure | aw_en blocks new writes until the B response handshake | Can keep accepting writes while one BVALID is pending; no response queue | 2025.2 can lose response accounting under backpressure |
| Address scaling | Fixed 2-bit register decode for the four generated registers | Same fixed 2-bit decode | Only becomes an aliasing trap if the bank is extended without changing decode |
| Application latency | No user-side completion/wait interface | No user-side completion/wait interface | Requires redesign or added bridge logic for variable latency |
| Timeout policy | No internal watchdog | No internal watchdog | System-level policy, not a protocol defect |
| Structure | Protocol and register logic share the subordinate file | FSM and register logic share the subordinate file | Factor application logic out when the design grows |
In the next article, we build our clean-slate replacement: starting with a compact, 100% compliant barebone slave, before advancing to the full-spec, decoupled architecture integrated into BitFlux.
References & Further Reading
- ARM AMBA AXI and ACE Protocol Specification (ARM IHI 0022H / IHI 0022E) — Specifically Sections A3.4.1 (Handshake Dependencies) and A3.4.4 (Channel Handshake Dependencies).
- AMD PG258: AXI-to-Avalon MM Bridge LogiCORE IP Product Guide — Demonstrating vendor use of timeout watchdogs to protect against downstream interface lockup.
- AMD UG1037: Vivado Design Suite AXI Reference Guide
- Dan Gisselquist (ZipCPU): Avoiding FPGA Hell — The seminal article on why cutting corners on bus interfaces and bench-only testing leads to unresolvable hardware lockups.
- Dan Gisselquist (ZipCPU): Using a Formal Property File to Verify an AXI-lite Peripheral — Formal analysis of an earlier Vivado-generated example. It provides historical context; the version-specific findings in this article come from the archived 2021.2 and 2025.2 subordinate snapshots named above.
