# 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.
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.
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).
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).
*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.
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?
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
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