In reply to whwjez:
The description of clocking blocks in the LRM is in need of much improvement. See 0003012: Cleanups needed on clocking block text (Mantis 890 revisited!) - Accellera Mantis
The LRM says that the clocking block event is equivalent to the specified event control. But that can’t be true because a clocking block is a concurrent process that has to receive the the event and then generate the clocking block event. They have to be two separate events. Think of a clocking block as an implicit always block
clocking cb @(posedge clk);
input ...;
output ...;
endclocking
always @(posedge clk) begin : translated_cb
// input sampling here
// output scheduling here
->cb;
end
initial block
@(posedge clk)
///
@cb; // this is race
Without the clarification mentioned in the Mantis link, there is no way to predict if the cb event has already fired - either @(posedge clk) could resume first. With the clarification, the translated always @(posedge clk) happens later, and the behavior you are seeing will be expected.