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