A transfer happens when valid and ready are high together. The source owns valid and the payload, and the sink owns ready.
Rule | Meaning |
|---|---|
Valid waits for ready | Once asserted, valid stays high until a handshake. |
Payload waits with valid | Data stays stable while valid is high and ready is low. |
Ready may fall anytime | The sink can delay acceptance. The source tolerates the wait. |
Two assertions state the rule. They sit in a snippet so the bench runs on simulators without concurrent-assertion support (for example Icarus). The testbench below checks the same conditions procedurally.
// snippet
`timescale 1ns/1ps
// Stable-payload rules for a valid/ready source (checked procedurally in the testbench):
// assert property (@(posedge clk) valid_o && !ready_i |=> valid_o);
// assert property (@(posedge clk) valid_o && !ready_i |=> $stable(data_o));Watch data against valid and ready. The payload changes while the first offer is still waiting.
Slot 1: idle: valid is 0 and data is 0.
Slot 2: the source offers a5 with valid high and ready low.
Slot 3: ready is still low and the offer holds.
Slot 4: data changes to 3c while ready is still low: the violation.
Slot 5: ready rises and the transfer completes with the wrong payload.
Slot 6: valid drops and the bus is idle.
The testbench samples the previous cycle and flags any change under valid without ready. It counts exactly one violation for the wave above, then proves a fixed source transfers two payloads cleanly.
The $stable(data) rule fires. When ready rises later, the sink receives the new data, and the original payload is lost.
Yes. The accepted offer retires on that edge, and the following cycle is idle. Keeping the old offer asserted through another ready-high edge would accept a duplicate; the fixed source below clears valid on acceptance.
A violating driver and a fixed two-item source. Acceptance is sampled before register updates. The fixed source is checked with continuous READY, isolated READY pulses and a long initial stall, so an extra accepted copy cannot hide behind one-cycle ready pulses.
`timescale 1ns/1ps
module hs_fixed_src (
input logic clk, rst_n, ready,
output logic valid,
output logic [7:0] data
);
logic idx = 1'b0, done = 1'b0;
always_ff @(posedge clk) begin
if (!rst_n) begin valid <= 1'b0; data <= 8'd0; idx <= 1'b0; done <= 1'b0; end
else if (!valid && !done) begin valid <= 1'b1; data <= (idx == 1'b0 ? 8'hA5 : 8'h3C); end
else if (valid && ready) begin
valid <= 1'b0; // retire the offer on its accepting edge
if (idx == 1'b0) idx <= 1'b1;
else done <= 1'b1;
end
end
endmodule