SystemVerilog offers different types of fork-join mechanisms to handle parallel execution. One of them is fork ... join_none
. This approach allows the main thread to continue executing the following statements without waiting for the forked threads to finish. In this article, we will explore how the fork join_none
works in SystemVerilog, with examples and a clear explanation.
What is Fork Join_None in SystemVerilog?
The fork join_none
construct enables the main thread to resume its execution immediately, even if the threads created by the fork
are still running. Unlike the standard fork join
, where the main thread waits for all child threads to complete, fork join_none
lets the main thread continue processing while the other threads remain active in the background.
For example, if you launch five threads, the main thread will resume as soon as the fork
statement executes. The five threads will continue running, but they will not block the execution of the main thread.
Example of Fork Join_None in SystemVerilog
Here’s a simple example using fork join_none
:
module tb;
initial begin
$display ("[%0t] Main Thread: Fork join is about to start", $time);
fork
print (20, "Thread1_0");
print (30, "Thread1_1");
print (10, "Thread2");
join_none
$display ("[%0t] Main Thread: Fork join has finished", $time);
end
// Automatic task definition
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 is about to start
[0] Main Thread: Fork join has finished
[10] Thread2
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.
In this example, the main thread prints that it is about to start and finishes immediately after the fork join_none
statement. The threads continue to run in the background.
Nested Fork Join_None Example
You can also have nested fork
statements with join_none
. Here’s an example:
module tb;
initial begin
$display ("[%0t] Main Thread: Fork join is about to start", $time);
fork
begin
fork
print (20, "Thread1_0");
print (30, "Thread1_1");
join_none
$display("[%0t] Nested fork has finished", $time);
end
print (10, "Thread2");
join_none
$display ("[%0t] Main Thread: Fork join has finished", $time);
end
// Automatic task definition
task automatic print (int _time, string t_name);
#(_time) $display ("[%0t] %s", $time, t_name);
endtask
endmodule
Simulation Log for Nested Fork Join_None
ncsim> run
[0] Main Thread: Fork join is about to start
[0] Main Thread: Fork join has finished
[0] Nested fork has finished
[10] Thread2
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.
In this scenario, the main thread continues execution immediately after starting the fork
statement. Even though there is a nested fork
inside the first one, the main thread doesn’t wait for the nested threads to finish.
Importance of the automatic
Keyword in Tasks
When using tasks inside fork join_none
, you may notice that tasks without the automatic
keyword can lead to unexpected behavior. Specifically, if a task is called from multiple threads without the automatic
keyword, the threads might share the same variables in simulation memory. This can result in incorrect outputs.
To ensure each thread gets its own copy of the task variables, you should declare the task with the automatic
keyword. Here’s an example:
Example Without the automatic
Keyword
module tb;
initial begin
$display ("[%0t] Main Thread: Fork join is about to start", $time);
fork
print (20, "Thread1_0");
print (30, "Thread1_1");
print (10, "Thread2");
join_none
$display ("[%0t] Main Thread: Fork join has finished", $time);
end
// Task without the 'automatic' keyword
task print (int _time, string t_name);
#(_time) $display ("[%0t] %s", $time, t_name);
endtask
endmodule
Simulation Log Without Automatic Keyword
ncsim> run
[0] Main Thread: Fork join is about to start
[0] Main Thread: Fork join has finished
[10] Thread2
[20] Thread2
[30] Thread2
ncsim: *W,RNQUIE: Simulation is complete.
In this case, the output for all threads is the same because the task does not use the automatic
keyword.
Conclusion
The fork join_none
mechanism in SystemVerilog allows parallel execution of threads while enabling the main thread to continue without waiting. This can significantly speed up simulations where the main thread does not need to wait for all tasks to complete. Additionally, using the automatic
keyword in tasks ensures that each thread gets its own copy of variables, preventing unexpected results.
Key Takeaways
- SystemVerilog Fork Join_None allows the main thread to continue execution without waiting for other threads to finish.
- Nested Fork Join_None supports complex parallel execution scenarios.
- The Automatic Keyword is essential to avoid variable sharing between threads in tasks.