This section gives an overview of how to describe one-hot state machines with VHDL, it assumes some prior knowledge of state machine design. Users should be familiar with the State Machine Design & VHDL Coding section before going ahead to this section.
A one-hot encoded state machine is a state machine
in which there is a bit in the state vector for every
state in the state machine. (eg. a state machine with
17 states will have a 17-bit wide state vector). It
is sometimes known as bit-per-state
Example state machine - The state machine is clocked by a signal 'clk' which is not shown on the flow diagram.
To detect when the state machine is in a particular state only requires that a single bit is tested instead of the whole state vector as would be the case for a binary-coded state machine. Changing states is simply a case of changing the currently 'hot' register to 'cold' and setting the next state register to 'hot'. (Note that 2 bits in the state vector change for every state transition - so caution must be used if the state vector is decoded asynchronously to generate other outputs.) The logic required to detect the current state to generate outputs and next state logic is minimal, requiring less combinatorial logic - so allowing for higher clock frequencies. This is the main reason for using the one-hot structure. Our example state machine has 6 states, so 6 registers will be required - the disadvantage of one-hot encoding is the larger quantity of registers that are required.
One-hot state machines require more flip-flops than a binary-coded state machine, so use them in devices that are register-rich but have low fan-in to each logic macrocell (e.g. Xilinx FPGAs). They are generally not useful in CPLDs such as the Xilinx 9500 or CoolRunner families since these have a wide fan-in to each logic macrocell.
There are 2 coding styles - "hard encoding" and "soft coding".
Soft encoding a state machine allows the state encoding to be quickly and easily changed. The basic technique is to declare an enumeration type which has the state names as the set of possible values, then declare the state vector(s) as signals of that type. The default encoding of the enumeration type is modified by the attribute "enum_encoding". The state assignments are made by positional association between the TYPE declaration and the ATTRIBUTE declaration. In our example listing below, idle takes the value "000001", state1 takes the value "000010" and so on. The rest of the state machine is coded as normal using a CASE construct. The following code snippet (Listing 1) shows our example state machine coded as a one-hot state machine using the two-process style:
ARCHITECTURE arc_softenc OF softenc IS -- declare the enumeration type TYPE states IS (idle,state1,state2,state3,state4,state5); -- assign the states ATTRIBUTE enum_encoding : string; ATTRIBUTE enum_encoding OF states: TYPE IS "000001 000010 000100 001000 010000 100000"; -- the state vectors SIGNAL present_state : states; SIGNAL next_state : states; 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 next_state <= state4; -- ..then go to state4 ELSE next_state <= state3; -- ..else stay in state3 END IF ; WHEN state4 => next_state <= state5; -- go to state5 WHEN state5 => 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_softenc;
Listing 1 : soft encoded one-hot state machine
Note the lack of a WHEN OTHERS clause - this will be discussed further in Undefined States.
A further extension of this idea is to replace the state assignments string with a text string that directs the synthesis tool to produces the required state encoding. For example the Metamor synthesis tool can produce a one-hot state machine by changing the expression in the attribute specification line to "one hot". The attribute specification line:
ATTRIBUTE enum_encoding OF states: TYPE IS "000001 000010 000100 001000 010000 100000";
ATTRIBUTE enum_encoding OF states: TYPE IS "one hot";
Hard encoding ensures that a one-hot state machine is always generated, regardless of the synthesis tool that is being used. This makes it less portable to other targets which might not be register-rich.
The CASE construct can't be used as it will use the entire state vector to make the state-to-state transition equations, and we need to use only the inputs and the currently 'hot' state. Each state is considered in turn, using an IF...THEN ...END IF; structure which describes when the state becomes 'hot' or 'cold'. The VHDL code for the example state machine is:
ARCHITECTURE arc_hardenc OF hardenc IS -- state vector, 6 bits for 6 states SIGNAL state_vector : std_logic_vector(5 DOWNTO 0); -- define hot & cold, must be defined before initial constant CONSTANT hot : std_logic := '1'; CONSTANT cold : std_logic := '0'; -- these constants act as indices to the state vector signal -- & must be defined before the initial state constant CONSTANT idle : integer := 0; CONSTANT state1 : integer := 1; CONSTANT state2 : integer := 2; CONSTANT state3 : integer := 3; CONSTANT state4 : integer := 4; CONSTANT state5 : integer := 5; -- initial state (idle is hot, all other states are cold) CONSTANT initial:std_logic_vector(5 DOWNTO 0):=(idle=>hot,OTHERS=>cold); BEGIN state_machine : PROCESS (clk,reset) BEGIN IF (reset='1') THEN -- async active hi reset state_vector <= initial; -- to idle state on reset ELSIF (clk'EVENT AND clk='1') THEN -- sync to clk rising edge -- idle IF ((state_vector(idle) = hot AND start = '1') OR state_vector(state5) = hot) THEN state_vector(idle) <= hot; ELSE state_vector(idle) <= cold; END IF; -- state 1 IF (state_vector(idle) = hot AND start = '0') THEN state_vector(state1) <= hot; ELSE state_vector(state1) <= cold; END IF; -- state 2 IF (state_vector(state1) = hot AND branch = '0') THEN state_vector(state2) <= hot; ELSE state_vector(state2) <= cold; END IF; -- state 3 IF (state_vector(state2) = hot OR (state_vector(state3) = hot AND hold = '1')) THEN state_vector(state3) <= hot; ELSE state_vector(state3) <= cold; END IF; -- state 4 IF ((state_vector(state3) = hot AND hold = '0') OR (state_vector(state1) = hot AND branch = '1')) THEN state_vector(state4) <= hot; ELSE state_vector(state4) <= cold; END IF; -- state 5 IF (state_vector(state4) = hot) THEN state_vector(state5) <= hot; ELSE state_vector(state5) <= cold; END IF; END IF; END PROCESS state_machine; END ARCHITECTURE arc_hardenc;
Listing 2 : hard encoded one-hot state machine
Note that 2 bits of the state vector change for each state transition: the present state goes cold, the next state goes hot, so be careful when asynchronously decoding the state vector to produce auxiliary outputs. It may be better to have synchronous outputs.
Because there is one flip-flop per state, some outputs may require a large number of product terms (fan-in) which defeats the purpose of one-hot encoding. Consider for example an output that is active in the first 8 states of a state machine with 16 states. With a one-hot coded state machine, this will require 8 product terms, but with a binary coded state machine it may require as little as 1 product term. However, an output which is valid in only one state may benefit from one-hot encoding as the output is just the state bit itself.
One concern that many designers have with one-hot state machines is the large number of undefined states. A binary-coded version of our example state machine requires a 3 bit state vector, and so the number of undefined states is:
23 - 6 = 2 undefined states.
The designer can take care of these undefined states using the WHEN OTHERS clause. See the State Machine Design & VHDL Coding section for more information.
A one-hot coded version of the example state machine however has many more undefined states because the state vector is 6 bits wide:
26 - 6 = 58 undefined states.
Using the WHEN OTHERS clause to take care of undefined states may not feasible because of the large amount of wide fan-in logic that will be generated.
WHEN OTHERS => next_state <= idle; -- illegal state, go to idle
The designer must decide what action should be taken when the state machine finds itself in an illegal / undefined state - should it go to a particular state? (perhaps the initial state), should it be reset asynchronously or synchronously? The designer can also decide to take no action on reaching an undefined state, allowing maximum logic minimisation, but risking a locked-up state machine. The consequences of these decisions will have far reaching effects on the amount of logic produced.
One-hot coding is used to increase the maximum clock frequency, so don't let the FPGA routing and layout cause any unnecessary loss due to excessive routing delays.
FPGA Express users have a further alternative to the hard and soft encoding styles described above. FPGA Express allows the designer to specify either one-hot or binary encoding as an synthesis option. The ENUM_ENCODING attribute must be left out to allow FPGA Express to choose the state encoding, just declare the enumeration type and the state vector(s):
ARCHITECTURE arc_fpgaexpenc OF fpgaexpenc IS -- declare the enumeration type TYPE states IS (idle,state1,state2,state3,state4,state5); -- DON'T USE THESE TWO LINES, ALLOW FPGA EXPRESS TO CHOOSE ENCODING! --ATTRIBUTE enum_encoding : string; --ATTRIBUTE enum_encoding OF states: TYPE IS "000001 000010 000100 -- 001000 010000 100000"; -- the state vectors SIGNAL present_state : states; SIGNAL next_state : states; BEGIN . .
Then in the Synopsys FPGA Express GUI (or the Xilinx Foundation GUI),
choose Synthesis -> Options, and click on the Project Tab of the Options
dialog box. Select the encoding style as required.
Maintained by Mark Harvey. Please email me with any comments.