A Subtle Gotcha When Using fork…join

I want to start things out light with a recent experience I've had using fork...join statements.


This is a companion discussion topic for the original entry at https://verificationgentleman.netlify.app/2014/03/09/a-subtle-gotcha-when-using-forkjoin.html

in “desired result”, why are the values in reversed order? I guess that’s a copy/past typo

btw, another trick you can play with in this is to add “#0;” after join_none in for loop.

The order in which the spawned threads are scheduled is not defined. This particular simulator chose to switch to the last spawned thread first (the thread that prints 2). It’s actually pretty cool that it did that, because this way we can also see that we shouldn’t rely on any specific execution order when it comes to parallel threads, unless we use explicit synchronization mechanisms (such as semaphores).

Also, thanks for the tip with the extra #0 delay. This works inside tasks, where you’re allowed to consume time, but if you have a function that spawns multiple threads, then you won’t be allowed to do any delays (# statements, @ statements, etc.).

You are right about the order. It’s just that I’ve never seen reversed order with the simulators I used.
As to the #0, it’s particularly useful when fork/join_none is used and you want to get the spawned thread run without advancing time. In fact, it’s used in UVM source codes.
Good posts~

Using #0 is the wrong thing to do. It is a hack that is a short term fix that creates problems later. The #0’s in the UVM and other code create unnatural ordering issue that are fixed by adding extraneous loops of #0s. The problem using them to fix the example here is that instead of getting parallel threads, you get a series of ordered threads delayed by one delta cycle. -dave_59

How i solved same problem

module top;
initial begin
for (int i = 0; i < 3; i++) begin

  fork
    begin
      some_task(i);
     
    end
  join_none 
 
 wait fork; //make wait all threads to finish before exiting the for loop.
end

end

task some_task(int i);
$display(“i = %d”, i);
endtask

endmodule

This seems to just fork off some_task() in each iteration, but wait for it to complete until moving on to the next. So in essence, it just does:

for (int i = 0; i < 3; i++) begin
  some_task(i);
end