Fork join_none inside for loop

Hi
I want to spawn multiple threads with each thread having an index input argument like

for (int i = 0;i < 5;i++) begin
  fork
    thread(i)
  join_none
end

wait_fork;

But since i is a common variable for all threads, all threads are spawned with index 4.
I don’t want to use join_all as i want all threads to be spawned simultaneously.

Please help out.

This is explained in the LRM. See the last example in section 9.3.2 Parallel blocks that explains fork/join_none.

Try this…

for(int i = 0; i < 5; i++) begin
  automatic int j;
  j = i;
  fork
   thread(j);
  join_none
end


In reply to malathi@aceic.com:

Thanks. It works.

What does this do?
Is the normal function call, pass by reference and by making the variable automatic are you making it pass by value.

In reply to arun_ajs:

What makes it work is that for each iteration of the for loop, a local automatic variable is created with a lifetime that is extended by the lifetime of the fork/join_none block that references it. The statements inside the fork/join_none block begin execution after finishing the for loop. It doesn’t mater if the function/task call passes its arguments by value or by reference; each call has an independent copy of the automatic variable that was set to a value as the for loop went through its iterations.

In reply to dave_59:

Thanks!
Makes sense now :)

In reply to dave_59:

In reply to arun_ajs:
What makes it work is that for each iteration of the for loop, a local automatic variable is created with a lifetime that is extended by the lifetime of the fork/join_none block that references it. The statements inside the fork/join_none block begin execution after finishing the for loop. It doesn’t mater if the function/task call passes its arguments by value or by reference; each call has an independent copy of the automatic variable that was set to a value as the for loop went through its iterations.

I have a question here.

Lets say in the above example:

for(int i = 0; i < 5; i++) begin
  automatic int j;
  j = i;
  fork
    thread(j);
  join_none
end

I made the following change:

for(int i = 0; i < 5; i++) begin

fork
automatic int j;
j = i;
thread(j);
join_none
end

This work fine too, but in case if I add begin-end around fork - join_none, it doesn’t work fine. Threads are spawned with 4.
Can you please what is the difference here.

Thanks
Subramaniam

In reply to ssubramaniam1990@gmail.com:
The change you show should not work, and introduces a race condition. You are now spawning two independent threads inside the fork/join_none. The two statements

j = i;
thread(j);

execute in parallel, so there is no guarantee that thread(j) gets the updated value of j. Also, the value of i at the point when j = i gets executed should be 5, for all iterations.

In reply to ssubramaniam1990@gmail.com:

In reply to dave_59:

In reply to ssubramaniam1990@gmail.com:
The change you show should not work, and introduces a race condition. You are now spawning two independent threads inside the fork/join_none. The two statements

j = I;
thread(j);

execute in parallel, so there is no guarantee that thread(j) gets the updated value of j. Also, the value of i at the point when j = i gets executed should be 5, for all iterations.

Adding code tags:
Hi Dave,

You are right. I should have shown my code previously.
Here is my code:

program automatic fork_join;

  initial begin
    for(int i = 0; i < 16; I++)
      begin
        fork 
         // begin
	      int index =I  ;
	      send(index);
         // end 
    	join_none 
      end
      wait fork;
  end
   
   task send(int j);
     temp();       
     $display("driving port %0d %t" , j, $realtime);
   endtask // send

endprogram

I am trying 3 different cases:

  1. Executing the above program, I can see the prints “Driving port 0-16”.
  2. In the fork block doing something like this:
        fork 
         // begin
	        int index ;
            index = I  ;
	        send(index);
         // end 
	    join_none

With this change Driving port 16 printed 16 times.

  1. Adding begin-end around fork - join_none:
        fork 
          begin
	        int index ;
            index = I  ;
	        send(index);
          end 
	    join_none

Again I see driving port16 being printed 16 times.

Can you please explain what is the difference between the 3 cases.

Thanks
Subramaniam

In reply to ssubramaniam1990@gmail.com:
The differences are in the lifetime of the variable index, and the timing of when that variable gets assigned with value of the variable i.

Realize that there will be 16 concurent variables named index and only one named i. In case 1) & 2), the index variable gets created upon each entry into the fork/join_none block. That occurs before spawning any process within the fork/join_none. In case 1) the variable initialization also occurs before any process within the fork/join_none. The thing that you need to remember is that automatic variables get created upon entry and initialized before executing any procedural statement within the block they are located in. So in case 1), each index variable gets the current value of i in each loop iteration.

In case 2), you moved the initialization into a separate procedural assignment statement. Each statement within a fork/join_none becomes as new child process and execution of the child process does not begin until the current parent thread suspends. The for loop now spawn 32 threads before suspending at the wait forkstatement when the value of i is 16. (and as I said before, it is a race if send(index) sees the unitialized value 0 or assigned value 16.

In case 3), the index variable is now declared inside a begin/end block, which is a single statement for the fork/join_none. So now the index variable does not get created until all 16 processes have been spawned, and the value of i is 16.

In reply to dave_59:

Thank you Dave for the detailed explanation.

In reply to ssubramaniam1990@gmail.com:

Thanks Dave and Subramaniam. All 3 cases were helpful.

Thanks Dave and Subramaniam the concept of fork join_none was very useful.

In reply to dave_59:

Hi Dave ,
For The Following Code


for( int j = 1; j <= 3; ++j )
 fork

 begin
  
  automatic int m = j; // the value of m is undetermined
  #k $write( "%0d", k );
 
 end

 join_none

The LRM Says the Value of m is undetermined . Why is That so ?

In reply to Etrx91:

This is an error in the LRM. See 0004176: erroneous comment in example of fork-end block? - Accellera Mantis

hello Dave sir, i got the concept what you have explained above but I have a small doubt
I have implemented the following code

module fork_test;
initial 
    begin
    for (int j=0; j<3; j++)
        begin
        automatic int k=j;//(1)
        fork
         //automatic int k=j;//(2)
           $display(k,$time );
        join_none
        end
    end
endmodule

I got the result i=3 thrice … i would like to know as I have put condition on i in for loop that it should be less than 3 still why its coming i =3.
another thing is when i implements number (1) statement it display result 2,1,0 why in reverse order? while for(2) it comes in order 0,1,2. I have run this code in EDA tool cadence incisive 15.20

thanks and regards

In reply to suman_vip:

I also ran this code on EDA playground and did not see the results you are getting. However, the order the $display statements execute is indeterminate.

There is no “i” in your example. But if you meant you were displaying “j” inside the fork/join_none loops, then j would be 3 since the for loop would terminate before any statements within the fork/join_none starts executing. That’s the point of giving each iteration of the for loop a local copy of k.

In reply to dave_59:

sorry sir , my mistake it was “j” .thanku sir

for(j=0;j<3;j++)
as per i understand , for loop will be suspended whenever j exceeds 2 ie if j=3 loop suspended but if i will display j it will show 3 is it so sir.

In reply to dave_59:

Hello Dave,
While going through this informative post. I had a question related to lifetime of control variable of for-loop.

initial
begin
  for(int j=1; j<=3; j++)
  begin
    fork
      automatic int k = j;
      #k $write($time,"k=%0d\n",k);
      begin
        automatic int m =j;
        $display("m=%0d",m);
      end
    join_none  
  end
#100;
end

I want to understand the lifetime of j in this example. for-loop execution will get over at time zero and it will schedule 6 process which will spawn once simulator hit #100.

In above example j is available(m=j) irrespective of for-loop execution is over. Does that means j lifetime is also dependent on the process which are schedule while for-loop is executing ?

In reply to gauravsi:

The lifetime of any automatic variable ends when the life of its block and all its nested blocks ends.