When two RTL modules need to exchange data, simply connecting a data bus between them is not enough. The receiving module may not always be ready to accept data, while the transmitting module may have new data available at a different time.
The ready-valid handshake protocol solves this problem with two control signals: valid and ready.
The source uses valid to indicate that it has valid data available, while the destination uses ready to indicate that it can accept the data. A data transfer takes place on a clock edge when both signals are high. This simple mechanism is widely used in RTL pipelines, streaming interfaces, and protocols such as AXI4-Stream.
For RTL engineers, understanding the ready-valid handshake is important because incorrect handling of these signals can result in dropped data, duplicated transactions, pipeline stalls, or even deadlock.
A ready-valid interface normally consists of:
A transfer occurs when:
valid = 1
ready = 1
at the active clock edge.
Conceptually:
Ready-Valid Interface
Source Destination
| |
|—— data ——————>|
|—— valid —————–>|
|<—– ready ——————|
| |
+—- Transfer when both = 1 —+
The important point is that neither valid nor ready alone means that a transfer has occurred.
For example:
VALID | READY | Transfer |
0 | 0 | No |
0 | 1 | No |
1 | 0 | No |
1 | 1 | Yes |
This behavior is also reflected in current AMD documentation for ready/valid interfaces, where a valid transfer occurs when both READY and VALID are high at the relevant rising clock edge.
Consider a producer that has a 32-bit data word to send.
The producer asserts:
valid = 1
data = 32’h12345678
The consumer is currently busy, so it keeps:
ready = 0
No transfer occurs.
The producer must keep valid asserted and maintain the corresponding data until the destination becomes ready.
When the consumer eventually asserts:
ready = 1
the next active clock edge completes the transfer.
Cycle VALID READY Transfer
————————————————
1 1 0 No
2 1 0 No
3 1 1 Yes
4 0 1 No
This is the basic mechanism behind ready-valid flow control.
The valid signal is controlled by the source.
When valid is high, the source is saying:
“I have a valid transaction available on the data bus.”
The source should not assert valid merely because it expects the destination to be ready.
Instead, valid should indicate that the source actually has a transaction to send.
One of the most important rules is:
The source must not wait for ready before asserting valid.
If both sides wait for the other to act first, the interface can deadlock. SystemVerilog.dev specifically identifies this as a common ready-valid design pitfall.
The ready signal is controlled by the destination.
When ready is high, the destination is indicating:
“I can accept a transaction.”
The destination may assert ready before valid arrives.
For example:
READY = 1
VALID = 0
does not transfer anything.
But if the source subsequently asserts valid:
READY = 1
VALID = 1
the transfer can occur on the next active clock edge.
This ability for either side to independently indicate its state makes the ready-valid protocol useful for modular RTL design.
Suppose the source asserts:
valid = 1
data = A
but the destination has:
ready = 0
The source must hold the transaction.
It should not change the data to B simply because the destination has not accepted A.
Conceptually:
Cycle VALID READY DATA
——————————————
1 1 0 A
2 1 0 A
3 1 0 A
4 1 1 A
The transfer occurs in Cycle 4.
After that transfer, the source can present the next transaction.
This rule prevents the receiver from sampling inconsistent or changing data.
For AXI-style interfaces, the same principle applies: once VALID is asserted, the associated transfer information must remain stable until the handshake completes.
A simple timing sequence can be represented as:
| 1 | 2 | 3 | 4 | 5 |
CLK |↑ |↑ |↑ |↑ |↑ |
VALID | 0 | 1 | 1 | 1 | 0 |
READY | 0 | 0 | 1 | 1 | 1 |
DATA | – | A | A | B | – |
^
Transfer A
At Cycle 3:
VALID = 1
READY = 1
Therefore, data A is transferred.
If the source has another transaction immediately available, it can keep VALID high and place the next data item on the bus.
This allows one transfer per clock cycle when both sides remain ready.
One of the biggest advantages of the ready-valid protocol is backpressure.
Backpressure occurs when a downstream module temporarily cannot accept more data.
For example:
Module A → Module B → Module C
If Module C becomes busy, Module B may eventually stop accepting data from Module A.
The ready signal propagates this condition backward.
Module A
|
v
Module B
|
v
Module C
|
X Busy
Module C can deassert ready.
Module B then stops accepting additional data when its available buffering is exhausted.
This prevents uncontrolled data flow and helps connect pipelines with different processing rates.
Ready-valid interfaces are therefore particularly useful in streaming and pipelined RTL architectures.
A simple SystemVerilog interface might look like this:
module data_source #(
parameter int WIDTH = 32
)(
input logic clk,
input logic rst_n,
output logic valid,
output logic [WIDTH-1:0] data,
input logic ready
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
valid <= 1’b0;
data <= ‘0;
end
else begin
if (!valid || ready) begin
valid <= 1’b1;
data <= next_data;
end
end
end
endmodule
The exact implementation depends on the behavior of the source, but the important principle is that the source retains the transaction while valid is high and ready is low.
The transfer condition can be expressed as:
assign transfer = valid && ready;
At every active clock edge where this condition is true, one transaction is accepted.
Consider this incorrect behavior:
Source:
if (ready)
valid = 1;
and:
Destination:
if (valid)
ready = 1;
Now both modules can wait indefinitely.
The source waits for ready.
The destination waits for valid.
Neither signal becomes active.
This is a classic deadlock scenario.
A safer design is:
Source:
valid depends on whether data is available
Destination:
ready depends on whether it can accept data
The two signals should be generated independently enough that the interface can make progress.
Another important RTL design issue occurs when valid and ready are connected through combinational dependencies.
For example:
Source VALID —> Destination READY
^ |
|____________________|
If the source calculates valid directly from ready, while the destination calculates ready directly from valid, the design can create a combinational loop.
This can lead to:
SystemVerilog.dev recommends avoiding such combinational loops in ready-valid interfaces and using registered signals or state-based logic where appropriate.
Ready-valid and request-acknowledge handshakes are related but should not be treated as identical protocols.
Feature | Ready-Valid | Request-Acknowledge |
Main signals | valid, ready | req, ack |
Typical use | Streaming data | Control/event transfer |
Data transfer | valid && ready | Depends on protocol |
Backpressure | Naturally supported | Can be implemented |
Common application | RTL pipelines, AXI streams | Control interfaces |
Ready-valid is especially effective when a design needs continuous data movement and flow control.
The ready-valid mechanism is strongly associated with AMBA AXI, particularly AXI4-Stream.
However, ready-valid is not limited to AXI.
It is a general RTL interface pattern that can be used whenever one synchronous module needs to transfer transactions to another while allowing the destination to control the flow rate.
AXI4-Stream uses the same fundamental idea: a transfer occurs when VALID and READY are asserted at the appropriate clock edge.
This is why understanding ready-valid signaling is useful before learning more complex AXI interfaces.
If:
VALID = 1
READY = 0
the source should not withdraw the transaction.
It should keep the transaction available until the destination accepts it.
Changing data while valid remains asserted and ready is low can cause the receiver to observe the wrong transaction.
This can introduce deadlock.
This can create problematic timing paths between connected modules.
It does not.
The transfer condition is:
valid && ready
A downstream module may temporarily stop accepting data. The upstream logic must handle this condition correctly rather than continuously overwriting or discarding transactions.
Ready-valid interfaces should be verified for both normal transfers and stalled conditions.
Important scenarios include:
A useful SystemVerilog assertion is to check that data remains stable when a transaction is waiting:
property valid_data_stable;
@(posedge clk)
disable iff (!rst_n)
valid && !ready |=> valid && $stable(data);
endproperty
assert property (valid_data_stable);
Inskill’s existing material on assertion-based verification also demonstrates checking valid-ready behavior using SystemVerilog Assertions, so this topic naturally connects to verification-oriented RTL learning.
The ready-valid handshake protocol is one of the most useful interface patterns for RTL engineers.
The concept is simple:
VALID = Source has a transaction
READY = Destination can accept it
Transfer = VALID && READY
But the implementation details matter.
When valid is high and ready is low, the source must continue presenting the same transaction. The source should not wait for ready before making valid active, because doing so can create deadlock. Designers should also avoid inappropriate combinational dependencies between ready and valid.
Once these rules are understood, ready-valid interfaces become much easier to apply to RTL pipelines, streaming data paths, AXI interfaces, DMA engines, FIFOs, and other SoC communication structures.
For an RTL engineer, mastering this protocol is less about memorizing two signals and more about understanding data stability, flow control, backpressure, throughput, and safe module-to-module communication.