Program Blocks in Testbenches


Grouping Statements


SystemVerilog Threading Enhancements


Thread Synchronization & Control

Threads coordinate using IPC (interprocess communication) constructs:


fork...join

fork_join blocks.png

program top;
  initial begin
    $display("@%0t: start fork...join example", $time);
    #10;
    $display("@%0t: sequential after #10", $time);

    fork
      $display("@%0t: parallel start", $time);

      #50;
      $display("@%0t: parallel after #50", $time);

      #10;
      $display("@%0t: parallel after #10", $time);

      begin
        #30;
        $display("@%0t: sequential after #30", $time);
        #10;
        $display("@%0t: sequential after #10", $time);
      end
    join_none

    $display("@%0t: after join", $time);
    #80;
    $display("@%0t finish after #80", $time);
  end
endprogram

join wait for everything to be done before it proceeds. join any continue after any one of them done. non would wait for them. Not that they all starts at the same time.


Setting independent variables in different Threads

initial begin 
	for (int j=0; j<3; j++) 
		fork automatic int k = j; // Make copy of index 
			$write(k); // Print copy 
		join_none 
		#0 $display;
end

Disabling a Thread

parameter TIME_OUT = 1000;

task wait_for_tr(Transaction tr);
    fork
        begin
            // Wait for response, or some maximum delay
            fork : timeout_block
                wait (bus.cb.addr != tr.addr);
                #TIME_OUT $display("@%0d: Error: timeout", $time);
            join_any

            disable timeout_block;
            $display("@%0d: Addr match %d", $time, tr.addr);
        end
    join_none
endtask

This version introduces two threads inside a fork...join_any, where:

If the correct bus address returns quickly enough:

If the bus address does not match in time:


Disabling Multiple Threads

SystemVerilog extends this concept with the disable fork statement, allowing you to stop all child threads spawned from the current thread.

⚠️ Warning:
Using disable fork can unintentionally stop too many threads, including those created by routine calls.
To avoid this, always surround target code with a fork...join block to limit the scope of the disable fork statement.

Limiting the Scope of a disable fork

initial begin
    wait_for_tr(tr0);  // Spawn thread 0

    // Create a thread to limit scope of disable fork
    begin
        wait_for_tr(tr1);  // Spawn thread 1

        fork
            wait_for_tr(tr2);  // Spawn thread 2
        join

        // Stop threads 1 & 2, but leave 0 alone
        #(TIME_OUT/2) disable fork;
    end
join
end

Explanation

This code structure demonstrates how to control which threads are stopped by limiting the scope of disable fork.

Thread hierarchy:

Thread ID Description Parent Scope
0 wait_for_tr(tr0) Outside all forks
1 wait_for_tr(tr1) Inside fork block
2 wait_for_tr(tr2) Spawned within thread 1
3–4 (inner fork calls) Child threads under 1

Behavior:

Using a Label to Stop Specific Threads

initial begin
    wait_for_tr(tr0);  // Spawn thread 0

    begin : threads_1_2
        wait_for_tr(tr1);  // Spawn thread 1
        wait_for_tr(tr2);  // Spawn thread 2
    end

    // Stop threads 1 & 2, but leave 0 alone
    #(TIME_OUT/2) disable threads_1_2;
join
end