Difficulty: Medium · Puzzle 5 of 6 · Topic: Fork Join Puzzles
Predict the full timeline. When does `all_done` print, and why is it not t=10?
`timescale 1ns/1ns
module p5;
initial begin
fork
begin #1 $display("%0t fast0", $time); end
begin #10 $display("%0t slow0", $time); end
join_any
$display("%0t after_any0", $time);
fork
begin #3 $display("%0t fast1", $time); end
begin #10 $display("%0t slow1", $time); end
join_any
$display("%0t after_any1", $time);
wait fork;
$display("%0t all_done", $time);
end
endmodule`join_any` never terminates the losing branch, so leftovers pile up. `wait fork` waits for every leftover child of the calling process, and each slow branch started at its own fork time.
1) First fork starts at t=0: fast0 prints at 1 and the parent resumes with after_any0 at 1. slow0 is still alive and will finish at 0 + 10 = 10.
2) Second fork starts at t=1: fast1 prints at 4, after_any1 at 4. slow1 will finish at 1 + 10 = 11.
3) `wait fork` now waits for both leaked slow branches: slow0 prints at 10, slow1 at 11, and all_done at 11.Answer: 1 fast0, 1 after_any0, 4 fast1, 4 after_any1, 10 slow0, 11 slow1, 11 all_done
Checked by running the code in Icarus Verilog 13 and Verilator 5; the printed output supports the answer above. Each leaked slow branch ends 10 ns after its own fork. (Icarus, 4-state) Each leaked slow branch ends 10 ns after its own fork. (Verilator, 2-state)
The leaked slow branches accumulate across forks: slow1 prints at 11, not 10, because it only started at t=1, and `wait fork` then waits for the later of the two.
What minimal code change guarantees neither slow branch prints? Model answer: put `disable fork;` right after each `join_any` (before the next fork); then after_any1 and all_done print at 4 and nothing prints at 10 or 11.
I would track the leaked threads, because join any leaves the slow branch running. The first fork gives fast0 and after any0 at 1, and its slow branch will end at 10. The second fork starts at 1, so fast1 and after any1 print at 4 and its slow branch ends at 11. Then wait fork waits for both, so slow0 at 10, slow1 at 11 and all done at 11. The trap is that slow1 is not at 10, since it started late. I would check that a disable fork follows every join any.