Each channel is an independent valid/ready handshake: a transfer happens on a rising edge where both valid and ready are high. Write traffic uses three channels (AW, W, B) and read traffic uses two (AR, R).
Channel | Signals | Purpose |
|---|---|---|
AW |
| Carries the write address; the handshake accepts it |
W |
| Carries the write data with one strobe bit per byte lane |
B |
| Returns the write response after address and data are paired |
AR |
| Carries the read address; the handshake accepts it |
R |
| Returns the read data with its response |
AWPROT and ARPROT carry protection attributes such as privileged versus normal access. Simple slaves often tie them off or ignore them and decode the address only.
AW and W may arrive in any order: address first, data first, or both together. The slave latches whichever half arrives first and waits for the other; only the pair commits to a register.
While a channel asserts valid without ready, it must hold its payload stable: the address, data and strobes cannot change until the handshake edge. Counting transfers means counting valid && ready edges, not signal levels.
Each slot is one clock cycle, and slot numbers match the cycle numbers in the text. The AW xfer and W xfer lanes are valid && ready for that channel, so a high slot means the beat transfers at the edge that ends it. reg0 is the register the write targets.
Cycle 1: AW transfers. The address is held, but reg0 and BVALID do not move, because half a write is not a write.
Cycles 2 to 3: only AW has arrived. Nothing commits until W transfers at the end of cycle 3.
Cycle 5: this slave commits one edge after the second half is held, so reg0 takes its new value and BVALID rises together. The register never updates before cycle 3.
Cycles 5 to 6: BVALID stays high while BREADY is low. The write closes only when BREADY is high in cycle 7 and the B handshake completes.
A write ends with the B response and a read ends with the R beat. The master must wait for that closing handshake before treating the transaction as done, for example before reading back a status register it just wrote.
The minimal slave in this topic accepts one outstanding transaction per channel: it deasserts AWREADY and WREADY while a write response is pending, and ARREADY while read data is pending.
No earlier than cycle 3, after both halves are captured. The B handshake closes the write.
The stability rule. The slave may capture either the old or the new data, so the written byte lanes become unpredictable. Payload must hold until the handshake edge.
The bench below exercises the rules on this page with a write-only sketch of the pairing logic (the full slave, with reads and byte strobes, is on the next page). It sends AW first, W first, and both in the same cycle. While only one half is held it checks that nothing commits and B stays idle. Then it stalls B for two cycles and checks that BVALID and BRESP hold, that no new half is accepted while B is pending, and that each write takes exactly one AW, one W and one B handshake. A final write to an unimplemented offset must answer SLVERR and leave the register unchanged.
// A write-only sketch of the pairing rule. The full slave, with reads and
// byte strobes, is on the next page.
module axil_write_pair (
input logic clk,
input logic rst_n,
input logic awvalid,
output logic awready,
input logic [3:0] awaddr,
input logic wvalid,
output logic wready,
input logic [31:0] wdata,
output logic bvalid,
input logic bready,
output logic [1:0] bresp,
output logic [31:0] reg0
);
timeunit 1ns; timeprecision 1ps;
logic aw_seen, w_seen;
logic [3:0] awaddr_q;
logic [31:0] wdata_q;
// One write at a time: no new half is accepted while B is pending.
assign awready = !aw_seen && !bvalid;
assign wready = !w_seen && !bvalid;
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;
bvalid <= 1'b0;
bresp <= 2'b00;
reg0 <= '0;
end else begin
if (awvalid && awready) begin
aw_seen <= 1'b1;
awaddr_q <= awaddr;
end
if (wvalid && wready) begin
w_seen <= 1'b1;
wdata_q <= wdata;
end
if (aw_seen && w_seen && !bvalid) begin // pair first, then commit once
if (awaddr_q[3:2] == 2'd0) begin
reg0 <= wdata_q;
bresp <= 2'b00; // OKAY
end else begin
bresp <= 2'b10; // SLVERR: no register at this offset
end
bvalid <= 1'b1;
aw_seen <= 1'b0;
w_seen <= 1'b0;
end
if (bvalid && bready)
bvalid <= 1'b0; // the B handshake closes the write
end
end
endmodule
reg0 for two cycles after the AW handshake. Which slave bug does that catch?Committing on the address alone. A slave that writes when aw_seen is set, before w_seen, changes the register with stale data before W arrives, and the check fails.
Write-only sketch of the pairing logic, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It sends AW first, W first and both in the same cycle, stalls B for two cycles, and answers SLVERR for a write to an unimplemented offset.