1
\$\begingroup\$

I'm developing a simulation model of a component that I need in my design. To make it fast and simple I decided to only create it in a behavioral manner (eg not synthesizable).

To do so I'm using a CASE statement (representing an FSM) with WAIT statements inside.

main : PROCESS
BEGIN 
    CASE state IS
         WHEN S0 =>
            WAIT UNTIL start = '1';
            state <= S1;

         WHEN S1 =>
            WAIT UNTIL stop = '1';
            state <= S2;

         WHEN S2 =>

            ...

         WHEN OTHERS =>
    END CASE;
END PROCESS;

The problem is that state enters S1 when the startsignal is asserted but doesn't enter S2when stopis asserted.

I always thought that using a WAIT statement in a CASE stucked the process in a given state until the condition is valid but this example shows that I'm wrong. What do you guys think of this?

\$\endgroup\$
4
  • \$\begingroup\$ what is the value of stop before entering the state? wait is waiting for change and the condition. \$\endgroup\$
    – Eugene Sh.
    Commented May 26, 2016 at 13:00
  • \$\begingroup\$ startand stopare both low first. That's why I can't get why it doesn't work (I tried with rising_edge too...) \$\endgroup\$
    – A. Kieffer
    Commented May 26, 2016 at 13:05
  • \$\begingroup\$ As written it'll enter S1 then the process will end. Perhaps you meant to loop around the CASE statement? \$\endgroup\$
    – user16324
    Commented May 26, 2016 at 13:17
  • \$\begingroup\$ It actually loops around the CASE, but too fast. I needed some temporization at the end of the process to get the right behavior. Thank you for your advices. \$\endgroup\$
    – A. Kieffer
    Commented May 26, 2016 at 13:28

2 Answers 2

1
\$\begingroup\$

Well ...I finally found the issue.

For those who could also wonder why this isn't working, in fact you just need a WAIT statement at the end of the process (outside the CASE statement).

The thing is, when start goes high, state is actually updated and take its new value: S1. Yet, since I'm not using any sensitivity list, the process is running indefinitely and at the same time that state is updated, the process comes back into S0 waiting for another start signal ...

That's why you just need this at the end:

    ...
    END CASE
WAIT FOR time_value;
END PROCESS;
\$\endgroup\$
1
  • \$\begingroup\$ The problem is that signal assignment does not occur until the end of the time interval. So, your wait until ... statement waits. When the event occurs, then you assign the signal. But time has not yet advanced, so state is still at its previous value. The process restarts, and state is still S0, so it waits again. I'll elaborate in an answer. \$\endgroup\$
    – PlayDough
    Commented Jun 3, 2016 at 7:23
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.