grant has a requestNo orphan responsePreparing questions and your practice workspace.
Preparing questions and your practice workspace.
Chapter 4 · Protocol patterns
Decompose handshakes, backpressure, bounded response, serialization, and ordering into small properties with precise diagnostics.
Curated and reviewed by industry engineersUpdated July 2026IEEE 1800 SystemVerilog reference

The language bench / assertions
assert property (@(posedge clk) disable iff (!rst_n)
$rose(req) |-> ##[1:3] ack);
// ACK must be sampled 1, 2, or 3 cycles after REQ rises.Question directory
Open a question at its supporting explanation. The complete property reference stays directly above each answer set.
Check causality, bounded response, stability, exclusivity, and ordering separately so a failure points to one broken rule instead of one dense property.
grant has a requestNo orphan responserequest reaches grantBounded latencyvalid and data holdBackpressure safeone response per IDNo loss or duplicationOne dense property can hide which obligation failed. Small contracts produce better diagnostics and compose cleanly.
Always-visible reference
These notes, tables, examples, and design limits contain the working knowledge for this chapter. The interview questions below are a checkpoint, not the primary explanation.
Protocol method
Every response must have a legal outstanding cause.
Each admitted request receives a response within the specified sampled window.
Control and payload remain stable while the receiver applies backpressure.
Each accepted request is consumed by exactly one matching response.
Recipe 01
a_req_one_cycle: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=> !req
);
a_ack_exact: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |-> ##2 $rose(ack)
);
a_ack_one_cycle: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(ack) |=> !ack
);
a_no_second_req: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=>
!$rose(req) until_with $rose(ack)
);
Recipe 02
a_req_granted: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |-> ##[1:3] $rose(gnt)
);
// Use ##[0:3] if same-edge grant is legal.
Recipe 03
| Operator | Does p include q sample? | Must q eventually occur? |
|---|---|---|
p until q | No | No: weak |
p until_with q | Yes | No: weak |
p s_until q | No | Yes: strong |
p s_until_with q | Yes | Yes: strong |
a_single_outstanding: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=>
!$rose(req) until_with $rose(gnt)
);
Recipe 04
a_hold_valid: assert property (
@(posedge clk) disable iff (!rst_n)
valid && !ready |=> valid
);
a_hold_payload: assert property (
@(posedge clk) disable iff (!rst_n)
valid && !ready |=> $stable(payload)
);
Recipe 05
| Sampled stall trace | Verdict | Reason |
|---|---|---|
S S S S S R | Pass | Five stalls, resolved on the sixth sample |
S S S S S S | Fail | The sixth consecutive stalled sample is illegal |
S S R | No failure | The five-sample antecedent never completes |
S S S ready=1 | No failure | Acceptance resolves the stall early |
sequence s_stall;
valid && !ready;
endsequence
a_stall_at_most_five: assert property (
@(posedge clk) disable iff (!rst_n)
s_stall[*5] |=> !s_stall
);
Recipe 06
| Protocol shape | Useful check | Limitation |
|---|---|---|
req held through gnt | $rose(gnt) |-> req | Only valid when request is still asserted |
one pulsed req, bounded grant | $rose(gnt) |-> recent request history | History does not consume the request |
single outstanding | Track outstanding bit | Cannot represent multiple requests |
K outstanding | Credit or pending counter | Counts do not prove identity |
tagged outstanding | ID set or scoreboard | Must handle reuse and duplicate responses |
Recipe 07
property p_matching_response;
logic [ID_W-1:0] saved_id;
@(posedge clk) disable iff (!rst_n)
($rose(req), saved_id = req_id)
|-> ##[1:8]
($rose(rsp) && rsp_id == saved_id);
endproperty
// For one-to-one or FIFO behavior:
// 1. push every accepted req_id into a reference queue
// 2. require rsp_id == queue.front() when FIFO is required
// 3. pop exactly once per accepted response
// 4. assert no response when the queue is empty
Interview checkpoint
Open a prompt after studying the reference above. A strong answer should name the sampled edge, match endpoint, failure mode, and proof evidence.
Split the contract into an exact response edge, response pulse shape, and admission rule. Separate properties produce clearer failures and let the interface decide whether a new request is legal on the acknowledgement edge.
Whether you decompose latency, pulse width, and serialization instead of assuming one implication proves all three.
A response-timing property alone does not reject an early extra acknowledgement or a second request.
a_req_one_cycle: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=> !req
);
a_ack_exact: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |-> ##2 $rose(ack)
);
a_ack_one_cycle: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(ack) |=> !ack
);
a_no_new_req: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=>
!$rose(req) until_with $rose(ack)
);
Trigger once on the request edge and allow a grant on any of the next three sampled edges: $rose(req) |-> ##[1:3] $rose(gnt). Use ##[0:3] if same-edge grant is legal. This checks latency, not transaction ownership.
Whether you can encode the deadline, decide if cycle zero is legal, and distinguish response timing from one-to-one accounting.
Changing |-> to |=> shifts each consequent; it does not prevent overlapping requests.
a_req_granted: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |-> ##[1:3] $rose(gnt)
);
// Use ##[0:3] if same-edge grant is legal.
Assert admission separately from response timing. $rose(req) |=> !$rose(req) until_with $rose(gnt) forbids a new request through and including the grant sample. Endpoint-excluding until permits same-edge readmission. Use a strong-until form when this property must also require grant eventually.
Whether you can separate admission, progress, endpoint policy, and outstanding capacity.
An implication operator alone does not serialize property attempts or consume a grant.
a_single_outstanding: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(req) |=>
!$rose(req) until_with $rose(gnt)
);
| Operator | Left side includes grant sample | Grant required |
|---|---|---|
until | No | No |
until_with | Yes | No |
s_until | No | Yes |
s_until_with | Yes | Yes |
When valid is sampled high and ready low, require valid to remain high and payload to remain stable on the next sampled clock. Repeating this one-step invariant on every stalled edge naturally holds the transaction until acceptance.
Whether you distinguish source obligations from sink progress and keep payload stable through the accepting sample.
Checking only valid misses data corruption while stalled. Requiring ready to rise is invalid unless the environment guarantees progress.
a_hold_valid: assert property (
@(posedge clk) disable iff (!rst_n)
valid && !ready |=> valid
);
a_hold_payload: assert property (
@(posedge clk) disable iff (!rst_n)
valid && !ready |=> $stable(payload)
);
Treat valid && !ready as the stall condition. If it is true for five consecutive sampled clocks, require it to be false on the next sample. A shorter stall never completes the five-cycle antecedent, so it creates no failure.
Whether you can count the allowed stalled samples and distinguish bounded progress from data stability.
Triggering only on valid can accidentally count cycles before backpressure begins or ignore a legal early valid withdrawal policy.
sequence s_stalled;
valid && !ready;
endsequence
a_stall_at_most_five: assert property (
@(posedge clk) disable iff (!rst_n)
s_stalled[*5] |=> !s_stalled
);
If request remains asserted through grant, gnt |-> req is enough. If request is a pulse and grant may arrive later, track outstanding state or check a bounded request history. The property must match the protocol's ownership model.
Whether you recognize when a temporal lookback is enough and when the protocol needs explicit state.
gnt |-> req falsely rejects a legal delayed grant when request is only a pulse.
logic [2:0] req_history;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) req_history <= '0;
else req_history <= {req_history[1:0], req};
end
a_grant_has_request: assert property (
@(posedge clk) disable iff (!rst_n)
$rose(gnt) |->
(req || |req_history)
);
A property-local variable can capture a request tag separately for every attempt and require a later matching response. That proves value correlation for each attempt, but it does not consume a response. Reused tags or overlapping attempts can let one response satisfy several obligations, so one-to-one and FIFO order need explicit state or a scoreboard.
Whether you understand attempt-local storage and the boundary between temporal correlation and transaction accounting.
first_match(##[1:$] gnt) does not prove FIFO order for multiple in-flight requests.
typedef logic [ID_W-1:0] tag_t;
property p_tagged_response;
tag_t saved_id;
@(posedge clk) disable iff (!rst_n)
($rose(req), saved_id = req_id)
|-> strong(1'b1 ##[1:8]
(rsp && rsp_id == saved_id));
endproperty
a_tagged_response:
assert property (p_tagged_response);
// Also enforce unique outstanding IDs, or use
// a scoreboard for one-to-one and FIFO order.