SystemVerilog LRM mentions about automatic variable usage with fork/join_none inside for loops. I understand the parent process (for loop) executes currently with child processes, and child processes won’t start execution until parent process executes a blocking statement or terminates. I’m having some confusions about the following code:
module fork_test;
initial begin
for (int i=0; i<4; i++) begin
fork
automatic int k = i;
#i $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
#0;
end
end
endmodule
What I get (I tried with three vendors, Mentor/Cadence/Synopsys):
[0 ns] i = 1, k = 0
[1 ns] i = 4, k = 1
[2 ns] i = 4, k = 2
[3 ns] i = 4, k = 3
A few things I don’t understand:
What i=1 for first iteration and 4 for 2-4 iterations?
Why timestamp (#i) seems indicating i is indeed 0->1->2->3?
The reason for the behavior you’re seeing is the nasty #0 at the end of the for-loop. Each for-loop iteration is separated by a #0 delay. It might help to unroll the for-loop to see what actually is going on
i=0;
fork
automatic int k = 0;
#0 $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
#0;
i=1;
fork
automatic int k = 1;
#1 $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
#0;
i=2;
fork
automatic int k = 2;
#2 $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
#0;
i=3;
fork
automatic int k = 3;
#3 $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
#0;
i=4;
In all iterations, the #i get evaluated with the current value of i in the loop. But the problem with the first iteration, there is a race condition between the #0 in front of the $display and the #0 before the next iteration. So the $display could have printed 0 or 1. But in all the other iteration, i has already reach 4 before any other $display.
Thanks a lot for your explanation! Now I’m still having one confusion, when you unrolled the for-loop, you replaced #i with #0/1/2/3 for each iteration (#i before unrolling). However, for $display in each iteration, the same i will get its value from end-of-for-loop?
It seems when i is used in #i delay, its value gets evaluated immediately. However, when i is used for $display, its value gets evaluated at the end of parent process? Or maybe #i is not part of child process. Instead, it is an event/delay control, it gets its value immediately, while the same i in $display, as part of child process, gets its value after parent process (for-loop) is done?
Ok, my theory is incorrect. I tried to comment out #0 in my original post. Now the test looks like this:
module fork_test;
initial begin
for (int i=0; i<4; i++) begin
fork
automatic int k = i;
#i $display("[%0t ns] i = %0d, k = %0d", $time, i, k);
join_none
// #0;
end
end
endmodule
I’m seeing 4 for both timestamps and $display.
[4 ns] i = 4
[4 ns] i = 4
[4 ns] i = 4
[4 ns] i = 4