Skid Buffer (Fully Registered Valid/Ready)

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.

The valid/ready handshake (recap)

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_*.

Specification

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.

  1. No combinational paths. 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.

  1. Outputs in every cycle:
  2. m_valid = 1 exactly when N ≥ 1, and then m_data is the oldest stored beat;
  3. s_ready = 1 exactly when N ≤ 1 (there is room for one more beat).
  4. Ordering. Every accepted beat is delivered exactly once, in order (no loss, duplication

or reordering). A beat accepted while the buffer is empty is offered on m_* in the very next cycle.

  1. Full throughput. It follows from rule 2 that with s_valid and m_ready both held at 1,

one beat transfers on each side in every cycle.

  1. Stability. It also follows that while m_valid && !m_ready, m_valid stays 1 and

m_data does not change.

  1. Reset. On a rising edge with rst = 1 the buffer becomes empty (N = 0): in the next cycle

m_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.

Example

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.

cycles_valids_datas_readym_validm_datam_readyNtransfers at end of cycle
11A10–10A in
21B11A01B in (skid)
31C01A02–
41C01A12A out
51C11B11B out, C in
61D11C11C out, D in
70–11D11D out
80–10–10

Sign up free to solve Skid Buffer (Fully Registered Valid/Ready) in your browser.

More Interfaces & Handshakes problems