Autopsy of Vivado AXI-Lite Templates (2021.2 vs. 2025.2): Protocol Defects and Design Traps

Article 1 of 1 in The amba & axi masterclass • (Updated: Sep 27, 2026) • 12 min read
Autopsy of Vivado AXI-Lite Templates (2021.2 vs. 2025.2): Protocol Defects and Design Traps

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.v and example_v1_0_S00_AXI.v.
  • Vivado 2025.2: example.v and example_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.

Version Context: The 2021.2 snapshot uses 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:

ChannelManager to SubordinateSubordinate to Manager
Write address (AW)AWADDR, AWPROT, AWVALIDAWREADY
Write data (W)WDATA, WSTRB, WVALIDWREADY
Write response (B)BREADYBRESP, BVALID
Read address (AR)ARADDR, ARPROT, ARVALIDARREADY
Read data (R)RREADYRDATA, RRESP, RVALID
While newer Arm specifications adopt the terms Manager and Subordinate, the classic terms Master and Slave remain standard in RTL port naming, such as S_AXI

The Handshake Rules

  1. Independent Handshakes: A transfer on any channel completes strictly on the rising clock edge where both VALID and READY are asserted:
    $$\text{aw\_fire} = \text{AWVALID} \land \text{AWREADY}$$
    $$\text{w\_fire} = \text{WVALID} \land \text{WREADY}$$
  2. 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.
  1. Response Prerequisite (Section A3.4.1):
    • A subordinate must assert BVALID only after both aw_fire and w_fire have occurred for that transaction.
  2. No Speculative Response Dependencies (Section A3.4.4):
    • A subordinate must not wait for the manager to assert BREADY before asserting BVALID. The manager is permitted to wait for BVALID before asserting BREADY.
  3. Early WREADY is Legal:
    • Section A3.4.4 explicitly permits a subordinate to assert WREADY before AWVALID or WVALID. However, early WREADY requires the subordinate to safely buffer write data until the corresponding write address has been accepted.

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
  end

If 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:

  1. Transaction 1 previously targeted Register 0. Consequently, the internal register axi_awaddr retains address 0x0.
  2. Transaction 2 targets Register 3 with data 0xCAFEBABE.
  3. In Cycle 2, the interconnect presents S_AXI_WVALID = 1, but S_AXI_AWVALID = 0. Because axi_wready is 1, a valid transfer occurs on the write data channel:
    $$\text{w\_fire} = 1 \land 1 = 1$$
  4. 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_AWVALID is 0, it falls back to axi_awaddr.
    • axi_awaddr still holds the address of the PREVIOUS transaction (Register 0)!
  5. The fault: slv_reg0 is overwritten with 0xCAFEBABE!
  6. In Cycle 3, when S_AXI_AWVALID finally arrives targeting Register 3, the write data handshake has already completed in the previous cycle, leaving the subordinate in an inconsistent state.
2025.2 Defect Summary: The W channel can handshake before AW, but the write block commits 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;
  end

The 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
   end

The 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 as 2'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:

The ARM AXI specification does not mandate transaction timeouts or require every subordinate to self-terminate with 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 AspectVivado 2021.2 snapshotVivado 2025.2 snapshotAssessment
AW/W pairingReady and write-enable logic require both valid channels; no W-only stale-address commitWREADY is high early and register storage writes on WVALID, selecting a stale address when AWVALID is low2021.2 couples acceptance; 2025.2 has a write-pairing defect
Read backpressureCan accept more AR handshakes while a prior R response is stalled, with only one response slotRead FSM waits for RVALID && RREADY before accepting another address2021.2 can lose accepted reads under backpressure
Write-response backpressureaw_en blocks new writes until the B response handshakeCan keep accepting writes while one BVALID is pending; no response queue2025.2 can lose response accounting under backpressure
Address scalingFixed 2-bit register decode for the four generated registersSame fixed 2-bit decodeOnly becomes an aliasing trap if the bank is extended without changing decode
Application latencyNo user-side completion/wait interfaceNo user-side completion/wait interfaceRequires redesign or added bridge logic for variable latency
Timeout policyNo internal watchdogNo internal watchdogSystem-level policy, not a protocol defect
StructureProtocol and register logic share the subordinate fileFSM and register logic share the subordinate fileFactor 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

  1. 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).
  2. AMD PG258: AXI-to-Avalon MM Bridge LogiCORE IP Product Guide — Demonstrating vendor use of timeout watchdogs to protect against downstream interface lockup.
  3. AMD UG1037: Vivado Design Suite AXI Reference Guide
  4. 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.
  5. 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.