Difficulty: Medium · Puzzle 3 of 6 · Topic: Event Scheduling Puzzles
Three processes use the same event at t=10. Which of them wake or pass, which can never print, and is the print order of the two that do wake defined?
`timescale 1ns/1ns
module tb;
event ev;
initial begin : early_waiter
@ev; // already waiting since t=0
$display("[%0t] early_waiter woke", $time);
end
initial begin : trigger_then_wait
#10;
-> ev;
@ev; // waits for a NEW trigger
$display("[%0t] trigger_then_wait woke", $time);
end
initial begin : level_waiter
#10;
wait (ev.triggered); // level-sensitive, true for the whole slot
$display("[%0t] level_waiter passed", $time);
end
initial begin
#30;
$display("[%0t] end", $time);
$finish;
end
endmodule-> ev is a momentary pulse; ev.triggered stays true until the end of the time slot.
1) early_waiter is blocked on @ev before t=10, so the -> ev at t=10 wakes it. It prints at t=10.
2) level_waiter uses wait(ev.triggered). Whether it runs before or after the -> ev in the t=10 slot, the condition is true once the event has fired in that slot, so it passes at t=10.
3) trigger_then_wait fires ev and then starts waiting for it. The trigger is already in the past, and nobody triggers ev again, so it blocks forever and never prints.
4) early_waiter and level_waiter both print at t=10. Their relative print order is not defined by the LRM (process order in one slot); only the set of lines is.
5) The only other line is [30] end.Answer: Exactly three lines: early_waiter woke and level_waiter passed (both at t=10, in either order), and [30] end. trigger_then_wait never prints.
Checked by running the code in Verilator 5; the printed output supports the answer above. early_waiter wakes at t=10, and trigger_then_wait never prints (lost wake-up). wait (ev.triggered) passes at t=10; the print order of the two t=10 lines is undefined, so each is checked on its own.
@ev right after -> ev in the same process is a classic lost wake-up: the trigger is a point in time, not a state. Use wait(ev.triggered) (or a persistent flag) when the waiter may arrive in the same time slot as the trigger.
What changes if trigger_then_wait uses ->> ev instead of -> ev? Model answer: ->> schedules the trigger in the NBA region of t=10, after the process has already reached @ev, so it wakes at t=10 as well (LRM 15.5.2). Not tool-verified: Icarus does not implement ->>, and Verilator fires it immediately so the process still sleeps.
Two lines print at ten and one at thirty. The process already waiting on the event wakes, and the one using wait on triggered passes either way, but the LRM does not define which of those two prints first. The process that triggers and then waits misses its own trigger, so it blocks forever. The trap is treating the trigger as a state when it is a point in time; I would use wait on triggered or a flag, and check for any waiter that can arrive in the same slot.