SystemVerilog provides a mechanism to handle multiple threads concurrently using fork and join. This allows you to run different parts of code simultaneously. However, the default fork-join construct waits for all threads to complete before continuing. But what if you want the main thread to resume execution as soon as one of the child threads finishes? In such cases, you can use the SystemVerilog fork join_any construct, which offers more flexibility.


What is the SystemVerilog Fork Join_any?

In a basic fork-join block, the main thread will wait until all child threads finish their execution. This approach may cause delays if one or more threads run indefinitely. In contrast, the fork-join_any construct allows the main thread to resume execution as soon as any one of the child threads completes. The remaining threads will continue to run in the background.

Key Points of SystemVerilog fork-join_any:

  • The main thread resumes execution when one thread finishes.
  • Other threads continue running even after the main thread has moved on.
  • It prevents the simulation from hanging if a thread takes too long to execute.

Example 1: Simple SystemVerilog fork-join_any

Below is an example of how fork-join_any works in a SystemVerilog testbench:

module tb;
  initial begin
    $display ("[%0t] Main Thread: Fork join going to start", $time);
    
    fork
      print (20, "Thread1_0");
      print (30, "Thread1_1");
      print (10, "Thread2");
    join_any

    $display ("[%0t] Main Thread: Fork join has finished", $time);
  end

  task automatic print(int _time, string t_name);
    #(_time) $display ("[%0t] %s", $time, t_name);
  endtask
endmodule

Simulation Log:

ncsim> run
[0] Main Thread: Fork join going to start
[10] Thread2
[10] Main Thread: Fork join has finished
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.

Explanation:

  • The main thread starts and initiates the fork block with three threads.
  • The main thread resumes as soon as Thread2 finishes (because join_any allows the main thread to continue once any one child thread completes).
  • The remaining threads, Thread1_0 and Thread1_1, continue running in the background.

Example 2: Nested SystemVerilog Fork Join_any

You can also use fork-join_any in nested blocks to manage more complex multi-threaded operations.

module tb;
  initial begin
    $display ("[%0t] Main Thread: Fork join going to start", $time);
    
    fork
      fork
        print (20, "Thread1_0");
        print (30, "Thread1_1");
      join_any
      print (10, "Thread2");
    join_any

    $display ("[%0t] Main Thread: Fork join has finished", $time);
  end

  task automatic print(int _time, string t_name);
    #(_time) $display ("[%0t] %s", $time, t_name);
  endtask
endmodule

Simulation Log:

ncsim> run
[0] Main Thread: Fork join going to start
[10] Thread2
[10] Main Thread: Fork join has finished
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

Explanation:

  • This example uses a nested fork-join_any construct.
  • The main thread resumes execution when any of the child threads in the nested fork block completes.
  • Threads are still running in the background after the main thread resumes, maintaining concurrency.

Advantages of Using fork-join_any

Featurefork-joinfork-join_any
Thread CompletionMain thread waits for all threadsMain thread resumes when any thread finishes
Simulation Hang RiskHigh if any thread runs foreverAvoids hanging by allowing early exit
ConcurrencyAll threads run until completionThreads run in parallel even after the main thread resumes

Why Choose fork-join_any?

  • If you want your simulation to avoid delays or hanging, fork-join_any is ideal.
  • It helps manage concurrency efficiently, especially when working with multiple independent tasks.

Conclusion

The fork-join_any construct is a useful tool in SystemVerilog when you need the main thread to resume execution as soon as any child thread completes, without waiting for all threads to finish. By using this construct, you can improve the efficiency and concurrency of your testbenches, ensuring that your simulation runs smoothly and avoids potential delays caused by hanging threads.

Scroll to Top