HDLbits
Start Practicing

RTL Design Patterns/Handshaking

Ready/Valid Pipeline Stage

medium
handshakingvalid-readypipeline-registerbubblestall

Why a registered stage is different from a passthrough

The Valid/Ready Interface Basics problem wires in_ready straight from out_ready with zero registers. That’s fine for glue logic, but real pipelines need actual flip-flops between stages to break long combinational paths and to let each stage operate somewhat independently. Once you add a register, the ready/valid logic has to account for what’s already sitting in that register: a transfer arriving this cycle isn’t necessarily the one this stage will present next cycle.

Interface

Identical to the combinational passthrough, plus clk/rst:

Signal Direction Description
clk/rst input Clock and synchronous, active-high reset.
in_data, in_valid, in_ready — Upstream side.
out_data, out_valid, out_ready — Downstream side.

The core logic

The stage holds one item in data_reg/data_valid. The key insight is the readiness equation:

in_ready = out_ready || !data_valid
  • If the stage is empty (!data_valid, a “bubble”), it can always accept new input — there’s nowhere for it to be blocked.
  • If the stage is full (data_valid), it can only accept new input if the downstream sink is also taking the current item this same cycle (out_ready) — because that guarantees the register will be free at the next clock edge, letting the stage refill and drain in the same cycle for full one-item-per-cycle throughput.
  • If the stage is full and out_ready is low, in_ready is correctly low: the stage must hold its data and stall upstream.

Cycle-by-cycle example

Cycle in_valid in_ready out_valid out_ready Note
0 1 1 0 – Stage empty, accepts A.
1 1 1 1 (A) 1 A drained, B accepted same cycle.
2 1 0 1 (B) 0 Downstream stalls; stage full, in_ready drops. C must wait.
3 1 0 1 (B) 0 Still stalled — B held, C still waiting.
4 1 1 1 (B) 1 Downstream resumes; B drains, C accepted.

Correctness constraints

  • No data may be lost or duplicated: an item transferred in (in_valid && in_ready) must eventually appear exactly once on out_data with out_valid.
  • While data_valid is high and out_ready is low, in_ready must be low (no silent overwrite of buffered data).
  • A “bubble” cycle (in_valid low) must propagate as out_valid low exactly one cycle later — it must not be confused with a stall.
  • This stage’s in_ready depends combinationally on out_ready (via the || !data_valid term) — that’s acceptable for a single stage, but chaining many of these back-to-back creates a long combinational ready path across the whole pipeline. The Skid Buffer problem in this category shows how to break that chain.