[SystemVerilog] Difference between `wait(cb.signal == 1'b1)` and `@(cb iff cb.signal == 1'b1)`

Hi all,

Let’s say I want to write monitor for a simple valid/ready data streaming interface: “@ clock edge, the transaction is valid if both valid and ready are high.”
I have a clocking block defined as follows (we don’t drive anything this only monitors the interface.

clocking @(posedge clk);
input valid;
input ready;
input data;
endclocking

I can think of two ways to define the monitor task, and I’m wondering if there is any reason to prefer one or the other. Can anybody tell me if there is a difference between the following sequences?

  • option cb:
task monitor_cb(output data_t data);
  @(cb iff (cb.valid === 1'b1) && (cb.ready === 1'b1));
  data = cb.data;
endtask
  • option wait:
task monitor_cb(output data_t data);
  wait((cb.valid === 1'b1) && (cb.ready === 1'b1));
  data = cb.data;
endtask

Any inputs are greatly appreciated.
Cheers,
No

AFAIK, both do the same thing. Others can correct me if im wrong.

In reply to No:

I strongly suggest when you have a process using clocking block events, use only clocking events in that process, and no other event controls.

Although the LRM guarantees that clocking block input variables get updated before triggering the clocking block event, it says nothing about which region the input samples get updated. This is best illustrated if you have @(cb) as the next event control after thewait (cb.valid …).

module top;
  bit clk=1, v;
  clocking cb @(posedge clk);
    input v;
  endclocking
  always #5 clk= !clk;
  
  initial begin
    wait(cb.v) $display("wait(cb.v)",, $time);
    @cb $display("@(cb)",, $time);
    @(cb iff !cb.v) $display("@(cb iff !cb.v)",, $time);
    @(cb) $display("@(cb)",, $time);
    $finish;
  end
  
  initial begin
    #12 v = 1;
    #26 v = 0;
  end
endmodule

On some simulators you will see that the wait followed by @cb occurs in the same cycle, whereas @(cb) followed by @(cb) always comes comes in adjacent cycles.

In reply to dave_59:

Hi Dave,

I have tried to simulate example provided by you and below is result.
I wanted to understand below behavior.


  # wait(cb.v)              20
  # @(cb)                   30
  # @(cb iff !cb.v)         40
  # @(cb)                   50

  1. As, we know “wait” is level sensitive will be block until cb.v = 1 ( and will continue check ).
  2. Clocking block input will be stable( prepone region ) before clocking event.

So, as v is updated at #12. but as you mentioned which region sampled value will update for #20 clocking event is not define by 1800.
but , wait is level sensitive it will become true as soon as cb.v updated ( display come at #20 ).
So, in which condition below display will come at #30 instead of #20.


  # @(cb)                   30

as per me this behavior, due to “clocking event @(cb)” evaluate before “wait” for #20 time.
hence,@cb $display(“@(cb)”, $time); shift to next time clocking event.
is it correct understanding ?

Thanks!

In reply to harsh pandya:

This race is very similar to the following example

module top;
  bit a,b;
  initial begin
    wait(a)
    @(b)
    $display("done1");
  end
  initial begin
    #20 
    a = 1; 
    b = 1;
    $display("done2");
  end
endmodule

Because the two assignments to a and b execute in the same region, the SystemVerilog LRM section 4.7 Nondeterminsim allows the wait statement to proceed as soon as the first assignment to a occurs, or it could proceed after the display of “done2”. If it chooses the latter, the change to b has already happened and @(b) never resumes.

If I put a #0 between the two assignments, or use a non-blocking assignment to b, then @(b) is guaranteed to execute before the change to b, so “done1” will be displayed.

In reply to dave_59:

Thanks Dave. I got it.