I think there are some general misconceptions about the execution semantics of an always
block and the concept of a sensitivity list
The always
construct instantiates a permanent procedural process. That process is either executing procedural statements, or suspended/blocked waiting for something to happen using an event control (e.g. a change on a signal or a delay to pass).
Typical RTL coding styles dictate a single event control as the first construct of the process, but the language allows many event controls anywhere within the process. Note that the language does not require any event controls, but without at least one, it would get stuck in an infinite loop at time 0.
The @(*)
event control is a shortcut used when modeling combinatorial logic. The compiler automatically creates an event control list with all the signals used in that the process, based on the procedural block of code that follows it. The only exception is variables being written to, which are not added.
always @(*) begin // waits for a change on b, c, or e
a = b + c;
d = a + e;
end
Is the same as writing
always begin
@(b or c or e) // variables a and d are not added to the list
a = b + c;
d = a + e;
end
The compiler does not look at behavior of the code when creating the list, just which signals have references.
Another thing to consider is that event controls are executable procedural constructs. They must execute first before the event happens If you add delays to the process
always @(*) begin // waits for a change on b, c, or e
#5 a = b + c;
#5 d = a + e;
end
Assume b
first changes at time 10. The assignment to a
happen at time 15 and the assignment to d
happens at time 20. It is not until time 20 that the process waits for another change on b
, c
, or e
. If b
changed again at time 19, that change event will be missed.