In SystemVerilog, a thread or process is any piece of code that executes independently as a separate entity. These threads can run concurrently, and this parallel execution is crucial for tasks like simulation and verification.

SystemVerilog Thread Creation and Fork-Join Mechanism

In SystemVerilog, several blocks like initial and always are treated as separate threads that start executing from time zero. Furthermore, the fork and join statements allow the creation of multiple threads that can run in parallel. The fork-join mechanism is essential for managing concurrent execution in a SystemVerilog testbench.

Different Types of Fork-Join in SystemVerilog

There are three types of fork-join in SystemVerilog, each serving a different purpose depending on how the threads are managed:

  1. fork-join: Waits until all the threads finish.
  2. fork-join_any: Finishes as soon as any one of the threads completes.
  3. fork-join_none: Does not wait and immediately exits after spawning the threads, allowing them to run in the background.

Here’s a breakdown of how each type works:

TypeDescriptionBehavior
fork-joinWaits for all threads to finishBlocks the main thread until all threads are complete
fork-join_anyWaits until any one thread finishesExits when any one thread completes
fork-join_noneDoes not wait for threads to finishMain thread continues immediately after spawning threads

Fork-Join in Action

The following sections show how to use each type of fork-join in SystemVerilog through simple examples.


SystemVerilog fork-join Example

In the fork-join example, all threads are executed in parallel. The main thread waits until all the threads finish before continuing.

Example: Using fork-join

module tb_top;
   initial begin
      #1 $display ("[%0t ns] Start fork ...", $time);

      // Main Process: Fork processes and wait for all to finish
      fork
         // Thread 1: Print after 5ns
         #5 $display ("[%0t ns] Thread1: Orange is named after orange", $time);

         // Thread 2: Print two statements with delays
         begin
            #2 $display ("[%0t ns] Thread2: Apple keeps the doctor away", $time);
            #4 $display ("[%0t ns] Thread2: But not anymore", $time);
         end

         // Thread 3: Print after 10ns
         #10 $display ("[%0t ns] Thread3: Banana is a good fruit", $time);
      join

      // Main Process: Continue after all threads finish
      $display ("[%0t ns] After Fork-Join", $time);
   end
endmodule

Simulation Log:

[1 ns] Start fork ...
[3 ns] Thread2: Apple keeps the doctor away
[6 ns] Thread1: Orange is named after orange
[7 ns] Thread2: But not anymore
[11 ns] Thread3: Banana is a good fruit
[11 ns] After Fork-Join

In this example, the main process waits until all threads finish executing before it continues.


SystemVerilog fork-join_any Example

The fork-join_any variant waits for only one of the forked processes to finish, allowing the main process to continue immediately after any thread completes.

Example: Using fork-join_any

module tb_top;
   initial begin
      #1 $display ("[%0t ns] Start fork ...", $time);

      // Main Process: Fork processes and wait for any to finish
      fork
         // Thread 1: Print after 5ns
         #5 $display ("[%0t ns] Thread1: Orange is named after orange", $time);

         // Thread 2: Print two statements with delays
         begin
            #2 $display ("[%0t ns] Thread2: Apple keeps the doctor away", $time);
            #4 $display ("[%0t ns] Thread2: But not anymore", $time);
         end

         // Thread 3: Print after 10ns
         #10 $display ("[%0t ns] Thread3: Banana is a good fruit", $time);
      join_any

      // Main Process: Continue after any thread finishes
      $display ("[%0t ns] After Fork-Join", $time);
   end
endmodule

Simulation Log:

[1 ns] Start fork ...
[3 ns] Thread2: Apple keeps the doctor away
[6 ns] Thread1: Orange is named after orange
[6 ns] After Fork-Join
[7 ns] Thread2: But not anymore
[11 ns] Thread3: Banana is a good fruit

Here, the main process continues after Thread2 completes, even though the other threads are still running.


SystemVerilog fork-join_none Example

The fork-join_none variant spawns the threads but doesn’t wait for them to finish. The main thread resumes execution right after the fork-join_none block is executed.

Example: Using fork-join_none

module tb_top;
   initial begin
      #1 $display ("[%0t ns] Start fork ...", $time);

      // Main Process: Fork processes without waiting
      fork
         // Thread 1: Print after 5ns
         #5 $display ("[%0t ns] Thread1: Orange is named after orange", $time);

         // Thread 2: Print two statements with delays
         begin
            #2 $display ("[%0t ns] Thread2: Apple keeps the doctor away", $time);
            #4 $display ("[%0t ns] Thread2: But not anymore", $time);
         end

         // Thread 3: Print after 10ns
         #10 $display ("[%0t ns] Thread3: Banana is a good fruit", $time);
      join_none

      // Main Process: Continue immediately
      $display ("[%0t ns] After Fork-Join", $time);
   end
endmodule

Simulation Log:

[1 ns] Start fork ...
[1 ns] After Fork-Join
[3 ns] Thread2: Apple keeps the doctor away
[6 ns] Thread1: Orange is named after orange
[7 ns] Thread2: But not anymore
[11 ns] Thread3: Banana is a good fruit

In this case, the main process doesn’t wait and proceeds immediately after the fork-join_none block is executed, while the threads continue running in the background.


Key Differences Between fork-join, fork-join_any, and fork-join_none

Here’s a comparison of the three types of fork-join:

TypeWaiting BehaviorUse Case
fork-joinWaits for all threads to completeWhen you need to ensure all tasks finish before continuing
fork-join_anyExits when any one thread finishesUseful when the main process can proceed after any task completes
fork-join_noneDoes not wait; threads continue in backgroundWhen the main process doesn’t need to wait for threads to finish

Conclusion

The fork-join mechanism in SystemVerilog allows you to manage concurrent execution of threads, making it easier to handle multiple tasks in a verification environment. Depending on your requirements, you can choose between fork-join, fork-join_any, or fork-join_none to control how and when the main process resumes after spawning threads.

By using these techniques effectively, you can improve the performance and accuracy of your simulations and verification tasks.


Key Takeaways:

  • SystemVerilog threads execute in parallel and can be managed using the fork-join mechanism.
  • Choose fork-join, fork-join_any, or fork-join_none based on whether you need to wait for threads to finish.
  • Use fork-join for synchronization, fork-join_any for partial synchronization, and fork-join_none for asynchronous execution.

This article should now help you fully understand how to use threads in SystemVerilog to handle concurrent processes efficiently in your simulation environment!

Scroll to Top