A return statement within the context of a fork-join block is illegal and shall result in a compilation error.
For example:
task wait_20;
fork
# 20;
return ; // Illegal: cannot return; task lives in another process
join_none
endtask
(1) I am trying to understand the logical reason behind this restriction
I understand that there exist 2 child threads due to join_none and the parent thread is where task resides.
The same section further states
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
(2) I am not clear on the reason behind this restriction. Logically why is it that child threads can’t access arguments pass as ref ?
If I want to detect change in input signal within 3ns ( including change at exactly 3ns ),
I would try to define a task as ::
task automatic (ref int a);
bit change;
fork
begin:P
fork
begin:C1
#3ns;
end
begin:C2
@(a);
change = 1;
end
join_any
uvm_wait_for_nba_region();
disable fork;
if(change) `uvm_info(..)
end:P
join
endtask
(3) With the above restricton, how should one model the task ?
(1) This is because each process you create has an independent call stack. Every time you call a function or task, you push a context onto the stack. Returning or exiting from the call pops the stack. Your fork/join_none block creates two processes, each with its own stack. The return statement has nothing to return from. A better example might be:
task wait_20;
fork
# 20;
#100 return ; // Illegal: cannot return; the task has already returned
join_none
endtask
That return statement would attempt to pop a call context from the stack that doesn’t exist.
(2) This restriction is primarily intended to keep the LRM simple. There are many other restrictions on the usage of automatic variables. One significant issue arises from the requirement to ensure that the lifetime of an automatic variable is synchronized with the lifetimes of all processes that reference it. Tracking this synchronization through a formal reference argument becomes challenging, particularly given the task is unaware of the actual argument’s lifetime. The LRM 1800-2023 partially addresses this issue by introducing a new argument specification called static ref. This specification mandates that the actual argument you pass must have a static lifetime. By doing so, it eliminates all the restrictions associated with automatic variables inside tasks/functions
(2) you can model this with a simple fork/join
task automatic wait_3 (ref int a);
bit change timeout;
fork
begin:C1
#3ns timeout <=1;
end
begin:C2
@(a,timeout)
if (!timeout) change = 1;
disable C1;
end
join
if(change) `uvm_info(..)
endtask
This works as long as there is only one concurrent activation of this task.