Hard · Interfaces & Handshakes · Pipelining
Implement a skid buffer: a fully registered pipeline slice for a valid/ready stream.
In the Valid/Ready Pipeline Register problem, s_ready = !m_valid || m_ready is combinational, so the ready signal still travels through the slice without a register. In a long pipeline the ready chain becomes the critical path. A skid buffer registers every output, including s_ready, while still moving one beat per cycle.
The trick: because s_ready is a register, it can only react to m_ready one cycle late. When the downstream suddenly stalls, the buffer has already promised to accept one more beat. That beat "skids" into a second storage register. So a skid buffer needs two entries of storage.
A transfer happens on a rising edge of clk where both valid and ready are 1. A sender that raises valid must hold valid and data unchanged until the transfer happens; the receiver may change ready at any time. Your module is a receiver on s_* and a sender on m_*.
module skid_buffer #(
parameter int WIDTH = 8
) (
input logic clk,
input logic rst, // synchronous, active-high
input logic s_valid, // upstream side
output logic s_ready,
input logic [WIDTH-1:0] s_data,
output logic m_valid, // downstream side
input logic m_ready,
output logic [WIDTH-1:0] m_data
);
Let N be the number of beats stored: beats accepted on s_* (at earlier rising edges) that have not yet been delivered on m_* (at earlier rising edges). N is 0, 1 or 2.
s_ready, m_valid and m_data must be driven by flip-flops(or depend only on flip-flops): they may change only right after a rising edge of clk, never because s_valid, s_data or m_ready changed. In particular there must be no path from m_ready to s_ready.
m_valid = 1 exactly when N ≥ 1, and then m_data is the oldest stored beat;s_ready = 1 exactly when N ≤ 1 (there is room for one more beat).or reordering). A beat accepted while the buffer is empty is offered on m_* in the very next cycle.
s_valid and m_ready both held at 1,one beat transfers on each side in every cycle.
m_valid && !m_ready, m_valid stays 1 andm_data does not change.
rst = 1 the buffer becomes empty (N = 0): in the next cyclem_valid = 0 and s_ready = 1. Stored beats are discarded. The data registers do not need a reset.
s_data is ignored when s_valid = 0 (the tests drive garbage there), and m_data is not checked when m_valid = 0. The tests' sender follows the handshake rules.
The hidden tests check the outputs every cycle against a model of rule 2, and they also wiggle the inputs between clock edges to make sure no output reacts to them.
The downstream stalls in cycles 2–3. Because s_ready cannot drop in the same cycle, beat B is still accepted in cycle 2 and parks in the second register (N = 2), so s_ready is 0 in cycles 3–4. When m_ready returns in cycle 4, A leaves, B moves up, and s_ready comes back one cycle later.
| cycle | s_valid | s_data | s_ready | m_valid | m_data | m_ready | N | transfers at end of cycle |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | A | 1 | 0 | – | 1 | 0 | A in |
| 2 | 1 | B | 1 | 1 | A | 0 | 1 | B in (skid) |
| 3 | 1 | C | 0 | 1 | A | 0 | 2 | – |
| 4 | 1 | C | 0 | 1 | A | 1 | 2 | A out |
| 5 | 1 | C | 1 | 1 | B | 1 | 1 | B out, C in |
| 6 | 1 | D | 1 | 1 | C | 1 | 1 | C out, D in |
| 7 | 0 | – | 1 | 1 | D | 1 | 1 | D out |
| 8 | 0 | – | 1 | 0 | – | 1 | 0 |
Sign up free to solve Skid Buffer (Fully Registered Valid/Ready) in your browser.