State Machine Design & VHDL CodingThis section gives an overview of how to describe state machines with VHDL, it assumes some prior knowledge of state machine design. Translating the state diagram to VHDLThe starting point for most state machine designs is a state flow diagram which shows the various states and the transitions between them. It can also show any outputs that are generated when the state machine is in a particular state. An example is shown below: ![]() Example state machine - The state machine is clocked by a signal 'clk' which is not shown on the flow diagram. What information is important to us?
Defining the clockA state machine advances from one state to another when the particular state transition conditions are satisfied, synchronous to the state machine's clock input. This can be implemented in a process with the clock signal in the sensitivity list. The process will only be activated by events on the clock signal (clk) ensuring all signal and variable changes are synchronized to the clock. state_machine : PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '1') THEN -- sync to clk rising edge -- state transitions go here END IF; END PROCESS state_machine; All statements enclosed within IF (clk'EVENT AND clk='1') THEN...END IF; will only be evaluated when the (clk'EVENT AND clk='1') condition is true, i.e. when a rising edge occurs on the clk signal. Your synthesis tool should interpret this as positve edge clocked registers. Alternatively, a WAIT statement could be used, in which case a sensitivity list is not specified: state_machine : PROCESS BEGIN WAIT UNTIL (clk'EVENT AND clk = '0'); -- sync to clk falling edge -- state transitions here END PROCESS state_machine; All statements enclosed within WAIT UNTIL (clk'EVENT AND clk='0')...END PROCESS state_machine; will only be evaluated when the (clk'EVENT AND clk='0') condition is true, i.e. when a falling edge occurs on the clk signal. Your synthesis tool should interpret this as negative edge clocked registers (and will probably need to invert your clock signal). Note that in the first of these examples, the rising edge of clk is used to clock the state machine, in the second the falling edge is used. (see the section on signal attributes for more information). Synchronous & asynchronous initializationSome sort of initialization is usually necessary as the power-up state of the flip-flops may not be defined or a pushbutton reset may be included on the pcb. For simulation, std_logic type signals and variables will default to 'U' (uninitialized) if there is no explicit initialization. Let's look at synchronous initialization first and assume that that the state machine must transition to the initial state from any other state when the reset input is low. Using the template that we developed earlier: state_machine : PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '1') THEN -- sync to clk rising edge IF (reset = '0') THEN state_vector <= idle; ELSE -- state transitions here END IF; END IF; END PROCESS state_machine; The reset signal has not been included in the process sensitivity list, so events on the reset signal will not activate the process - it's still only the clock that activates the process. The second IF condition is only evaluated when a rising edge occurs on the clk signal, i.e. synchronous reset. The state_vector is either a VHDL signal or variable and it is assigned the value idle when the reset signal is '0' at a rising clock edge. What exactly state_vector and idle are will be decided by the state assignments that we choose. Now for the asynchronous initialization. This time the reset signal must be included in the process sensitivity list as the state machine must respond to events on both the clk signal and the reset signal - so an event on either signal will activate the process. state_machine : PROCESS (clk,reset) BEGIN IF (reset = '0') THEN -- asynch active low reset state_vector <= idle; -- to idle state on reset ELSIF (clk'EVENT AND clk = '1') THEN -- sync to clk rising edge -- state transitions here END IF; END PROCESS state_machine; Note that the reset has priority over the clock implicitly due to the fact that the IF statement is evaluated in sequential order and hence the initialization condition is checked first - if it evaluates to TRUE the state_vector will be assigned the value idle regardless of the value of clk. State transitionsThe templates we have developed so far allow us to define where our example state machine will start from (the initial state is idle) and when the state transitions will take place (synchronous with the rising or falling edge of clk). We now need to define the conditions that cause the state transitions to happen. Examining the example state diagram above we see that :
The generally accepted method of coding a state flow is by using a CASE...WHEN construct with one WHEN for each state. The transition conditions for each state are described by IF...THEN...ELSE statements. The VHDL process will now look like this: state_machine : PROCESS (clk,reset) BEGIN IF (reset='0') THEN -- asynch active low reset state_vector <= idle; -- to idle state on reset ELSIF (clk'EVENT AND clk='1') THEN -- sync to clk rising edge -- state transitions CASE state_vector IS WHEN idle => -- when in idle.. IF (start = '0') THEN -- ..wait for start=0 state_vector <= state1; -- ..then go to state1 ELSE state_vector <= idle; -- ..else stay in idle END IF; WHEN state1 => -- in state 1.. IF (branch = '1') THEN -- ..if branch=1 state_vector <= state4; -- ..then go to state4 ELSE state_vector <= state2; -- ..else go to state2 END IF; WHEN state2 => state_vector <= state3; -- go to state3 WHEN state3 => IF (hold = '0') THEN -- ..if hold=0 state_vector <= state4; -- ..then go to state4 ELSE state_vector <= state_vector; -- ..else stay in state3 END IF; WHEN state4 => state_vector <= state5; -- go to state5 WHEN state5 => state_vector <= idle; -- go to idle WHEN OTHERS => -- all other vector values state_vector <= idle; -- ..go to idle END CASE; END IF; END PROCESS state_machine; The last remaining step is to choose values for the states. Manual State encodingNote : State encoding is also known as state assignment and the term 'explicit' is sometimes used instead of 'manual'. Binary encodingUsing binary encoding, each state is assigned a unique binary number. Since our example has 6 states, we will require 3 bits to fully encode it and we will choose to assign the value "000" to idle, "001" to state1, "010" to state2 and so on. There is no strict reason to use this assignment order, but assigning "000" to idle (the initial state) makes sense as the flip-flops inside most types of FPGA will power-up already cleared (i.e. at logic '0') or will be cleared by their reset input. (this restriction does not apply to Xilinx FPGAs). The state encodings are explicitly declared as constants, either in the architecture declarative section or in the process declarative section or in a separate VHDL package - however declaring them in the architecture declarative section is more usual as they generally need only be visible inside the architecture where the state machine process resides. If they need to be visible outside of the architecture (i.e. used by other design entities), then a package can be used. ARCHITECTURE arc_bincoded OF bincoded IS -- sequential binary state machine encoding CONSTANT idle : std_logic_vector(2 DOWNTO 0) := "000"; CONSTANT state1 : std_logic_vector(2 DOWNTO 0) := "001"; CONSTANT state2 : std_logic_vector(2 DOWNTO 0) := "010"; CONSTANT state3 : std_logic_vector(2 DOWNTO 0) := "011"; CONSTANT state4 : std_logic_vector(2 DOWNTO 0) := "100"; CONSTANT state5 : std_logic_vector(2 DOWNTO 0) := "101"; BEGIN Gray encodingThis is a form of binary encoding in which only one bit of the state vector changes when moving from state to state so that the state vector doesn't pass through any intermediate values - important if it's being decoded asynchronously elsewhere in the FPGA. The states are then known as "adjacent states". The VHDL code for the state assignments is similar to the binary coding: ARCHITECTURE arc_graycoded OF graycoded IS -- gray state machine encoding CONSTANT idle : std_logic_vector(2 DOWNTO 0) := "000"; CONSTANT state1 : std_logic_vector(2 DOWNTO 0) := "100"; CONSTANT state2 : std_logic_vector(2 DOWNTO 0) := "101"; CONSTANT state3 : std_logic_vector(2 DOWNTO 0) := "111"; CONSTANT state4 : std_logic_vector(2 DOWNTO 0) := "110"; CONSTANT state5 : std_logic_vector(2 DOWNTO 0) := "010"; BEGIN Enumerated typeFirst an enumeration type 'states' is declared whose range of possible values are the names of the states in the state machine. Then the state vector is declared as a signal of type states: ARCHITECTURE arc_enumtype OF enumtype IS -- enumerated type state machine encoding TYPE states IS (idle,state1, state2, state3, state4, state5); SIGNAL state_vector : states; BEGIN It is not immediately clear from this exactly how many bits are required to encode all the states (i.e. exactly how many bits are required for the state vector) and what the state encoding will be. Most VHDL compilers will correctly synthesise the required number of bits and default to a sequential binary state assignment, ie. "000", "001"...."101" as in our binary encoding example. The default state encoding can be overidden by using the enum_encoding attribute: ARCHITECTURE arc_enumtype OF enumtype IS -- enumerated type state machine encoding TYPE states IS (idle,state1, state2, state3, state4, state5); ATTRIBUTE enum_encoding : string; ATTRIBUTE enum_encoding OF states : TYPE IS "000 100 101 111 110 010"; SIGNAL state_vector : states; BEGIN This will assign the values "000"; to idle, "100" to state1, etc. The state assignment in this example is gray coded and the synthesis results are the same as the gray encoding example. This technique could be applied equally well to the binary coding scheme. One-hot encoding (state-per-bit)The one-hot encoding technique was developed as a consequence of the typical FPGA architecture - rich in registers but with limited fan-in to each internal logic block. It is sometimes known as state-per-bit encoding as the state vector has one bit for each state in the state machine. For more information, see the One-hot state machine & VHDL coding section. Automatic State AssignmentSome VHDL synthesis tools will allow the state encoding scheme to be selected automatically using a 'synthesis directive' which takes the form of a VHDL attribute. For example, the Metamor compiler will automatically generate a one-hot encoded state machine if the following code is included: ARCHITECTURE arc_onehot OF onehot IS -- enumerated type state machine encoding TYPE states IS (idle,state1, state2, state3, state4, state5); ATTRIBUTE enum_encoding OF states : TYPE IS "one hot"; SIGNAL state_vector : states; BEGIN Refer to the documentation supplied with the VHDL synthesis tool you are using. Two process styleSo far all the examples have used a single process to describe the state machine. A popular coding style is to use two processes, a combinatorial process and a synchronous process. In the combinatorial process, the state transition conditions are described in terms of the present state (and any inputs) and in the synchronous process the clocking is defined. Using this style for our example state machine (with asynchronous initialization and explicit binary state encoding) gives : ARCHITECTURE arc_twoproc OF twoproc IS -- sequential binary state machine encoding CONSTANT idle : std_logic_vector(2 DOWNTO 0) := "000"; CONSTANT state1 : std_logic_vector(2 DOWNTO 0) := "001"; CONSTANT state2 : std_logic_vector(2 DOWNTO 0) := "010"; CONSTANT state3 : std_logic_vector(2 DOWNTO 0) := "011"; CONSTANT state4 : std_logic_vector(2 DOWNTO 0) := "100"; CONSTANT state5 : std_logic_vector(2 DOWNTO 0) := "101"; SIGNAL present_state : std_logic_vector(2 DOWNTO 0); SIGNAL next_state : std_logic_vector(2 DOWNTO 0); BEGIN -- the combinatorial process comb_proc : PROCESS (present_state,start,branch,hold) BEGIN -- state transitions CASE present_state IS WHEN idle => -- when in idle .. IF (start = '0') THEN -- ..wait for start=0 next_state <= state1; -- ..then go to state1 ELSE next_state <= idle; -- ..else stay in idle END IF ; WHEN state1 => -- in state 1.. IF (branch = '1') THEN -- ..if branch=1 next_state <= state4; -- ..then go to state4 ELSE next_state <= state2; -- ..else pass to state2 END IF ; WHEN state2 => next_state <= state3; -- go to state3 WHEN state3 => IF (hold = '0') THEN -- ..if hold=0 state_vector <= state4; -- ..then go to state4 ELSE state_vector <= state_vector; -- ..else stay in state3 END IF ; WHEN state4 => next_state <= state5; -- go to state5 WHEN state5 => next_state <= idle; -- go to idle WHEN OTHERS => -- all other vector values next_state <= idle; -- ..go to idle END CASE; END PROCESS comb_proc; -- the synchronous process sync_proc : PROCESS (clk,reset) BEGIN IF (reset = '0') THEN -- async active low reset present_state <= idle; -- to idle state on reset ELSIF (clk'EVENT AND clk = '0') THEN -- sync to clk falling edge present_state <= next_state; -- change state END IF ; END PROCESS sync_proc; END ARCHITECTURE arc_twoproc; Synchronous initialization of two process state machineTo implement a synchronous initialization in a two process state machine, just change the synchronous process as follows: -- the synchronous process sync_proc : PROCESS (clk) BEGIN IF (clk'EVENT AND clk = '0') THEN -- sync to clk falling edge.. IF (reset = '0') THEN -- async active low reset present_state <= idle; -- to idle state on reset ELSE present_state <= next_state; -- ..change state END IF; END IF; END PROCESS sync_proc; END ARCHITECTURE arc_twoproc; Using WHEN OTHERSThe CASE statements used in our examples all include the WHEN OTHERS condition which defines the state machine action when the state vector has a value that is not covered by the state assignments - the state machine is in an illegal state. This can happen in real-world design due to such things as noise, power glitches or flip-flop setup and hold violations. The designer can choose to make the state machine 'fault tolerant' by forcing the state machine to a legal state (usually the initial state) whenever it is in an illegal state using the WHEN OTHERS as follows: WHEN OTHERS => -- all other vector values next_state <= idle; -- ..go to idle This however consumes a lot more logic resources. If fault recovery is not required then the designer can choose to define the uncovered values as 'don't care': WHEN OTHERS => -- all other vector values next_state <= (OTHERS =>'-'); -- ..don't care This will allow the logic synthesis tool to minimize the state transition equations. Note that there must be one '-' for each bit in the state vector. The WHEN OTHERS condition may also be needed for simulation even if all the states have been assigned - the state vector is usually defined as being a std_logic_vector type, and so each bit in the state vector has 9 possible values, not just '1' or '0'. Generating other outputsMost state machines are used to generate other outputs, not just the state vector itself. These other outputs are generated either from the state vector only (Moore machine) or from a combination of the state vector and other inputs (Mealy machine). As an example, we will generate two outputs from our example state machine; out1 will be active high in state2 and state3 and out2 will be active high in state3 if an additional input, in1, is active high. The timing is shown below: ![]() The outputs can be generated in one of several ways : As a combinatorial function of the present state vector and the inputs![]() The output decoding logic introduces an extra propagation delay between the clock edge and the output signals. Assuming that the rising edge of clk is used to clock the present state registers (i.e. the state vector) then the clock edge to output delay is as shown below: ![]() The total delay from the rising clock edge to when the output changes state is made up of the delay from the clock edge to when the state vector changes state (Tco) plus the propagation delay in the output decoding logic (Tpd). If the state machine has been coded in the single process style, the state vector must be declared as a signal (so that it's visible outside the process), and then the outputs can be generated using a conditional signal assignment (WHEN...ELSE): out1 <= '1' WHEN ((state_vector=state2) OR (state_vector=state3)) ELSE '0'; out2 <= '1' WHEN (in1='1' AND state_vector=state3) ELSE '0'; If the two process style has been used, the output assignments can be placed inside a separate combinational process (i.e. a third process), with a signal assignment in each WHEN clause: -- the output combinatorial process out_comb_proc : PROCESS (present_state) BEGIN -- state transitions CASE present_state IS WHEN idle => out1 <= '0'; out2 <= '0'; WHEN state1 => out1 <= '0'; out2 <= '0'; WHEN state2 => out1 <= '1'; -- out1 goes active out2 <= '0'; WHEN state3 => out1 <= '1'; -- out1 still active out2 <= in1; -- out1 goes active if in=1 WHEN state4 => out1 <= '0'; out2 <= '0'; WHEN state5 => out1 <= '0'; out2 <= '0'; WHEN OTHERS => out1 <= '0'; out2 <= '0'; END CASE; END PROCESS out_comb_proc; As a synchronous function of the present state vector and the inputs.![]() This is best described using a multi-process style as the code for the outputs can be kept seperate from the code for the state vector. ARCHITECTURE arc_decpstate OF decpstate IS -- sequential binary state machine encoding CONSTANT idle : std_logic_vector(2 DOWNTO 0) := "000"; CONSTANT state1 : std_logic_vector(2 DOWNTO 0) := "001"; CONSTANT state2 : std_logic_vector(2 DOWNTO 0) := "010"; CONSTANT state3 : std_logic_vector(2 DOWNTO 0) := "011"; CONSTANT state4 : std_logic_vector(2 DOWNTO 0) := "100"; CONSTANT state5 : std_logic_vector(2 DOWNTO 0) := "101"; SIGNAL present_state : std_logic_vector(2 DOWNTO 0); SIGNAL next_state : std_logic_vector(2 DOWNTO 0); SIGNAL next_out1 : std_logic; SIGNAL next_out2 : std_logic; BEGIN -- the next state combinatorial process state_comb_proc : PROCESS (present_state,start,branch,hold) BEGIN -- state transitions CASE present_state IS WHEN idle => -- when in idle .. IF (start = '0') THEN -- ..wait for start=0 next_state <= state1; -- ..then go to state1 ELSE next_state <= idle; -- ..else stay in idle END IF; WHEN state1 => -- in state 1.. IF (branch = '1') THEN -- ..if branch=1 next_state <= state4; -- ..then go to state4 ELSE next_state <= state2; -- ..else pass to state2 END IF; WHEN state2 => next_state <= state3; -- go to state3 WHEN state3 => IF (hold = '0') THEN -- ..if hold=0 next_state <= state4; -- ..then go to state4 ELSE next_state <= next_state; -- ..else stay in state3 END IF; WHEN state4 => next_state <= state5; -- go to state5 WHEN state5 => next_state <= idle; -- go to idle WHEN OTHERS => -- all other vector values next_state <= idle; -- ..go to idle END CASE; END PROCESS state_comb_proc; -- the next output combinatorial process out_comb_proc : PROCESS (present_state, hold) BEGIN -- output transitions CASE present_state IS WHEN idle => next_out1 <= '0'; next_out2 <= '0'; WHEN state1 => next_out1 <= '1'; next_out2 <= '0'; WHEN state2 => next_out1 <= '1'; next_out2 <= in1; WHEN state3 => IF (hold = '1') THEN next_out1 <= '1'; next_out2 <= in1; ELSE next_out2 <= '0'; next_out1 <= '0'; END IF; WHEN state4 => next_out1 <= '0'; next_out2 <= '0'; WHEN state5 => next_out1 <= '0'; next_out2 <= '0'; WHEN OTHERS => next_out1 <= '0'; next_out2 <= '0'; END CASE; END PROCESS out_comb_proc; -- the synchronous process sync_proc : PROCESS (clk,reset) BEGIN IF (reset = '0') THEN -- async active low reset present_state <= idle; -- to idle state on reset out1 <= '0'; -- set out1 low on reset out2 <= '0'; -- set out2 low on reset ELSIF (clk'EVENT AND clk = '0') THEN -- sync to clk falling edge.. present_state <= next_state; -- ..change state out1 <= next_out1; -- ..change output out1 out2 <= next_out2; -- ..change output out2 END IF; END PROCESS sync_proc; END ARCHITECTURE arc_decpstate; The main advantage of a state machine implemented in this way, is that the time delay from the clock edge to when the outputs change state is the same as that for the state vector : ![]() However the outputs can only change state when a clock edge occurs. One disadvantage of this coding style can be seen by examining the output combinatorial process: WHEN state1 => next_out1 <= '1'; next_out2 <= '0'; Our original specification for the outputs required out1 to go active high in state2, but the above code snippet seems to indicate that it goes high in state1, but what the code is really "saying" is that when the state vector changes from state1 to state2, then the signal out1 is assigned the value of the signal next_out1. We can overcome this slight difficulty by using the next state vector instead of the present state. As a synchronous function of the next state vector and the inputs![]() This is essentially the same as the previous coding style (and produces the same timing) but avoids the problem of outputs being specified with the states preceding the one in which they are actually active. The only change to the code is in the outputs combinatorial process. The next state vector (next_state)is now included in the sensitivity list instead of the present state vector: -- the next output combinatorial process out_comb_proc : PROCESS (next_state, in1) BEGIN -- output transitions CASE next_state IS WHEN idle => next_out1 <= '0'; next_out2 <= '0'; WHEN state1 => next_out1 <= '0'; next_out2 <= '0'; WHEN state2 => next_out1 <= '1'; next_out2 <= '0'; WHEN state3 => next_out1 <= '1'; next_out2 <= in1; WHEN state4 => next_out1 <= '0'; next_out2 <= '0'; WHEN state5 => next_out1 <= '0'; next_out2 <= '0'; WHEN OTHERS => next_out1 <= '0'; next_out2 <= '0'; END CASE; END PROCESS out_comb_proc; The other difference that can be seen is for the output generation in state3: WHEN state3 => next_out1 <= '1'; next_out2 <= in1; The next state vector is generated from the present state vector and the start, branch and hold inputs - so the code to generate the outputs when the next state vector is state3 isn't qualified with hold. Maintained by Mark Harvey. Please email me with any comments. |