The stage stores one whole beat: data, keep and last together. It adds one cycle of latency and never reorders or drops.
Throughput is one beat per cycle while the source offers and the sink accepts every cycle. Under a sink stall the stage holds its beat and deasserts ready.
Situation | Behavior |
|---|---|
Sink stalls while full | The stage holds its beat and drives |
Push and pop coincide | The arriving beat replaces the departing beat in the same cycle. Nothing is lost. |
Continuous flow | Each cycle moves one beat in and one beat out. The stage stays full. |
Latency | An offered beat appears at the output no earlier than the next cycle. |
Each slot is one clock cycle. A beat accepted in one slot (s_tvalid and s_tready both high) appears on m_tdata in the next slot, and it leaves at the end of a slot where m_tvalid and m_tready are both high.
Slot 1 (fill): the stage is empty, so s_tready is high and A0 is accepted. It appears on the output in slot 2: one cycle of latency.
Slots 2 and 3 (stall): the stage is full and the sink is not ready, so s_tready is low and the source holds B0.
Slot 4 (replace): m_tready rises. A0 leaves and B0 enters at the same edge, so s_tready is high even though the stage is full.
Slots 5 to 7 (flow, then drain): C0 follows B0 one cycle behind; with no new offer the stage empties and m_tvalid falls in slot 7.
A single full flag is the whole control. Ready is low only when the stage is full and nothing leaves.
The forward payload is registered, but the reverse ready path is combinational: m_tready -> pop -> s_tready. Chaining these stages chains ready logic and routing delay across them. This stage therefore does not cut reverse timing. Use the Skid Buffer and Pipeline Stages design when registered backpressure is needed, with storage for the in-flight beat. Target timing reports are still required for physical signoff.
The full listing sits with its testbench under Testbench below.
The push branch runs first, so a coincident arrival replaces the departing beat. That ordering is what keeps the stage lossless.
Both move in one cycle: the held beat leaves and the new beat is stored. The stage stays full.
s_tready is 0. The source must keep TVALID and the whole beat stable.
No earlier than the next cycle. The accepting edge samples it into the register first.
The register plus directed partial-beat stall/replacement checks, then a finite scoreboard over 4000 beats with varying TKEEP, random stalls on both sides, and TLAST every ninth beat. Inputs change on falling edges; transfers are counted at rising edges before registered updates. After all offers are issued the sink stays ready for a bounded drain. A cycle watchdog reports accepted, delivered and pending beats.
module axis_pipe #(
parameter int DW = 64,
parameter int KW = DW/8
) (
input logic clk,
input logic rst_n,
input logic s_tvalid,
output logic s_tready,
input logic [DW-1:0] s_tdata,
input logic [KW-1:0] s_tkeep,
input logic s_tlast,
output logic m_tvalid,
input logic m_tready,
output logic [DW-1:0] m_tdata,
output logic [KW-1:0] m_tkeep,
output logic m_tlast
);
logic full;
logic [DW-1:0] data_q;
logic [KW-1:0] keep_q;
logic last_q;
wire push = s_tvalid && s_tready;
wire pop = m_tvalid && m_tready;
assign s_tready = !full || pop;
assign m_tvalid = full;
assign m_tdata = data_q;
assign m_tkeep = keep_q;
assign m_tlast = last_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
full <= 1'b0;
data_q <= '0;
keep_q <= '0;
last_q <= 1'b0;
end else begin
if (push) begin
data_q <= s_tdata;
keep_q <= s_tkeep;
last_q <= s_tlast;
full <= 1'b1;
end else if (pop) begin
full <= 1'b0;
end
end
end
endmodule
Source checker, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. Directed partial-beat stall and replacement checks, then a finite scoreboard over 4000 beats with varying TKEEP, random stalls on both sides and TLAST every ninth beat, with a bounded drain and a cycle watchdog. It is not a liveness proof under arbitrary permanent backpressure.