Task usage using Non-blocking assignment

I have a question :

reg [7:4]width,d1,d2;
 

  task t1(input [7:4]f,output reg [7:4]a);
    begin
      d1 <= f;
      a <= d1;
    end
  endtask


 always@(posedge clk)
   begin
     t1(width,d2);
   end

Observation : In the above code I find d2 gets the new value of width every 3 clock cycles.It should be every 2 clock cycles.

In reply to sush:

I have a question :

reg [7:4]width,d1,d2;
task t1(input [7:4]f,output reg [7:4]a);
begin
d1 <= f;
a <= d1;
end
endtask
always@(posedge clk)
begin
t1(width,d2);
end

Observation : In the above code I find d2 gets the new value of width every 3 clock cycles.It should be every 2 clock cycles.

It is correct behaviour only.The 3rd extra clock cycle is coming because of the always block which gets triggered on posedge of clk and then only the task t1 gets called and it consumes 2 clock cycles.

In reply to rishabhj:
The problem is that output arguments get copied by value upon exit from a task or function. But that is before the non-blocking assignment has a chance to update the ‘a’ argument. So each call to ‘t1’ copies the previous value of ‘a’ to ‘d2’.

What you need to do is make ‘a’ pass by reference instead of by value. Pass by reference also requires that the task have an automatic lifetime.

 task automatic t1(input [7:4]f,ref reg [7:4]a);
      d1 <= f;
      a <= d1;
  endtask

In reply to dave_59:

Dear Rishab,

Please find the code below executed as a .sv file.

reg [7:4]width,d1,d2;
	reg clk;
 

  task automatic t1(input [7:4]f,ref [7:4]a2  );
    begin
      d1 <= f;
      a2 <= d1;
    end
  endtask


 always@(posedge clk)
   begin
     t1(width,d2);
   end    

   initial
     begin
       clk = 1;
       width = 4'd6;
       forever #10 clk = ~clk;
     end

Observation : Error : LHS in non-blocking assignment may not be an automatic variable.

Please let me know if there is any mistake in my code.

In reply to sush:

Sorry, forgot about that restriction. Since the code inside the task does not know what the storage class of the variable that was passed to it by reference, you must assume it could have an automatic lifetime, and NBA are not allowed to automatic variables.

The only alternative is go back to the output argument and make a blocking assignment to it. But pass a temporary variable to it.

reg [7:4]width,d1,d2,t2;
 
 
  task t1(input [7:4]f,output reg [7:4]a);
    begin
      d1 <= f;
      a <= d1;
    end
  endtask
 
 
 always@(posedge clk)
   begin
     t1(width,t2);
     d2 <= t2;
   end

In reply to dave_59:

Hi Dave,
With Due Respect,
I have coded ram in verilog and used task with nonblocking statement and was getting 1 clock delay.After going through this link,i have used temp variable as you briefed,but that one clock delay is unaffected.Please find below the RAM verilog code:
module ram(clk,rst,wr,addr,data_in,data_out);

//you can change here and the program will work accordingly
parameter ADDR_WIDTH=9; //decide the address bits
parameter DATA_WIDTH=8; //decide the data bits
parameter memory_depth = 1 << ADDR_WIDTH; //by shifting like this,you can create memory of particular size.For eg :- here 2 raised to 10 memory is created i.e 1024 bits.

input [ADDR_WIDTH-1:0]addr; // [9:0] addr
input clk,rst,wr; // 1 bit ports
input [DATA_WIDTH-1:0] data_in; // [63:0] data_in
output reg [DATA_WIDTH-1:0] temp,data_out; //[63:0] data_out
integer i=0;

reg [DATA_WIDTH-1:0] ram[memory_depth-1:0]; //[63:0] ram [1023:0]

always@(posedge clk ,negedge rst)
begin
if (~rst ) //if reset is 0 it will set whole ram with initial predefined value
begin
reset; //reset is called to initialize the value of ram with the default value
temp <= 'd0;
data_out <= 'd0;
end
else
begin
if (wr==1) //if wr == 1 then write task will be called
write(addr,data_in);
else // (wr==0) //if wr == 0 then read task will be called
begin
read(addr,temp);
data_out <= temp;
end
end
end

task write(input [ADDR_WIDTH-1:0] addr,input [DATA_WIDTH-1:0] data_in); // by changing in parameter value of addr and data_in is updated
ram[addr] <= data_in; //passing data_in in particular addr of memory
endtask

// Read Task
task read(input [ADDR_WIDTH-1:0] addr,output [DATA_WIDTH-1:0] temp); //by changing in parameter value of addr and d_out is updated
temp <= ram[addr]; //particular data and this addr is updated in d_out
endtask

//reset task
task reset;
begin
$display(“memory_depth=%0d”,memory_depth);
for(i=0;i<memory_depth-1;i=i+1)
begin
ram[i]=0; //it will allot 0 to all the memory value
end
end
endtask
endmodule
Kindly suggest the solution.

Thanks in advance.

In reply to sush:

As Dave said output arguments get copied by value upon exit from a task or function. But that is before the non-blocking assignment has a chance to update the ‘a’ argument. So each call to ‘t1’ copies the previous value of ‘a’ to ‘d2’.

The behavior of three clock cycles is obvious as IEEE 1364-1995 Verilog standard says Evaluation of instance and primitive inputs fallowed by updates of primitive and instance outputs, and the evaluation of nonblocking RHS expression updates in the active region of stratified event queue.

As in your case, you have nonblocking assignment to the output argument of a task, but output argument will update in active region and LHS of your output argument will update in NBA region of event queue. This is why “d2” gets the new value of width every 3 clock cycles in your code.

Note: For analyzing this concept write one Test Bench and monitor all signals value updating including task argument/task variables, there you can easily see how “a” value is getting updated from “d1”.

Regard,
Sriram Y.