Each AXI channel has its own valid/ready transfer event. A stall on one does not itself transfer or cancel a beat on another, but shared capacity and transaction dependencies can legitimately couple progress. Channel independence does not guarantee that every other channel continues.
Channel | Payload | Valid driver | Ready driver |
|---|---|---|---|
AW (write address) | Address and protection | Master | Slave |
W (write data) | Data and strobes | Master | Slave |
B (write response) | Response | Slave | Master |
AR (read address) | Address and protection | Master | Slave |
R (read data) | Data and response | Slave | Master |
Each pair of arrows is one complete handshake. The solid arrow shows where valid and the payload go; the dashed one is that channel's ready, running the other way.
Down: the master drives valid on AW, W and AR, and the slave answers with ready.
Up: the slave drives valid on B and R, and the master answers with ready.
Separate handshakes: a stall on one pair does not by itself stall another. Shared buffers and transaction rules, such as B only after AW and W, couple them.
AXI4-Lite carries one beat per transaction, with no bursts and no IDs. Bursts, IDs and ordering belong to full AXI4 and are out of scope here.
Treat each channel as a small elastic pipeline. Beats enter whenever the next stage has room, and wait otherwise.
Registered ready keeps timing closed. A combinational ready would chain through the interconnect and limit the clock. A registered ready breaks the path, and the skid or FIFO behind it absorbs the extra cycle.
FIFOs decouple bursty ends. A FIFO between a fast producer and a slow consumer absorbs bursts up to its depth. Size the depth against the round-trip latency of the ready path.
Stages compose. Two skid stages in series still move one beat per cycle and still never lose one, as the testbench below shows.
No protocol rule requires it. AR and R can progress when their resources and transaction rules permit; shared capacity may legitimately throttle them. Independence alone promises no unconditional progress.
Two skid stages in series form a small elastic pipeline. The testbench drives random backpressure and checks that 128 beats arrive in order.
module skid_buffer #(
parameter int DW = 8
) (
input logic clk,
input logic rst_n,
input logic s_valid_i,
output logic s_ready_o,
input logic [DW-1:0] s_data_i,
output logic m_valid_o,
input logic m_ready_i,
output logic [DW-1:0] m_data_o
);
logic [DW-1:0] out_data, skid_data;
logic out_valid, skid_valid, ready_q;
logic next_out_valid, next_skid_valid;
logic [DW-1:0] next_out_data, next_skid_data;
wire push, pop;
assign m_valid_o = out_valid;
assign m_data_o = out_data;
assign s_ready_o = ready_q; // registered: no combinational m_ready_i to s_ready_o path
assign push = s_valid_i && ready_q;
assign pop = out_valid && m_ready_i;
always_comb begin
next_out_valid = out_valid;
next_skid_valid = skid_valid;
next_out_data = out_data;
next_skid_data = skid_data;
if (pop) begin
if (skid_valid) begin
next_out_data = skid_data;
if (push) next_skid_data = s_data_i; // unreachable with registered ready, kept for safety
else next_skid_valid = 1'b0;
end else if (push) begin
next_out_data = s_data_i;
next_out_valid = 1'b1;
end else begin
next_out_valid = 1'b0;
end
end else if (push) begin
if (!out_valid) begin
next_out_data = s_data_i;
next_out_valid = 1'b1;
end else begin
next_skid_data = s_data_i;
next_skid_valid = 1'b1;
end
end
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
out_valid <= 1'b0;
skid_valid <= 1'b0;
ready_q <= 1'b1;
out_data <= '0;
skid_data <= '0;
end else begin
out_valid <= next_out_valid;
skid_valid <= next_skid_valid;
out_data <= next_out_data;
skid_data <= next_skid_data;
ready_q <= !(next_out_valid && next_skid_valid);
end
end
endmodule
// Two skid stages in series: a minimal elastic pipeline.
module vr_pipe2 #(
parameter int DW = 8
) (
input logic clk,
input logic rst_n,
input logic s_valid_i,
output logic s_ready_o,
input logic [DW-1:0] s_data_i,
output logic m_valid_o,
input logic m_ready_i,
output logic [DW-1:0] m_data_o
);
logic mid_valid, mid_ready;
logic [DW-1:0] mid_data;
skid_buffer #(.DW(DW)) st0 (
.clk(clk), .rst_n(rst_n),
.s_valid_i(s_valid_i), .s_ready_o(s_ready_o), .s_data_i(s_data_i),
.m_valid_o(mid_valid), .m_ready_i(mid_ready), .m_data_o(mid_data)
);
skid_buffer #(.DW(DW)) st1 (
.clk(clk), .rst_n(rst_n),
.s_valid_i(mid_valid), .s_ready_o(mid_ready), .s_data_i(mid_data),
.m_valid_o(m_valid_o), .m_ready_i(m_ready_i), .m_data_o(m_data_o)
);
endmodule