A packet is the run of beats up to and including the beat with TLAST = 1. Packets can follow back to back with no gap.
TLAST alone ends nothing. Only an accepted beat, one with TVALID and TREADY both 1, can close a packet.
TKEEP marks which bytes of TDATA must be transported. Bit i covers byte i. This packed-packet example uses contiguous low bits on its partial final beat; general AXI-Stream also permits sparse masks and all-zero TKEEP, including a zero-byte TLAST transfer.
An 18-byte packet on a 64-bit bus needs three beats. The first two carry 8 bytes each, and the last carries bytes 16 and 17.
Beat | Bytes | TKEEP | TLAST |
|---|---|---|---|
0 | Bytes 0 to 7 |
| 0 |
1 | Bytes 8 to 15 |
| 0 |
2 | Bytes 16 and 17 |
| 1 |
Read each row as one accepted beat and each column as one byte lane. A filled cell is a byte the sink must keep; its TKEEP bit is 1.
Beats 0 and 1: all eight lanes carry data, so TKEEP is FF.
Beat 2: only lanes 1 and 0 carry data, bytes 17 and 16. TKEEP is 03 (binary 0000_0011) and TLAST marks the end.
Byte order: byte i of a beat sits in lane i, so packet byte 8 is lane 0 of beat 1.
The diagram shows that packet shape with a one-cycle stall on the middle beat.
Slot 1: idle, with tvalid 0 and tready 1.
Slot 2: the first beat fires, with tdata a0 and tkeep ff.
Slot 3: the second beat waits through a stall, with tready 0 and tdata held at b1.
Slot 4: the stall releases and the second beat fires.
Slot 5: the partial final beat fires, with tdata c2, tkeep 03 and tlast 1.
Slot 6: the link idles with tvalid 0 while tdata and tkeep hold their last values.
The monitor counts beats and packets. Both counters advance only on tvalid && tready, and a sticky flag records protocol violations.
The monitor flags unknown TKEEP on a transfer and violations of an outstanding stalled offer: VALID must stay asserted, and TKEEP, TLAST and the valid data bytes must hold through the acceptance edge. Null-byte data is ignored. All-zero TKEEP is legal; a transferred TLAST still closes the packet even when no bytes are carried.
The TKEEP test uses $isunknown, because an ordinary equality involving X can be treated as false by an if. Stall comparisons use !== to detect changes in four-state control and valid-byte data, including known-to-X changes. They do not establish known values: identical X data or TLAST held through a stall compares unchanged and passes. Only accepted TKEEP has an explicit unknown-value check here. Add separate known-value checks for meaningful data bytes and TLAST if the verification contract requires them.
The full listing sits with its testbench under Testbench below.
Beats 0 and 1 carry 8 bytes each with TKEEP 8'hFF and TLAST 0. Beat 2 carries bytes 16 and 17 with TKEEP 8'h03 and TLAST 1.
No. TLAST counts only on a transfer, so the packet ends when the final beat is accepted.
beat_count 5 and pkt_count 2. What does that mean?Five beats transferred, closing two packets. The testbench below checks exactly that case.
The monitor plus directed checks for packet counts, partial and zero-byte transfers, unknown KEEP, acceptance-edge payload changes, and withdrawn VALID. Each negative case starts from a reset monitor so its sticky error cannot hide another missed violation.
module axis_monitor #(
parameter int DW = 64,
parameter int KW = DW / 8
)(
input logic clk,
input logic rst_n,
input logic m_tvalid,
input logic m_tready,
input logic [DW-1:0] m_tdata,
input logic [KW-1:0] m_tkeep,
input logic m_tlast,
output logic [15:0] beat_count,
output logic [15:0] pkt_count,
output logic proto_err
);
wire fire = m_tvalid && m_tready;
logic stalled_q;
logic [DW-1:0] data_q;
logic [KW-1:0] keep_q;
logic last_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
beat_count <= '0;
pkt_count <= '0;
proto_err <= 1'b0;
stalled_q <= 1'b0;
data_q <= '0;
keep_q <= '0;
last_q <= 1'b0;
end else begin
if (fire) begin
beat_count <= beat_count + 16'd1;
if (m_tlast)
pkt_count <= pkt_count + 16'd1;
end
if (fire && $isunknown(m_tkeep))
proto_err <= 1'b1;
if (stalled_q) begin
if (m_tvalid !== 1'b1 || m_tkeep !== keep_q || m_tlast !== last_q)
proto_err <= 1'b1;
for (int b = 0; b < KW; b++)
if (keep_q[b] === 1'b1 && m_tdata[b*8 +: 8] !== data_q[b*8 +: 8])
proto_err <= 1'b1;
end
stalled_q <= m_tvalid && !m_tready;
data_q <= m_tdata; keep_q <= m_tkeep; last_q <= m_tlast;
end
end
endmodule