System Verilog : Fork Join

The fork-join construct enables the creation of concurrent processes from each of its parallel blocks. All the blocks get the same start time and the finish time is controlled by the type of join construct used.

Formal syntax for a parallel block.

par_block ::= 
fork [ : block_identifier ] { block_item_declaration } { statement_or_null }
join_keyword [ : block_identifier ]
join_keyword ::= join | join_any | join_none
block_item_declaration ::= 
{ attribute_instance } data_declaration
| { attribute_instance } local_parameter_declaration ;
| { attribute_instance } parameter_declaration ;
| { attribute_instance } overload_declaration
| { attribute_instance } let_declaration

With fork-join -which is available in conventional Verilog – procedure can continue only if all forked process has been completed. But in System Verilog two more variants are added – Join_none and join_any. With join_none construct, parent procedure will not wait for any forked process to complete. It means, parent procedure will continue while forked process are running. With join_any when any of the forked process completes, procedure will continue.

Fork Join	    The parent process blocks until all the processes spawned by this fork complete.
Fork Join_any	The parent process blocks until any one of the processes spawned by this fork completes. Other blocks left running
Fork Join_none	The parent process continues to execute concurrently with all the processes spawned by the
fork. The spawned processes do not start executing until the parent thread executes a blocking statement or terminates. All blocks left running

Below is a sample code for fork-join construct. In this case, all the blocks will be start executing at the same time.

But in BLK1, first statement is #2ns and BLK3 & 4 are waiting for the events. So BLK2 will be executed first and then BLK1. Once BLK1 completes, it generates ev_blk1 event which triggers BLK4. Same way after BLK4, event ev_blk4 will be generated which will trigger BLK3.

So the order would be BLK2, BLK1, BLK4 and BLK3. P1 will continue after BLK3.

Simulation result is shown below.

class fork_join_class;
 
 task run(input event ev_blk1,ev_blk4);
 fork
   BLK1: begin
     # 2ns
     $display( "BLOCK 1\n" );
     # 10ns;
     ->ev_blk1;
   end
 
   BLK2: begin
     $display( "BLOCK 2\n" );
   end
 
   BLK3: begin
     @ev_blk4;
     $display( "BLOCK 3\n" );
   end

   BLK4: begin
     @ev_blk1
     $display( "BLOCK 4\n" );
     ->ev_blk4;
  end
  join
 endtask
endclass

program myprog;
  event ev_blk1,ev_blk4;
  fork_join_class c1 = new;
  
  initial begin
   c1.run(ev_blk1,ev_blk4);
   P1: begin
   $display("Parent Process Started\n");
   end

   
   #100ns;
  end
endprogram

/*
With Join : 
BLOCK 2
BLOCK 1
BLOCK 4
BLOCK 3
Parent Process Started

With Join_none : 
Parent Process Started
BLOCK 2
BLOCK 1
BLOCK 4
BLOCK 3

With Join_any :
BLOCK 2
Parent Process Started
BLOCK 1
BLOCK 4
BLOCK 3

*/

If we replace line 26 with join_none, without waiting for any of the blocks completion, parent process will start. Other process will be left running and we will get the same order as in fork-join.

So the order would be P1, BLK2, BLK1, BLK4 and BLK3.

If we replace line 26 with join_any, parent process will wait for one block for completion and then parent process will start. Other process will be left running and and the rest will be in the same order.

So the order would be BLK2, P1, BLK1, BLK4 and BLK3.

The disable fork and wait fork information can be found in System Verilog: Disable Fork & Wait Fork

Leave a Reply

Your email address will not be published. Required fields are marked *