First, some preliminaries.
The wait
statement waits for an event and the condition to be true. So, if there is no event, the condition will not be evaluated. So, if start
is already 1 when entering S0, it will halt on the wait
statement until start
changes and becomes 1.
Also, as I noted in my comment, signal assignment does not occur until the end of the time step. Signal assignments are non-blocking. That is, the assignment does not block execution until the assignment is complete. As you have it written, it will take 2 changes on start
going to 1 to cause a transition to state S1. I tweaked your code to add some report statements, and put it into a testbench. Here's the code:
main : PROCESS
BEGIN
while true loop
CASE state IS
WHEN S0 =>
report "Entering S0, waiting for start";
WAIT UNTIL start = '1';
state <= S1;
WHEN S1 =>
report "Entering S1, waiting for stop";
WAIT UNTIL stop = '1';
state <= S2;
WHEN OTHERS =>
END CASE;
end loop;
END PROCESS;
Now, the testbench just pulses start
twice (using 1 ps wide pulses, so '0' at time 0, '1' at time 1 ps, '0' at time 2 ps, and '1' at time 3 ps). Watch what happens:
VSIM 1> run 5 ps
# ** Note: Entering S0, waiting for start
# Time: 0 ps Iteration: 0 Instance: /foo
# ** Note: Entering S0, waiting for start
# Time: 1 ps Iteration: 1 Instance: /foo
# ** Note: Entering S1, waiting for stop
# Time: 3 ps Iteration: 1 Instance: /foo
See the transition to S1? This is because your code is doing this.
- Assume a starting state of S0
- Wait for
start
to change and become 1
- Schedule
state
to go to S1. Note state is not yet S1. The non-blocking assignment allows execution to continue before the assignment is complete.
- Restart the process.
- State is still S0 (the scheduled update to
state
hasn't occurred yet).
- Wait for
start
to change and become 1. This completes the time interval and now state
changes to S1.
- Schedule
state
to go to S1. It is already S1.
- Restart the process.
- State is now S1. Wait for
stop
to change and become 1. This complete the time interval and now state
changes (again) to S1.
So, there's two ways to fix it. The first is to assign state
before waiting for the signal to change. For example:
main : PROCESS
BEGIN
while true loop
CASE state IS
WHEN S0 =>
report "Entering S0, waiting for start";
state <= S1;
WAIT UNTIL start = '1';
WHEN S1 =>
report "Entering S1, waiting for stop";
state <= S2;
WAIT UNTIL stop = '1';
WHEN OTHERS =>
END CASE;
end loop;
END PROCESS;
Now, look what happens with the same stimulus on start
:
VSIM 1> run 5 ps
# ** Note: Entering S0, waiting for start
# Time: 0 ps Iteration: 0 Instance: /foo
# ** Note: Entering S1, waiting for stop
# Time: 1 ps Iteration: 1 Instance: /foo
In fact, this is the kind of style recommended for register inference. First assign the new value, then wait for the event.
But since this is a model, we can go one farther. We can use a variable
and then we can do a blocking assignment. This blocks execution until the assignment is complete. For example:
main : PROCESS
variable state : states := S0;
BEGIN
while true loop
CASE state IS
WHEN S0 =>
report "Entering S0, waiting for start";
WAIT UNTIL start = '1';
state := S1;
WHEN S1 =>
report "Entering S1, waiting for stop";
WAIT UNTIL stop = '1';
state := S2;
WHEN OTHERS =>
END CASE;
end loop;
END PROCESS;
Note that I've made state
a variable, and now use the blocking assignment (:=
). And the result:
VSIM 1> run 5 ps
# ** Note: Entering S0, waiting for start
# Time: 0 ps Iteration: 0 Instance: /foo
# ** Note: Entering S1, waiting for stop
# Time: 1 ps Iteration: 1 Instance: /foo
Indeed, I can put the assignment before or after the wait
statement and get the same result.
Your solution to add a wait for time_value
is problematic. It introduces a delay where one isn't necessary and perhaps not desirable. I recommend you change your model to the variable
based example for more obvious behavior.
stop
before entering the state?wait
is waiting for change and the condition. \$\endgroup\$start
andstop
are both low first. That's why I can't get why it doesn't work (I tried withrising_edge
too...) \$\endgroup\$