Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
    • SRAM and ROM
    • Register Files
    • Valid/Ready Handshake
      • Rules That Keep Flow Control Safe
      • Skid Buffer and Pipeline Stages
      • AXI-Style Channels and Backpressure
      • Three Pipeline Stage Styles, Measured
    • AXI-Lite
    • AXI-Stream
    • Bus Integration
    • Synchronous FIFO
    • Register-Map and CSR RTL
    • Memory Reliability
    • Peripheral RTL Integration
    • Full AXI4: RTL Roadmap
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Valid/Ready Handshake
  1. Topics
  2. RTL Design
  3. Memory & Buses
  4. Valid/Ready Handshake
  5. Skid Buffer and Pipeline Stages

Part 7 · Memory & Buses · Intermediate

Skid Buffer and Pipeline Stages

A skid buffer that never drops a beat: two registers, a registered ready, and a torture test.

On this page

    Why this matters

    A registered stage learns about backpressure one cycle late, so the beat already leaving upstream has nowhere to go. A naive one-deep skid also drops the incoming beat when a pop and a push coincide.

    After this lesson you can: build a two-register skid buffer with a registered ready, and explain what happens on a push, a pop and a coincident pop and push.

    The late-ready problem

    A registered stage learns about backpressure one cycle late. Downstream deasserts ready, but the beat already leaving upstream has nowhere to go.

    A naive one-deep skid clears its hold flag on any pop. When a pop coincides with a push, the incoming beat has nowhere to go and is dropped. The design below makes that case impossible: its registered ready is low whenever both registers are full, so a push can never coincide with a full buffer.

    A skid buffer that never loses a beat

    Two registers hold at most two beats: an output register and a skid register. A registered ready reports not-full, so no combinational path runs from downstream ready to upstream ready.

    A push into a busy output lands in the skid. A pop with a full skid moves the held beat to the output. A pop that coincides with a push passes the new beat straight into the output, with no bubble. The full listing and testbench sit under Testbench below.

    Each slot is one clock cycle. Upstream lanes (valid, ready, data) and downstream lanes (m_valid, m_ready, m_data) follow one rule: a beat moves at the rising edge that ends a slot in which its valid and ready are both high.

    • Slot 0: 0xA1 is offered with ready 1, so it is pushed into the output register at the end of the slot.

    • Slot 1: 0xA1 is on the output, but downstream stalls (m_ready 0). 0xB2 is offered with ready still 1, so it is pushed into the skid register.

    • Slot 2: both registers are full, so the registered ready is 0. The new offer 0xC3 must stay put.

    • Slot 3: the stall holds; 0xC3 stays stable and no beat is lost.

    • Slot 4: downstream resumes (m_ready 1), so 0xA1 pops at the end of the slot and the held 0xB2 moves to the output.

    • Slot 5: ready is 1 again. 0xB2 pops while 0xC3 is pushed in the same edge, so 0xC3 goes straight to the output with no bubble.

    • Slot 6: no new offer; 0xC3 pops.

    Picking a stage

    Stage

    Ready path

    Throughput under backpressure

    Cost

    Pass-through register

    Combinational: ready flows through the stage

    One beat per cycle

    One data register; the chained ready path sets the timing limit.

    Plain registered stage

    Registered

    Half rate: it cannot take a beat while it holds one, so it accepts every other cycle even with no stall (measured 0.50 in the comparison lesson)

    One data register plus control.

    Skid buffer

    Registered

    One beat per cycle; stalls are absorbed

    Two data registers plus control.

    Common pitfall

    A one-deep skid that clears its hold flag on any pop drops the incoming beat when pop and push coincide. The design below registers its ready, so a push can never land in a full buffer. The testbench covers the coincidence first with directed beats, then with random traffic.

    Interview

    Likely question: “What breaks in a naive one-deep skid when pop and push coincide?”

    • Dropped beat. The hold flag clears on the pop while the new beat arrives.

    • Fix. Register the ready so a push can never coincide with a full buffer.

    Check yourself Downstream deasserts its ready without warning while a beat is in flight. Where does the beat go, and when does upstream hear about it?

    Into the skid register. Upstream hears about it one cycle later, when the registered ready falls, and that one cycle of lag is exactly what the extra register covers.

    Check yourself Pop and push coincide while only the output holds a beat. What happens to ready?

    The new beat passes straight into the output, the old one leaves, and ready stays high. No bubble and no skid detour.

    Testbench

    The testbench covers simultaneous pop/push, random backpressure, and directed shutdown with both slots full and an upstream offer blocked. Shutdown stops generating new offers, accepts the pending one, then drains within a bound. Input/output stability and accepted-minus-delivered occupancy are checked.

    skid_buffer.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    module skid_buffer #(
      parameter int DW = 8
    ) (
      input  logic          clk,
      input  logic          rst_n,
      input  logic          s_valid_i,
      output logic          s_ready_o,
      input  logic [DW-1:0] s_data_i,
      output logic          m_valid_o,
      input  logic          m_ready_i,
      output logic [DW-1:0] m_data_o
    );
      logic [DW-1:0] out_data, skid_data;
      logic out_valid, skid_valid, ready_q;
      logic next_out_valid, next_skid_valid;
      logic [DW-1:0] next_out_data, next_skid_data;
      wire push, pop;
    
      assign m_valid_o = out_valid;
      assign m_data_o  = out_data;
      assign s_ready_o = ready_q;               // registered: no combinational m_ready_i to s_ready_o path
      assign push = s_valid_i && ready_q;       // upstream hands a beat over
      assign pop  = out_valid && m_ready_i;     // downstream takes the output beat
    
      always_comb begin
        next_out_valid  = out_valid;
        next_skid_valid = skid_valid;
        next_out_data   = out_data;
        next_skid_data  = skid_data;
        if (pop) begin
          if (skid_valid) begin
            next_out_data = skid_data;          // held beat moves to the output
            if (push) next_skid_data = s_data_i;  // unreachable with registered ready, kept for safety
            else      next_skid_valid = 1'b0;
          end else if (push) begin
            next_out_data  = s_data_i;          // pass straight through, no bubble
            next_out_valid = 1'b1;
          end else begin
            next_out_valid = 1'b0;              // draining
          end
        end else if (push) begin
          if (!out_valid) begin
            next_out_data  = s_data_i;          // output empty: fill it directly
            next_out_valid = 1'b1;
          end else begin
            next_skid_data  = s_data_i;         // output busy: absorb into the skid
            next_skid_valid = 1'b1;
          end
        end
      end
    
      always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
          out_valid  <= 1'b0;
          skid_valid <= 1'b0;
          ready_q    <= 1'b1;
          out_data   <= '0;
          skid_data  <= '0;
        end else begin
          out_valid  <= next_out_valid;
          skid_valid <= next_skid_valid;
          out_data   <= next_out_data;
          skid_data  <= next_skid_data;
          ready_q    <= !(next_out_valid && next_skid_valid);
        end
      end
    endmodule
    

    Remember

    • Two registers hold at most two beats: an output register and a skid register.

    • Ready is registered and low whenever both are full, so a push never lands in a full buffer.

    • A pop that coincides with a push passes the new beat straight to the output with no bubble.

    Rule of thumb: register the ready, and give the in-flight beat a second register to land in.

    How this was checked

    Source checker, run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It covers simultaneous pop and push, random backpressure, and a directed shutdown with both slots full and an upstream offer blocked. Shutdown stops generating new offers, accepts the pending one, then drains within a bound; input and output stability and accepted-minus-delivered occupancy are checked. Timing closure and correctness for arbitrary traffic need separate evidence.

    Keep going

    • Practice: Two-stage valid/ready pipeline: throughput, backpressure and skid buffers

    • Code lab: Skid buffer

    • Interview: run a mock interview on this topic

    Related topics

    • AXI-Style Channels and Backpressure

    • Three Pipeline Stage Styles, Measured

    Previous

    199 / 350

    Next: AXI-Style Channels and Backpressure

    Up next

    Up nextAXI-Style Channels and Backpressure

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Three Pipeline Stage Styles, Measured
    clkvalidreadydataa1b2c3m_validm_readym_data0a1b2c3
    A skid buffer absorbing a late ready deassertion, then a same-cycle pop and push