VHDL State Machine
Quick Syntax
-- state enumeration
type sm_states is (idle, init, send, done);
signal sm_state : sm_states;
STATE_MACHINE : process (clk,reset,enable)
begin
if rising_edge(clk) then
if reset = '1' then
output_command <= x"00";
sm_state <= idle;
output_enable <= '0';
else
-- defaults
output_command <= x"00";
output_enable <= '0';
case sm_state is
when idle =>
if enable = '1' then
sm_state <= init;
else
sm_state <= idle;
end if;
when init =>
output_command <= x"1F";
sm_state <= send;
output_enable <= '1';
when send =>
output_command <= x"2E";
sm_state <= done;
output_enable <= '1';
when done =>
output_command <= x"7A";
sm_state <= idle;
output_enable <= '1';
when others =>
sm_state <= idle;
end case;
end if;
end if;
end process;
In the code snippet above, we can see that there are 4 states where the SM hangs out in idle. Once the incoming signal enable is '1', then we start our step by step execution through each of the states. Once we reach the done state, we return back to idle, where we wait for the next time enable is '1' again. This is perfect for sending a sequence of commands, writes, reads, signals, etc.
Purpose
State machines are simply a series of states that represent a step by step process (not to be confused with a VHDL process). They are used commonly in different designs like DDR memory interfaces, I2C, SPI, etc.
They are the preferred way of coding any sort of series of steps because they are easy to use, easy to understand, and easy to maintain. Typically, case statements are the main ingrediant in a state machine. Of course, you could code up equivalent logic with if statements, but that gets very messy and is prone to problems.
Best Practices
1. Use case statements in state machines.
2. Make sure that you enumerate your states since it's easier to code and ensures that you follow the states of whatever interface you are coding for.
3. Use when others in your case statement just in case someone in the future deletes a state and thinks its not necessary any more. It's a catch all if something goes wrong and you should use it to take your state machine back to idle.
4. For any signals being read in the state machine, they must be on the same clock domain so always use proper clock domain crossing here by double registerring these signals if they are coming from another clock domain. If incoming signals are on the same clock domain, don't worry about it.
5. Assuming you are using a process, use defaults at the top of your state machine for output signals. That way you only need to set output signals at each state that change from the default instead of driving every signal at every state which greatly multiplies and complicates your code.