System Verilog : Fork Join
Last modified 2017-01-21
Sini
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.
parblock ::= fork [ : blockidentifier ] { blockitemdeclaration } { statementornull } joinkeyword [ : blockidentifier ] joinkeyword ::= join | joinany | joinnone blockitemdeclaration ::= { attributeinstance } datadeclaration | { attributeinstance } localparameterdeclaration ; | { attributeinstance } parameterdeclaration ; | { attributeinstance } overloaddeclaration | { attributeinstance } letdeclaration
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 - Joinnone and joinany. With joinnone construct, parent procedure will not wait for any forked process to complete. It means, parent procedure will continue while forked process are running. With joinany 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 Joinany The parent process blocks until any one of the processes spawned by this fork completes. Other blocks left running Fork Joinnone 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 evblk1 event which triggers BLK4. Same way after BLK4, event evblk4 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 forkjoinclass;
task run(input event evblk1,evblk4); fork BLK1: begin # 2ns $display( "BLOCK 1\n" ); # 10ns; ->evblk1; end
BLK2: begin $display( "BLOCK 2\n" ); end
BLK3: begin @evblk4; $display( "BLOCK 3\n" ); end
BLK4: begin @evblk1 $display( "BLOCK 4\n" ); ->evblk4; end join endtask endclass
program myprog; event evblk1,evblk4; forkjoinclass c1 = new;
initial begin c1.run(evblk1,evblk4); 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 Joinnone : Parent Process Started BLOCK 2 BLOCK 1 BLOCK 4 BLOCK 3
With Joinany : BLOCK 2 Parent Process Started BLOCK 1 BLOCK 4 BLOCK 3
*/

If we replace line 26 with joinnone, 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 joinany, 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