Fork within loop with join ALL

Hello,

I’d like to fork threads in a loop. So I use the classical approach:


for(int index=0;index<14;index++)begin
    automatic int idx=index;
    fork
        begin
            `ovm_do_on(sequence_inst,p_sequencer.my_sqr[idx]);
        end
    join_none;
end

And it works fine, except that I’d like to use “join” (all) instead of “join_none” since I have to wait for all the sequences to be completed before continuing the test case.
When I use “join”, it looks like the sequences are not forked, but launched one after the other…

Any idea why it behaves like this?
I’ve fixed this by unfolding the loop into a big fork and it works fine, but I’m really curious why the use of “join” in this loop breaks the parallelization.

Thanks a lot &
BR

Because fork/join is a blocking statement, only the statements inside the fork/join are executed in parallel.

What you need to do add a wait fork statement after the for loop. This block the current thread until all child threads have completed. You have to be careful if there are any other earlier fork_join_none statements that you do not want to wait for them to finish. If there are, the you need to create an isolating thread.


fork 
  begin : isolating_thread
    for(int index=0;index<14;index++)begin : for_loop

      fork
      automatic int idx=index;
        begin
            `ovm_do_on(sequence_inst,p_sequencer.my_sqr[idx]);
        end
      join_none;
    end : for_loop
  wait fork;
  end : isolating_thread
join

In reply to dave_59:

Hi Dave,

Thanks a lot for the explanation!
That works indeed perfectly.

Dave :
I would like to know the purpose of using “wait fork” here ?
do you think fork/join actually already take care of wait fork? or it has to do something about child seqs of sequence_inst in Andreas38’s example?
thanks in adv

In reply to aming:

The wait fork suspends the parent sequence (or test) until all the child sequences (processes) have completed. There reason you can’t just use fork/join is because of the outer for_loop used to spawn each sequence (process) with a fork statement. If you used a fork/join, then each iteration of the loop would be blocked until the sequence completed, and the fork/join is not really doing anything different than a begin/end.

Thanks Dave. I am afraid i still dont fully understand the usage of wait fork here. :(

I copied your code below, I understand that using fork/join_none inside of for-loop, and using fork/join(first line and last line of your code) to wait all threads created by for-loop to be completed, but i dont understand why there is still a wait fork statement befor join, does the fork/join ( first line and last line of your code ) already wait for all threads to be completed?

thank you

fork 
  begin : isolating_thread
    for(int index=0;index<14;index++)begin : for_loop
      fork
      automatic int idx=index;
        begin
            `ovm_do_on(sequence_inst,p_sequencer.my_sqr[idx]);
        end
      join_none;
    end : for_loop
  wait fork;
  end : isolating_thread
join

In reply to aming:

A fork/join blocks until all direct child threads to complete. A fork/join block does not wait for its grandchildren threads to complete unless a child thread is blocked waiting for a grandchild thread.

In the example above there is only one child thread of the outerfork/join, the isolating_thread begin/end block. Without the wait fork statement, the isolating_thread would not block waiting for the grandchildren threads spawned by the fork/join_none inside the for loop.

Now the reason for the outer fork/join block is a bit trickier to explain, since we are only showing a fragment of code.

The wait fork statement blocks until all child threads of the current parent thread complete. We do not know if there were any other fork/join_none statements that came before this code snippet that we do not want to wait for, The isolating thread creates thread layer that guarantees that we only block waiting for the child threads of the isolating_thread.

In these examples, the automatic variable is declared before the fork. In most other examples I have seen it inside the Fork. Are both equivalent? Before forking feels as if the variable isn’t really belonging to the forked thread?

In reply to NiLu:

You are correct. There needs to be a separate idx variable for each invocation of the fork/join_none. I overlooked this since it was not the main point of the original question. I have edited the example.

Is it possible to do something similar, but accomplish join_any behavior instead of join_all? All examples that I have seen on this topic always use the “wait fork” statement.

I am interested in starting multiple threads in parallel and joining them when just one completes. In particular, I want to wait for just one of any events to occur. I have tried a few variations of the following example, but without success.

Any help would be greatly appreciated. Thanks!

JH

fork
   begin
      foreach(my_cbs[i]) begin  // loop through an array of callbacks
         fork
            automatic int var_i = i;
            begin
               @(my_cbs[var_i].change_event);  // wait for an event triggered within the callback
            end
         join_none
      end
   end
join_any

In reply to jhardy:

begin
  event just_one_of any;
  foreach(my_cbs[i]) // loop through an array of callbacks
    fork
      int var_i = i;
         begin
               @(my_cbs[var_i].change_event);  // wait for an event triggered within the callback
               -> just_one_of_any;
         end
    join_none
  @just_one_of_any; 
end

Thanks Dave! I appreciate the quick reply. Your suggestion solved my problem.

I assume that we still should declare the variable as automatic. Please correct correct me if I am wrong.

In reply to jhardy:
Yes. If this code is inside a class, then it is already automatic by default.

In reply to theketi:
i < 8 has to fail in order to end the loop. So i will be 8 after exiting the loop.

In reply to dave_59:

Hello Dave,
I am trying to run below code :


        my_seq = new[2];
        fork 
            begin : isolating_thread
              for(int index=0;index<2;index++) begin : for_loop
                  automatic int idx=index;
                  automatic int local_qd=$urandom_range(1,4);
                  fork
                      `uvm_do_with(my_seq[idx],
                                    {
                                     qd           == local_qd;
                                    })
                  join_none;
              end : for_loop
            wait fork;
            end : isolating_thread
        join

I am getting below Error :

=======================================================
Solver failed when solving following set of constraints
integer qd = 0;
integer local_qd = 4;
constraint WITH_CONSTRAINT // (from this) (constraint_mode = ON) (<File_path>:233)
{
(qd == local_qd);
}
=======================================================

Can you please help me with this ?? Thanks a lot in advance.

  • Biren

In reply to birenkumar:

This likely has nothing to do with a fork statement. Maybe you did not declare qd as rand.

In reply to dave_59:

Hi Dave,

If I have two for-loop, how to handle it ? Is below code correct ?



fork 
  begin : isolating_thread
    for(int index=0;index<14;index++)begin : for_loop
      fork
      automatic int idx=index;
        for(int j=0;j<14;j++)begin 
         automatic int jdx=j;
        begin
            `ovm_do_on(sequence_inst,p_sequencer.my_sqr[idx][jdx]);
        end
      join_none;
    end : for_loop
  wait fork;
  end
  end : isolating_thread
join


In reply to zz8318:

In reply to dave_59:
Hi Dave,
If I have two for-loop, how to handle it ? Is below code correct ?


fork 
begin : isolating_thread
for(int index=0;index<14;index++)begin : for_loop
fork
automatic int idx=index;
for(int j=0;j<14;j++)begin 
automatic int jdx=j;
begin
`ovm_do_on(sequence_inst,p_sequencer.my_sqr[idx][jdx]);
end
join_none;
end : for_loop
wait fork;
end
end : isolating_thread
join

That should work, not sure you need that second automatic variable assignment for j, but it shouldn’t hurt.

I just ran into this problem yesterday… great thread… I totally missed the part about the automatic variable for the fork.

In reply to dave_59:

With respect to the fork and join ALL inside for loop, I have a very basic question regarding the same.


for (i = 0; i<3;i++) begin
    automatic int j = i;
    fork 
       $display(j);
    join
end

Will the output for the above code will be :
0
1
2
???

In reply to SanjanaDhiran:

A fork/join with one statement behaves just like a begin/end block as far as your example is concerned.