Digital Design / Module 7: Memories and FIFOs
43. Valid/ready handshakes
Build backpressure that doesn't lose data.
See it move
The idea
When two blocks exchange data, each may need to pause. A valid/ready handshake handles this with two signals:
- valid from the sender means "the data is good".
- ready from the receiver means "I can take it".
A transfer happens on a clock edge if and only if both are high. If either is low, nothing moves.
The rules
- Once valid goes high, it must stay high, with data unchanged, until the transfer happens.
- Valid must not depend combinationally on ready. Ready may depend on valid, but avoiding that too is cleaner.
- Ready may rise and fall freely if no transfer is pending.
Breaking the first rule can lose or corrupt data. Breaking the second can create a combinational loop if both sides make the same assumption. AXI uses this handshake on all its channels.
A one-stage pipeline register
assign in_ready = !out_valid || out_ready;
always_ff @(posedge clk)
if (rst)
out_valid <= 1'b0;
else if (in_ready) begin
out_valid <= in_valid;
if (in_valid) out_data <= in_data;
endThe stage accepts new data if it is empty, or if its current word is leaving in this very cycle.
Throughput and timing
This design runs at full , but in_ready depends combinationally on out_ready. Chain many stages, and a long ready path forms all the way back through the . The lesson cuts that path.
Common mistake
Asserting ready only after seeing valid, and asserting valid only after seeing ready. Each waits for the other, so nothing ever happens. Make sure the rules allow at least one side to act first. Look at the animation above: if ready is seen a cycle late with no spare storage, words vanish.
Key ideas
- Transfer happens only when both valid and ready are high.
- Don't make valid depend combinationally on ready.
- Hold data stable until the transfer.
Your exercise
Find the bug
This code has one deliberate bug. Read the symptom, then click the line you think is wrong.
The fix
assign in_ready = !out_valid || out_ready;
Why it failed
A stage may accept new data only if it is empty, or if its current word is leaving in this same cycle. Always asserting ready overwrites a word that hasn't been taken, so the data is lost without any error. Random backpressure in the testbench exposes this quickly.
Interview questions
coreMay valid drop before a transfer?
In AXI-style rules, no: once asserted it stays until accepted.
basicIn a valid/ready handshake, when exactly does a word transfer?
On a clock edge where valid and ready are both high. If either is low at that edge, nothing moves, and the sender must keep presenting the same word. Neither signal alone means anything has happened: valid means the data is good, and ready means the receiver can take it.
basicA sender has three words and holds valid high from cycle 1. The receiver's ready over cycles 1 to 6 is 0, 1, 0, 1, 1, 1. In which cycles do the words transfer?
Word 1 transfers in cycle 2, the first cycle with ready high. Word 2 waits through cycle 3 and transfers in cycle 4. Word 3 transfers in cycle 5. In cycle 6, ready is high but valid is low, since the sender has nothing left, so nothing moves.
coreThis sender keeps valid high and steps its data every cycle. What goes wrong when the receiver applies backpressure?
always @(posedge clk)
if (rst) begin valid <= 0; data <= 0; end
else begin
valid <= 1'b1;
data <= data + 1; // ignores ready
endThe data advances whether or not the word was accepted, so any word offered while ready is low is replaced before it transfers, and it is lost. The rule is that the data must stay unchanged until the transfer: advance only when valid && ready. A stability check over random backpressure caught 47 violations in 100 cycles of simulation with this sender.
coreWhy must valid not depend combinationally on ready?
The receiver is allowed to make ready depend on valid. If the sender also makes valid depend on ready, the two form a combinational loop between the blocks. That can oscillate, or settle with both waiting for each other, and timing tools can't analyse it properly. The rule gives one side, the sender, the duty to act first, so the loop can't form.
coreA receiver raises ready only after it sees valid. A sender raises valid only after it sees ready. What happens?
Nothing ever transfers: each side waits for the other, which is a deadlock. In a simulation of this pair, there were 0 transfers in 50 cycles. Each design might look reasonable alone. The protocol has to state who may wait, and in valid/ready protocols it is the sender that must not wait for ready.
coreThis one-stage pipeline register should pass one word per cycle when out_ready is always 1. What does it actually do?
assign in_ready = !out_valid;
always @(posedge clk)
if (rst) out_valid <= 1'b0;
else if (in_ready) begin
out_valid <= in_valid;
if (in_valid) out_data <= in_data;
endOnce out_valid is 1, in_ready is 0, and the register updates only when in_ready is high. So out_valid can never clear: the stage accepts one word and then presents it forever. The receiver takes the same word every cycle. In simulation, the stage sent 99 transfers downstream while accepting only 1 word from the sender. The usual fix is in_ready = !out_valid || out_ready, which also gives full throughput.
advancedWrite a plain Verilog-2005 testbench check that flags a sender that changes its data, or drops valid, before the transfer.
always @(posedge clk) begin
if (!rst && pv && !pr && (!valid || data != pd))
$display("ERROR %0t: word changed before transfer", $time);
pv <= valid; pr <= ready; pd <= data;
endRemember last cycle's valid, ready and data. If last cycle had valid high and ready low, the word didn't transfer, so this cycle valid must still be high and the data unchanged. In simulation, this check found 47 errors in 100 cycles with the sender that steps its data every cycle. Running it on every valid/ready interface catches protocol bugs at the source.
advancedIs a receiver allowed to raise ready before valid arrives, and to lower it again without a transfer?
Yes to both, in AXI-style valid/ready rules. Ready may be raised early, which is good for throughput, and may be dropped while valid is low, because no transfer is pending. The asymmetry is deliberate: once valid is high, the sender has made a promise and must keep it until the transfer, while ready makes no promise. The receiver may also wait for valid before raising ready, since the sender is not allowed to wait for ready.