SystemVerilog provides support for parallel or concurrent threads through fork join construct. Multiple procedural blocks can be spawned off at the same time using fork and join. There are variations to fork join that allow the main thread to continue executing rest of the statements based on when child threads finish.

Syntax


	fork
		// Thread 1
		// Thread 2
		// ...
		// Thread 3
	join

fork join example

In the example shown below, three threads are forked using fork join. The main thread stays suspended until all the threads spawned by the fork is completed. Any block of code within begin and end are considered as a separate thread, and in this case it is Thread2.


module tb;
	initial begin
      $display ("[%0t] Main Thread: Fork join going to start", $time);
		fork
			// Thread 1 
			#30 $display ("[%0t] Thread1 finished", $time);
			
			// Thread 2
          	begin
              	#5 $display ("[%0t] Thread2 ...", $time);
				#10 $display ("[%0t] Thread2 finished", $time);
            end
            
            // Thread 3
			#20 $display ("[%0t] Thread3 finished", $time);
		join
      $display ("[%0t] Main Thread: Fork join has finished", $time);
	end
endmodule

The main thread forks all three threads at time 0ns. Thread2 is a block of procedural code and finishes only when it executes all the statements inside begin and end. Thread2 takes 15 ns to finish, and because it started at 0ns, it finishes at 15ns and is the first thread to finish. Thread1 takes the most simulation time to finish and does so at 30ns, while Thread3 finishes earlier at 20ns.

 Simulation Log
ncsim> run
[0] Main Thread: Fork join going to start
[5] Thread2 ...
[15] Thread2 finished
[20] Thread3 finished
[30] Thread1 finished
[30] Main Thread: Fork join has finished
ncsim: *W,RNQUIE: Simulation is complete.

Nested fork join

fork join can be nested in other fork join also.

Example #1


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
          print (10, "Thread2");              
		join
      $display ("[%0t] Main Thread: Fork join has finished", $time);
	end
  
  // Note that this task has to be automatic
  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
[20] Thread1_0
[30] Thread1_1
[30] Main Thread: Fork join has finished
ncsim: *W,RNQUIE: Simulation is complete.

Example #2


module tb;
	initial begin
      $display ("[%0t] Main Thread: Fork join going to start", $time);
		fork
			fork // Thread 1
              #50 $display ("[%0t] Thread1_0 ...", $time);
              #70 $display ("[%0t] Thread1_1 ...", $time);
              begin
                #10 $display ("[%0t] Thread1_2 ...", $time);
                #100 $display ("[%0t] Thread1_2 finished", $time);
              end
            join
			
			// Thread 2
          	begin
              	#5 $display ("[%0t] Thread2 ...", $time);
				#10 $display ("[%0t] Thread2 finished", $time);
            end
            
            // Thread 3
			#20 $display ("[%0t] Thread3 finished", $time);
		join
      $display ("[%0t] Main Thread: Fork join has finished", $time);
	end
endmodule

See that the main thread stays suspended until all the nested forks are over by 110ns.

 Simulation Log
ncsim> run
[0] Main Thread: Fork join going to start
[5] Thread2 ...
[10] Thread1_2 ...
[15] Thread2 finished
[20] Thread3 finished
[50] Thread1_0 ...
[70] Thread1_1 ...
[110] Thread1_2 finished
[110] Main Thread: Fork join has finished
ncsim: *W,RNQUIE: Simulation is complete.