HDLbits
Start Practicing

RTL Design Patterns/Handshaking

Valid/Ready Interface Basics

easy
handshakingvalid-readyprotocol

The valid/ready contract

Nearly every streaming interface in modern RTL — AXI-Stream, most internal bus protocols, and every pipeline in this track — uses some form of valid/ready handshaking. The rules are simple to state but easy to get subtly wrong:

  • The source asserts in_valid and drives in_data to indicate it has data to send.
  • The sink asserts in_ready to indicate it can accept data this cycle.
  • A transfer happens exactly on cycles where both in_valid and in_ready are 1. On any other cycle, no transfer occurs, no matter what in_data shows.
  • The source must hold in_data and in_valid stable once in_valid is asserted, until a transfer actually occurs (i.e. it may not “give up” and drop in_valid while the sink isn’t ready — that would be data loss, since the source can’t tell whether the sink already acted on it).
  • The sink may assert or deassert in_ready on any cycle, for any reason (e.g. its own internal buffer is full) — the source cannot assume anything about when in_ready will go high.

This problem is the simplest possible correct implementation of that contract: a wire-through with no registers and no buffering, where the block’s own ready is just a direct reflection of what’s downstream.

Interface

Signal Direction Description
in_data input Data from the upstream source.
in_valid input High when in_data is a genuine offer to transfer.
in_ready output High when this block can accept in_data this cycle.
out_data output Data forwarded to the downstream sink.
out_valid output High when out_data is a genuine offer to transfer.
out_ready input High when the downstream sink can accept out_data.

Correctness constraints

  • No data may ever be silently dropped: whenever in_valid && in_ready is true, that same data must appear on out_data with out_valid true in the same cycle.
  • out_valid must be low whenever in_valid is low — this block must never “invent” a transfer.
  • in_ready must faithfully reflect whether accepting data this cycle would actually be safe to forward — for this purely combinational block, that means in_ready simply equals out_ready, since there is no buffering to decouple the two sides.
  • Because there are no registers on the data path, this block introduces zero cycles of latency, but also zero cycles of decoupling — if out_ready is combinationally driven off logic that also depends on in_ready somewhere upstream, that’s a real timing hazard in a large design (motivating the registered stages in the next problems in this category).