Most of us stick to a certain way of writing a state machine. Perhaps you type out the construct that you are most familiar with without giving much thought to the alternatives. Depending on the method that you were taught when learning VHDL, you may prefer one method to another.

Over the years, I have seen many different state machine designs. To appease my curiosity, I set out to investigate the most common ways to design finite-state machines (FSMs) in VHDL. As it turns out, there are actually quite a few ways to write them.

The most apparent difference between FSMs written in VHDL, is the number of processes used. The FSM may be implemented entirely in one clocked process. Or it can be split up into one synchronous process and one or two combinatorial processes. Namely the two-process or three-process state machine.

The number of processes is not the only thing that sets FSM designs apart. They can also be divided into two different behavior types; Mealy and Moore machines. As we shall see, some designs can be used for both, while others are restricted to implementing Moore machines.

The testcase

We are going to implement this state machine diagram in the subsequent sections:
Moore type state machine diagram

The FSM consists of two states; S0 and S1. The output value in each state is denoted below the state names; Value0 and Value1. The vertices represent state changes, and the text on the vertices are boolean conditions that will cause the FSM to take that path. They are simply named Condition0 and Condition1.

Assume that Value0 and Value1 are constants.

One-process state machine

The one-process FSM design is the one you are most likely to encounter. It is usually implemented as a fully synchronous process. This results in the state, as well as the output being implemented in registers.

Learn how to create a traffic lights controller using a one-process FSM!

An advantage is that synchronous designs usually are easier to debug. Depending on the enclosing logic, it could also make it easier to meet timing requirements during place and route.

Below is a one-process implementation of the FSM.

process(Clk) is
begin
   if rising_edge(Clk) then
       if nRst = '0' then
           State <= S0;
           Dout  <= Value0;

       else

           case State is

               when S0 =>
                   Dout <= Value0;

                   if Condition0 then
                       State <= S1;
                   end if;

               when S1 =>
                   Dout <= Value1;

                   if Condition1 then
                       State <= S0;
                   end if;

           end case;
       end if;
   end if;
end process;

A consequence of setting the output within the state that it belongs to, is that the output signal will change one clock cycle after the state signal. This is generally not a problem when it comes to the function of the FSM. But as we can see from the waveform below, it can be a little confusing to make sense of during a debugging session.

Moore type state machine waveform

Mealy and Moore

FSMs can generally be divided into two types; Mealy machines and Moore machines. All of the above belong to the latter. The FSM diagram, the VHDL code, and the waveform are all related to the Moore machine.

The Mealy and Moore machines are named after their creators. They were thought out long before any FPGAs ever existed. The concept of state machines is universal, but comes very handy when describing the behavior of digital logic.

Mealy can do more than Moore

The difference between a Mealy machine and a Moore machine is how and when the outputs are changed. In a Moore machine, the outputs are set based on the current state. In a Mealy machine on the other hand, the outputs are set when the state change happens.

Consider this example which is the one-process state machine from the top of this article converted into a Mealy type state machine:

process(Clk) is
begin
   if rising_edge(Clk) then
       if nRst = '0' then
            State <= S0;
            Dout  <= Value0;

       else

           case State is

               when S0 =>
                   if Condition0 then
                       State <= S1;
                       Dout  <= Value1;
                   end if;

               when S1 =>
                   if Condition1 then
                       State <= S0;
                       Dout  <= Value0;
                   end if;

           end case;
       end if;
   end if;
end process;

The assignment to the output signal Dout has been moved inside of the If-statement. At the same time, the State signal is changed. One advantage of this can be viewed in the waveform below. The state and output signals change simultaneously. Thus, making debugging easier.

Mealy type state machine waveform

State machine diagrams for Mealy machines are usually drawn differently than for Moore machines. While the diagram shown earlier in this article was a Moore type diagram, this is the equivalent Mealy type diagram:

Moore machine state diagram

The notation on the vertices is on the format input / output.

Using Mealy machines sometimes result in state machines with fewer states than a Moore machine would require. This is because they can eliminate the need for some transitional states.

Two-process state machine

Some engineers prefer to divide the FSM code into two processes. One synchronous, and the other combinatorial. To add to the confusion, there are at least three different ways that a two-process state machine can be implemented.

Sequential process controls current State

In this implementation, a NextState signal exists in addition to the usual State signal. The synchronous process only controls the State signal. All other related signals are set by the combinatorial process as a result of a changed state or condition.

process(Clk) is
begin
    if rising_edge(Clk) then
        if nRst = '0' then
            State <= S0;

        else
            State <= NextState;

        end if;
    end if;
end process;

process(State, Condition0, Condition1) is
begin
    NextState <= State;

    case State is

        when S0 =>
            Dout <= Value0;

            if Condition0 then
                NextState <= S1;
            end if;

        when S1 =>
            Dout <= Value1;

            if Condition1 then
                NextState <= S0;
            end if;

    end case;
end process;

Although this is a Moore machine, the state signal will be aligned with the output signal. This is due to the extra NextState signal which is set by the combinatorial process. In the waveform below we can see that the State and Dout signals are in sync, and the NextState signal precedes them by one clock cycle.

Two-process state machine waveform

Another thing to be aware of is that although the output and state signals are synchronous in time, the output will be delayed by one delta cycle. As we can see from the waveform below, Dout changes one delta cycle after the State signal.

Delta cycle delay of the output following a change on the state signal

A delta cycle is a zero-time unit used by VHDL simulators to model changes in signal values in combinatorial processes. Depending on the logic in the downstream data path, this may affect the synthesized netlist. Or it may cause the behavior in simulation to differ from the behavior of the implemented design. There is nothing magical or mystical about delta cycles, but that is out of the scope of this article.

Read more about delta cycles here

Sequential process controls state

In this variant of the two-process state machine, there is no NextState signal. The sequential process entirely controls the State signal. The combinatorial process is sensitive to state changes, and sets the output signal accordingly.

process(Clk) is
begin
    if rising_edge(Clk) then
        if nRst = '0' then
            State <= S0;

        else
            case State is

                when S0 =>
                    if Condition0 then
                        State <= S1;
                    end if;

                when S1 =>
                    if Condition1 then
                        State <= S0;
                    end if;

            end case;

        end if;
    end if;
end process;

process(State) is
begin
    case State is

        when S0 =>
            Dout <= Value0;

        when S1 =>
            Dout <= Value1;

    end case;
end process;

This is also a Moore type state machine because the output is set based on the active state. While state and output signals will appear to be in sync in the waveform, the output is in fact delayed by a delta cycle.

Sequential process controls current state and output

Here, we are letting the synchronous process control the current state signal as well as the output signal. This implementation is logically equivalent to the Moore type one-process FSM mentioned at the beginning of this article.

process(Clk) is
begin
    if rising_edge(Clk) then
        if nRst = '0' then
            State <= S0;
            Dout  <= Value0;

        else
            State <= NextState;

            case State is

                when S0 =>
                    Dout <= Value0;

                when S1 =>
                    Dout <= Value1;

            end case;
        end if;
    end if;
end process;

process(State, Condition0, Condition1) is
begin
    NextState <= State;

    case State is

        when S0 =>
            if Condition0 then
                NextState <= S1;
            end if;

        when S1 =>
            if Condition1 then
                NextState <= S0;
            end if;

    end case;
end process;

By synchronizing both the state and output signal to the clock, we can ensure that they will be implemented in registers, regardless of how the downstream logic looks like. Also, there is no delta cycle delay between the state and output signals.

The choice between using the one-process FSM or this two-process equivalent is entirely down to preference. Some people think that factoring out the state changing code into a combinational process looks better. Others believe that splitting things that belong together and the extra NextState signal just makes it more complicated.

Three-process state machine

Some engineers prefer to split up code into as many processes as possible. Every signal would have its own process if possible. There certainly are advantages to this divide and conquer strategy. A process is a design unit, and it makes sense to make them as specialized as possible.

process(Clk) is
begin
    if rising_edge(Clk) then
        if nRst = '0' then
            State <= S0;

        else
            State <= NextState;

        end if;
    end if;
end process;

process(State) is
begin
    case State is

        when S0 =>
            Dout <= Value0;

        when S1 =>
            Dout <= Value1;

    end case;
end process;

process(State, Condition0, Condition1) is
begin
    NextState <= State;

    case State is

        when S0 =>
            if Condition0 then
                NextState <= S1;
            end if;

        when S1 =>
            if Condition1 then
                NextState <= S0;
            end if;

    end case;
end process;

This Moore type FSM uses next state logic. Therefore the State and Dout signals will appear to be in sync, while the Dout signal is in reality delayed by a delta cycle. It is essentially the same as the first two-process FSM mentioned in this article, but the output signal now has its own combinatorial process.

Which one should you use?

There is no right or wrong. It depends on a number of things. Do you need a Moore machine or a Mealy machine? Is the downstream logic that received the output signal synchronous or combinatorial? It also depends on which style of coding you prefer.

The one-process FSM is probably the most common type. But you will definitely encounter FSMs using multiple processes if you haven’t already.

I have found that preference for one type or the other sometimes depend on the background of the person. Engineers that have a computer science background, like I do, are more likely to choose the one-process FSM. Perhaps because they are used to thinking in a sequential manor when analyzing code.

Similarly, engineers that have an electronics background are more likely to use a two- or three-process FSM. Older, more experienced individuals, are more likely to prefer multi-process variants (perhaps we should listen to them).

These are of course entirely my subjective observations.

Whichever style you choose, make sure you so with Moore/Mealy machines and delta cycles in mind.

Read about delta cycles here

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 thoughts on “One-process vs two-process vs three-process state machine

  1. Dear Jonas
    Hi,
    Thanks for sharing, It’s inspiring and very helpful.

    Please keep u the nice work.

    Best Regards

    Posted on October 8, 2018 at 1:57 pm
    1. Thank you!

      I’m glad you found the blog post helpful. Let me know if there is something in particular you would like to see an article about.

      Jonas

      Posted on October 8, 2018 at 4:33 pm
  2. Regarding the one-process state machine, have you considered storing the state vector in a process variable?

        example_fsm : process(clock, reset)
            variable state      : fsm_type;
            variable next_state : fsm_type;
        begin
            if ((reset = RST_ACT_LVL) and (ASYNC_RST)) then
            test <= parity(test);
    
            elsif(rising_edge(clock)) then
                if ((reset = RST_ACT_LVL) and (not(ASYNC_RST))) then
    
                else
                    -- State case
                    case(state) is
                        when IDLE =>
                            next_state := STATE1;
    
                        when STATE1 =>
                            next_state := IDLE;
    
                        when others =>
                            null;
                    end case;
    
                    -- Commands
                    ---------------------------
                    -- Command description:
                    -- Transitions :
                    -- IDLE to STATE1
                    ---------------------------
                    if ((state = IDLE) and (next_state = STATE1)) then
                        null;
    
                    end if;
    
    
                    -- State progression
                    state := next_state;
                end if;
            end if;
        end process example_fsm;
    

    I feel like this method eliminates the discrepancy between signal and state value while keeping the other one-process-fsm advantages.

    Posted on June 26, 2019 at 12:22 pm
    1. That’s definitely a valid solution for a one-process FSM. I didn’t think of it because I generally try to avoid using variables in such a way that they result in registers. There’s nothing wrong with doing so, it’s just down to coding style. I’ve gone back and forth with this over the years 🙂

      A drawback of using variables for registers is that it can be difficult to predict what kind of hardware it will synthesize into, although in this example, it’s pretty clear. Also, they can be a bit harder to work with in the simulator. Not showing up in waveforms by default, etc.

      On the plus side, it’s nice to be able to encapsulate objects that belong to the process inside of it. You could do that with the state type as well.

      Posted on June 26, 2019 at 1:56 pm