Argument Passed by Reference cannot be used within fork-join_any/join_none

Hi all,

When i were passing reference to tasks in fork-join_any/fork-join_none questasim throughing an Error.

This is my Env class.

class env;

static range_xform_types::d2rd_lut_t d2rd_coeff[range_xform_types::d2rd_lut_depth];//first array
static range_xform_types::d2r_lut_t d2r_coeff[range_xform_types::d2r_lut_depth]; //second array

task run();
fork
range_gen.run();
range_drv.run(d2rd_coeff,d2r_coeff);
range_mon.run();
range_sb.run(d2rd_coeff,d2r_coeff);
range_chk.run();
join_none
#1;
endtask

endclass :env


This is driver class.

class range_drv;

 task automatic run(ref range_xform_types::d2rd_lut_t d2rd_coeff[range_xform_types::d2rd_lut_depth],ref range_xform_types::d2r_lut_t  d2r_coeff[range_xform_types::d2r_lut_depth]);
   fork
   this.run_1(); 
   this.run_2(d2rd_coeff);***//line num 28.***   
   this.run_3(d2r_coeff);  ***//line num 29.***  
  join_none
 #1;
 endtask

task run_1();

endtask

task automatic run_2 (ref range_xform_types::d2rd_lut_t d2rd_coeff[range_xform_types::d2rd_lut_depth]);
   
endtask

task automatic run_3 (ref range_xform_types::d2r_lut_t  d2r_coeff[range_xform_types::d2r_lut_depth]); 

endtask

endclass :range_drv

** Error: …/tb/range_xform_driver.sv(28): (vlog-LRM-2295) Arguments passed by reference cannot be used within fork-join_any or fork_join_none blocks

** Error: …/tb/range_xform_driver.sv(29): (vlog-LRM-2295) Arguments passed by reference cannot be used within fork-join_any or fork_join_none blocks

To get more info a an error message use verror

eg verror 2295

# Message # 2295: # Within a fork-join_any or fork-join_none block, it shall be illegal to refer to formal # arguments passed by reference other than in the initialization value expression of variables # declared in a block_item_declaration of the fork. # # [DOC: IEEE Std 1800-2009 Verilog LRM - Section 9.3.2 "Parallel blocks"]

From page 159 of IEEE Std 1800-2009 SystemVerilog LRM

Variables declared in the block_item_declaration of a fork-join block shall be initialized to their initialization value expression whenever execution enters their scope and before any processes are spawned. Within a fork-join_any or fork-join_none block, it shall be illegal to refer to formal arguments passed by reference other than in the initialization value expressions of variables declared in a block_item_declaration of the fork.

So your code looks to be non-LRM compliant

In reply to Nigele:

Using static variables in not a good idea especially if they need to be passed around in your environment. Better to wrap them in a config class and pass the handle around.

In reply to Jonathan_Alvarez:

Jonathan,

This is an LRM restriction (See section Section 9.3.2 "Parallel blocks in the 1800-2012 LRM). The reason behind this restriction has to do with the fact that the lifetime of any variable referenced inside a fork/join_none/join_any block has to exist throughout the life of the fork block. Recall that there are these kinds variable lifetimes

  • Static - permanent and not an issue for this problem
  • Automatic - exists for the duration of a block activation
  • Dynamic - class object memory management by active references.
  • Queues, dynamic arrays, and associative array add another dimension to the above for each element.

The problem is when you pass variable by reference, you have no information about what kind of storage class the variable belongs to in order to be able to extend it’s lifetime. You only have the reference to a generic variable type that matches the type of the reference.

Suppose you have a task with a ref argument to an int, and you call that task passing it class member that is an int. The code that calls that task only only passes a reference to that int, and not the handle to the class it belongs to. same problem with passing an element of an array.

If the compiler in-lines the task (replacing the call to the task with the contents of the source code of the task) you can get around this restriction. But then you can’t take advantage of separate compilation( compiling the task definition in a separate step from compiling the code that calls the tak).

In reply to dave_59:

Hi Dave,

Thank you for the detailed explanation. I didn’t realized about that (the variable by reference have no information about the kind of storage). Besides, i think it is very informative (and well explained) what you have written about the kinds of variable lifetimes.
And thanks for the (indirect) explanation on how VCS may proceed to support that feature(compiler may do inline task replacement).

Thank you again Dave.
Best regards,
Jonathan

*In reply to Jonathan_Alvarez:*I want to add that I have no idea how other tools might support this feature. In-lining is just one way I came up with that I think would work. It’s also possible that the tool is not really supporting it and your code just happens to work because the actual lifetime of the variable passed by ref is at least as long as the process being forked.

In reply to dave_59:

Hi Dave,
what does this line mean - The problem is when you pass variable by reference, you have no information about what kind of storage class the variable belongs to in order to be able to extend it’s lifetime.

i have a task which has ref type argument of type int. i want to call this task inside fork join. i know the type of the argument is int.

Could you please explain it in a simpler way. Is there a work around for this?

In reply to twainerm:

I think it would be better to ask a new question with an example code showing what you’re trying to accomplish, especially what’s driving you to pass an argument by ref

In reply to dave_59:

Dave.
Wondering if there is a short workaround for this restriction? in my case I just need “const ref xxx”, unfortunately, I cant make major change of the testbench…


  // just demo
   task automatic monitor( const ref logic abc);
   fork
    begin 
      forever
        @(abc) do_something;
    end
    begin
      some_others;
    end
    join_any
endtask

monitor(xyz);