In reply to Verif Engg:
event_controls like @ and # in procedural code are not statement by themselves; they are prefixes to the statements that follow. And a statement can be a simple statement, like an assignment, or a block like begin/end or fork/join. And a block is allowed wherever a single statement is allowed.
When you write @(posedge clk); it is really @(posedge clk) null_statement;
I should have given you enough information to answer your question, but here is another variation:
forever
@posedge(clk)
if(vif.sof == 1) begin
//some code here
end
Now there is a big difference if a semicolon follows @(posedge clk) or not.