In the minimal slave every valid, data and response output is registered, while every ready is combinational. AWREADY and WREADY decode the pairing flags and BVALID; ARREADY is simply !RVALID.
Each ready path decodes local registered state, with no combinational path from a channel input to its output here. That is a useful timing structure, but clock closure still requires target implementation and timing reports. A master must offer VALID without waiting for READY and hold VALID and payload until the handshake edge.
A new write half cannot be accepted while its response is pending: AWREADY and WREADY stay low until the B handshake clears BVALID. A new read address likewise waits until the R handshake clears RVALID.
Count the edges in the listing for the best case with BREADY and RREADY always high. A write needs a capture edge, a commit edge that raises BVALID, and a clearing edge before the next address is accepted: three edges per write. A read needs a capture edge and a clearing edge: two edges per read.
Each slot is one clock cycle. The master offers AW and W together and holds them, so VALID stands for both AWVALID and WVALID, and READY is AWREADY && WREADY. BREADY is high throughout. Call the edge that ends slot 0 edge E.
Slot 0: VALID and READY are both high, so the pair is captured at E.
Slot 1: READY drops because the pair flags are set. The commit happens at E+1.
Slot 2: BVALID is high and BREADY is high, so B is accepted at E+2.
Slot 3: READY returns and the second pair is captured at E+3. Three edges per write, not one.
The read strip uses the same rule with one fewer step. The address is captured at the end of slot 0, RVALID is high in slot 1 and clears against RREADY, and ARREADY returns in slot 2 for the next capture: two edges per read.
Three limits serialize everything: one outstanding transaction per channel, no staging of the next address or data beat while a response is pending, and the pairing wait when AW and W arrive cycles apart.
A faster AXI4-Lite slave can queue addresses and data, pipeline responses, and track several outstanding transactions without IDs. Responses must stay in order: pair queued AW and W entries in arrival order, retain each response until accepted, and return reads in accepted address order. This topic's minimal implementation deliberately accepts only one outstanding read and one outstanding write. IDs and response reordering are separate full-AXI features.
Limit | Cost in cycles | Fix (beyond minimal) |
|---|---|---|
AW/W pairing | Commit waits for the slower half | Accept halves into FIFOs so channels never block each other |
B before next AW | Three edges per write at best | Pipeline responses; track multiple outstanding writes |
R before next AR | Two edges per read at best | Queue read addresses and pipeline in-order responses; no IDs required |
BREADY is high throughout. A write pair is captured on edge 10. On which edge is the next write address captured at the earliest?Edge 13: edge 11 commits and raises BVALID, edge 12 clears it against BREADY, and the readies return only after that edge.
READY path pay off, and what does it cost?It pays off when the combinational ready fan-in grows too slow for the clock. It costs one cycle of handshake latency on that channel and a skid or FIFO to avoid dropping beats.
The slave again with sixteen writes then sixteen reads. AW and W are offered independently at the earliest falling edge after each acceptance, with BREADY/RREADY always high. The checker counts actual rising-edge handshakes and requires three-edge spacing for AW, W and B, and two-edge spacing for AR and R. Best-case trace: capture pair at E, commit after E+1, accept B at E+2, capture next pair at E+3.
module axilite_regs #(
parameter int AW = 8,
parameter int DW = 32
) (
input logic clk,
input logic rst_n,
input logic s_awvalid,
output logic s_awready,
input logic [AW-1:0] s_awaddr,
input logic s_wvalid,
output logic s_wready,
input logic [DW-1:0] s_wdata,
input logic [DW/8-1:0] s_wstrb,
output logic s_bvalid,
input logic s_bready,
output logic [1:0] s_bresp,
input logic s_arvalid,
output logic s_arready,
input logic [AW-1:0] s_araddr,
output logic s_rvalid,
input logic s_rready,
output logic [DW-1:0] s_rdata,
output logic [1:0] s_rresp
);
localparam [1:0] OKAY = 2'b00;
localparam [1:0] SLVERR = 2'b10;
if (DW != 32 || AW < 3) begin : g_bad_contract
$error("axilite_regs requires DW=32 and AW>=3 (four-byte register stride)");
end
logic [DW-1:0] reg0, reg1;
logic aw_seen, w_seen;
logic [AW-1:0] awaddr_q;
logic [DW-1:0] wdata_q;
logic [DW/8-1:0] wstrb_q;
logic [1:0] bresp_q, rresp_q;
assign s_awready = !aw_seen && !s_bvalid;
assign s_wready = !w_seen && !s_bvalid;
assign s_arready = !s_rvalid;
assign s_bresp = bresp_q;
assign s_rresp = rresp_q;
function automatic logic [DW-1:0] apply_wstrb(
input logic [DW-1:0] old_v,
input logic [DW-1:0] new_v,
input logic [DW/8-1:0] strb
);
logic [DW-1:0] tmp;
int i;
begin
tmp = old_v;
for (i = 0; i < DW/8; i++) begin
if (strb[i])
tmp[i*8 +: 8] = new_v[i*8 +: 8];
end
apply_wstrb = tmp;
end
endfunction
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
aw_seen <= 1'b0;
w_seen <= 1'b0;
awaddr_q <= '0;
wdata_q <= '0;
wstrb_q <= '0;
s_bvalid <= 1'b0;
bresp_q <= OKAY;
s_rvalid <= 1'b0;
rresp_q <= OKAY;
s_rdata <= '0;
reg0 <= '0;
reg1 <= '0;
end else begin
if (s_awvalid && s_awready) begin
aw_seen <= 1'b1;
awaddr_q <= s_awaddr;
end
if (s_wvalid && s_wready) begin
w_seen <= 1'b1;
wdata_q <= s_wdata;
wstrb_q <= s_wstrb;
end
if (aw_seen && w_seen && !s_bvalid) begin
case (awaddr_q[AW-1:2])
0: begin
reg0 <= apply_wstrb(reg0, wdata_q, wstrb_q);
bresp_q <= OKAY;
end
1: begin
reg1 <= apply_wstrb(reg1, wdata_q, wstrb_q);
bresp_q <= OKAY;
end
default: begin
bresp_q <= SLVERR;
end
endcase
s_bvalid <= 1'b1;
aw_seen <= 1'b0;
w_seen <= 1'b0;
end
if (s_bvalid && s_bready)
s_bvalid <= 1'b0;
if (s_arvalid && s_arready) begin
case (s_araddr[AW-1:2])
0: begin
s_rdata <= reg0;
rresp_q <= OKAY;
end
1: begin
s_rdata <= reg1;
rresp_q <= OKAY;
end
default: begin
s_rdata <= '0;
rresp_q <= SLVERR;
end
endcase
s_rvalid <= 1'b1;
end
if (s_rvalid && s_rready)
s_rvalid <= 1'b0;
end
end
endmodule
Source checker, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It runs sixteen writes then sixteen reads with BREADY and RREADY always high, counts rising-edge handshakes, and requires three-edge spacing for AW, W and B and two-edge spacing for AR and R. Physical timing signoff is a separate step.