Why is the @data not triggered on a value change?



// Code your testbench here
// or browse Examples
module tb;
  
  task my_first_task (input logic [31:0] addr, ref logic [31:0] data);
    
    data = 8'h8;
    
    for (int i=0; i <100; i++) begin
      $display ("Entering the loop");
    end
    
     for (int i=0; i <100; i++) begin
       $display ("Entering the second loop");
    end
   endtask
  
  logic [31:0] addr, data;
  
  initial  begin
  data = 'h10;
  addr = 'h01;
    fork 
      my_first_task (addr,data);
    
      begin
        @data;
        $display ("Read from bus %h \n",data);
      end join
  end
  endmodule

I dont see the @data getting triggered when the data is declared as ref.I understand the control will come back when the data is declared as output. Can any one tell me why the data change is not reflected?

In reply to rag123:

You have 2 forked tasks in parallel. The change in data is not detected in the begin-end task.
Try:


fork 
      my_first_task (addr,data);
 
      begin
        #1; 
        @ $changed(data);
        $display ("Read from bus %h \n",data);
      end join

Ben SystemVerilog.us

In reply to rag123:

This is a race condition. There is no guarantee if the assignment trigger, or the event control @data executes first. A non-blocking assignment would fix this particular example.

In reply to ben@SystemVerilog.us:

Hi Ben, I really frown upon putting in #1’s to fix race conditions.

In reply to dave_59:


Ben,
I dont see the code getting compiled in. which tool are u using?

Dave,
If i change it to non blocking, it is not compiling.

Error-[DTNBA] Dynamic type in non-blocking assignment
testbench.sv, 7
tb, "data"
  Dynamic type (Automatic variable in this case) cannot be used on the 
  left-hand side of non blocking assignments.

In reply to rag123:

The following worked. Some notes:

  • The task or function ‘my_first_task’ with ref arguments must be automatic.
  • data <= 8’h1; // non-blocking assignment may not be an automatic variable
  • my_first_task needs the #1; Love it or hate it, it’s the nature of the beast.

module tb;
    // The task or function 'my_first_task' with ref arguments must be automatic.
    task automatic my_first_task (input logic [31:0] addr, ref  logic  [31:0] data);
        #1; // <----------- NEEDED THiS
        data = 8'h1; // non-blocking assignment may not be an automatic variable
        
        for (int i=0; i <1; i++) begin
            $display ("Entering the loop");
        end
        
        for (int i=0; i <1; i++) begin
            $display ("Entering the second loop");
        end
    endtask
    
    logic [31:0] addr, data;
    
    initial  begin
        data = 'h10;
        addr = 'h01;
        fork 
            my_first_task (addr,data);
            
            begin                
                @ (data); // <-------- $changed requires a cloking event for the past of data
                $display ("Read from bus %h \n",data);
            end 
        join
    end
endmodule
 Entering the loop
# Entering the second loop
# Read from bus 00000001 
# 

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us
For training, consulting, services: contact Home - My cvcblr


  1. SVA Alternative for Complex Assertions
    Verification Horizons - March 2018 Issue | Verification Academy
  2. SVA: Package for dynamic and range delays and repeats | Verification Academy
  3. SVA in a UVM Class-based Environment
    SVA in a UVM Class-based Environment | Verification Horizons | Verification Academy

In reply to ben@SystemVerilog.us:
Typically, one uses clocking events. The following works, regardless of the order of the blocks in the fork-join


module tb;
    bit clk; 
    initial forever #10 clk=!clk;  
    task automatic my_first_task (input logic [31:0] addr, ref  logic  [31:0] data);
        @(posedge clk);
        data = 8'h2; // non-blocking assignment may not be an automatic variable
        
        for (int i=0; i <1; i++) begin
            $display ("@ %t Entering the loop ", $realtime);
        end
        
        for (int i=0; i <1; i++) begin
            $display ("Entering the second loop");
        end
    endtask
    
    logic [31:0] addr, data;
    
    initial  begin
        data = 'h10;
        addr = 'h01;
        fork 
                     
            begin
                // @(posedge clk);                 
                @ (data);
                $display ("@ %t Read from bus %h \n", $realtime, data);
            end 

            my_first_task (addr,data);
        join
    end
endmodule
 
# @                   10 Entering the loop 
# Entering the second loop
# @                   10 Read from bus 00000002 
# 
 

****NOTE
The following has a race condition

 
module tb;
    bit clk; 
    initial forever #10 clk=!clk;  
    task automatic my_first_task (input logic [31:0] addr, ref  logic  [31:0] data);
        @(posedge clk);
        data = 8'h2; // non-blocking assignment may not be an automatic variable
        
        for (int i=0; i <1; i++) begin
            $display ("@ %t Entering the loop ", $realtime);
        end
        
        for (int i=0; i <1; i++) begin
            $display ("Entering the second loop");
        end
    endtask
    
    logic [31:0] addr, data;
    
    initial  begin
        data = 'h10;
        addr = 'h01;
        fork 
                     
            begin
                @(posedge clk);      
                $display ("@ %t Before the @data %h \n", $realtime, data);           
                @ (data); // <<< Gets bloked here, waiting for a new change 
                $display ("@ %t Read from bus %h \n", $realtime, data);
            end 

            my_first_task (addr,data);
        join
        $display ("@ %t After the joins %h \n", $realtime, data);
    end
endmodule

@                   10 Entering the loop 
# Entering the second loop
# @                   10 Before the @data 00000002 



WIth the #1 as in 
@(posedge clk); #1; 
        data = 8'h2; 
It WORKS OK 

 @                   10 Before the @data 00000010 
# 
# @                   11 Entering the loop 
# Entering the second loop
# @                   11 Read from bus 00000002 
# 
# @                   11 After the joins 00000002 
#  

BTW, this forum does not discuss tools. One should ever identify the tool you are using. You can just say “my tool deos this”, but in the this, delete anything that can identify the tool. Your question is Not a tool issue, but a SystemVerilog issue.
Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us
For training, consulting, services: contact Home - My cvcblr


  1. SVA Alternative for Complex Assertions
    Verification Horizons - March 2018 Issue | Verification Academy
  2. SVA: Package for dynamic and range delays and repeats | Verification Academy
  3. SVA in a UVM Class-based Environment
    SVA in a UVM Class-based Environment | Verification Horizons | Verification Academy

In reply to ben@SystemVerilog.us:

Thanks Ben. I modified the post.

In reply to rag123:

See https://verificationacademy.com/forums/systemverilog/driving-wire-task-interface.
On using nonblocking assignment in tasks
Driving a wire thru a task is illegal.
Quote:
1800-2012 14.3 Clocking block declaration

  • A clockvar whose clocking_direction is inout shall behave as if it were two clockvars, one input and one output, having the same name and the same clocking_signal.
  • Reading the value of such an inout clockvar shall be equivalent to reading the corresponding input clockvar.
  • Writing to such an inout clockvar shall be equivalent to writing to the corresponding output clockvar.

interface dut_mem_if (input logic clk);
	wire[31:0] data;  // <------------------- the bi-direct
 
	clocking driver_cb @ (posedge clk);
		inout data;   // <------------------- the bi-direct 
	endclocking : driver_cb
endinterface : dut_mem_if 
 
class dut_mem_driver; 
	virtual interface dut_mem_if.drvr_if_mp  vif;
 // ...
	virtual task write_task(logic [31:0] data, address);
		this.vif.driver_cb.data <= data; 

In reply to ben@SystemVerilog.us:
Making the task NOT automatic and usi G nonblocking assignments works.

. 
// Code your testbench here
// or browse Examples
// Code your testbench here
// or browse Examples
module tb;
    bit clk; 
    initial forever #10 clk=!clk;  
    task  my_first_task ();
        // @(posedge clk);
        data <= 8'h2; // non-blocking assignment may not be an automatic variable
 
        for (int i=0; i <1; i++) begin
            $display ("@ %t Entering the loop ", $realtime);
        end
 
        for (int i=0; i <1; i++) begin
            $display ("Entering the second loop");
        end
    endtask
 
    logic [31:0] addr, data;
 
    initial  begin
        data = 'h10;
        addr = 'h01;
        fork 
 
            begin
                // @(posedge clk);                 
                @ (data);
                $display ("@ %t Read from bus %h \n", $realtime, data);
            end 
 
            my_first_task ();
        join
    end
endmodule

KERNEL: @ 0 Entering the loop

KERNEL: Entering the second loop

KERNEL: @ 0 Read from bus 00000002

KERNEL:

In reply to ben@SystemVerilog.us:

There are a number of issues at play here.
A task must have automatic lifetime in order to pass an argument by reference. That argument does not know the lifetime of the actual variable passed to it, so it places the most restrictive rules for assignments and does not allow non-blocking assignments. That was my mistake.

The original question exposes a race condition—there are a many ways of dealing with it. Without knowing the requirements behind what the code was trying to accomplish, it’s difficult to give a best solution.