Implementing a FSM in VHDL
Asked Answered
D

6

6

Just wondering if I'm implementing a finite state machine in VHDL whether or not I need to state what all of the outputs are in every possible state? Even if I know that some outputs won't change from one state to the other and I know that the order of the states will also be in the same order?

For example, in this (forced) example:

entity test is
    port (
        clk : in std_logic;
        a : in std_logic;
        b: out std_logic;
        c: out std_logic;
    );
end test;

architecture Behavioral of test is

type executionStage is (s1,s2,s3);
signal currentstate, nextstate: executionStage;

begin
    process (clk)
    begin
          if(rising_edge(clk)) then
                 currentstate <= nextstate;
          else 
                 currentstate <= currentstate;
          end if;
    end process;

    process(currentstate)
    begin
        case currentstate is
            when s1 =>
                if (a = '1') then
                    b <= '1';
                    c <= '0';
                else
                    b <= '1';
                    c <= '1';
                end if;

                nextstate <= s2;

            when s2 =>
                -- b doesnt change state from s1 to here, do I need to define what it is here?
                if (a = '1') then
                    b <= '1';
                    c <= '1';
                else
                    b <= '1';
                    c <= '0';
                end if;

                nextstate <= s3;

            when s3 =>
                if (a = '1') then
                    b <= '0';
                    c <= '0';
                else
                    b <= '1';
                    c <= '1';
                end if;

                nextstate <= s1;
        end case;
    end process;
end Behavioral;

From my understanding if I don't do this then latches are created?

It's not a big deal in something like that example but if I have a machine with more than 10 outputs and more than 10 states then my VHDL files start to look incredibly messy and I'm sure it must be bad practice to copy and paste the same thing over and over. Is there a better way of doing this?

edit: Can I define a 'default' state for an ouput? IE set b to be 1 outside of all the processes and then only define what it is in the case statements where it is 0? Would that work?

Duque answered 14/5, 2011 at 9:47 Comment(0)
M
7

Yes, you will infer latches if you only drive signals intended to be combinatorial in some branches of the process.

However, you can define a 'default' state for the signal simply by assigning a value to it before the case statement (but within the same process). For example:

process(currentstate, a)
begin
    b <= '1';
    c <= '1';
    case currentstate is
        when s1 =>
            if (a = '1') then
                c <= '0';
            end if;

            nextstate <= s2;

        when s2 =>
            -- b doesnt change state from s1 to here, do I need to define what it is here?
            if (a /= '1') then
                c <= '0';
            end if;

            nextstate <= s3;

        when s3 =>
            if (a = '1') then
                b <= '0';
                c <= '0';
            end if;

            nextstate <= s1;
    end case;
end process;
Mucker answered 14/5, 2011 at 14:53 Comment(4)
Thanks Tomi! So in my example I could set b <= '1' in the process(currentstate) and then I would only have to set the value in the s3 state? Would I still have to set it for both the if and else or would it be sufficient to only set it where b is equal to 0? So only in the else statement?Duque
Basically I'm wondering I can set a default value that can be 'inherited' by all other case/if statements within the first case statement. Or do I need to set a new default within all case/if statements?Duque
You just need to ensure a value is assigned to the signal in every branch of the branch (i.e. that there is no path through the process in which the signal is not assigned a value). This is easiest to accomplish by just assigning a reasonable default value outside the case statement. There is no way to do that within the case statement.Mucker
I updated my response to include an example. In doing that, I realized I probably misunderstood your comment. Yes, it is possible to assign the value within a when statement, but outside an if statement contained in that. There is no requirement to have just a single statement within a when (which is already evident in your code, since you assign nextstate outside the if statement.Mucker
E
7

Three problems with your example code:

The last port in your port list should not have a semicolon:

port (
    clk : in std_logic;
    a : in std_logic;
    b: out std_logic;
    c: out std_logic -- no semicolon here!!!
    );

In your register process, you should not have an "else" statement. While this will probably be accepted by the tools, it will confuse your fellow-VHDL designers.

process (clk)
begin
    if(rising_edge(clk)) then
        currentstate <= nextstate;
    end if;
end process;

In your combinational logic, the sensitivity list should contain all signals that you read: process(a, currentstate). In this particular case (again) things will probably work out fine, but you are bound to infer latches or cause other problems if your sensitivity list is not correct.

As for your question:

  1. Yes, you need to assign a value (for each state) to each signal in the combinational process.
  2. As Tomi mentions, you can easily do this by assigning a default value in the beginning of the process.
  3. But you can also write the entire state machine in one single synchronous process. This way, you do not have to assign a value to every signal in every state.
Eterne answered 15/5, 2011 at 12:58 Comment(1)
Thanks Philipe! Really appreciate the info. :)Duque
T
3

Just a note to Philippe's response (can't comment on it directly?)..

I do prefer to write state machines in the two process style. It makes it very clear where you expect inferred flipflops and where you don't. It's also a bit more along the lines of describing the hardware - imagine building a state machine with board level logic for example. The registered device matches the state <= next_state process, and the case statement maps to the and/or array in front of the state register..

Having said that, I typically use one process state machines for small simple tasks, and move over to two process machines for bigger ones. I will even sometimes use a third process for organizing state outputs into different "task" groups.. but not often. A really large state machine tends to tell me the architecture needs work..

Tegan answered 18/10, 2012 at 20:56 Comment(0)
B
0
process (clk)
begin
  if(rising_edge(clk)) then
    currentstate <= nextstate;
  end if;
end process;

Hi

the above process is problematic but not due to the sensitivity list. It is ok to only declare clk for sequential process. Both simulation and synthesis tools won't have problems with it. clk is the fastest changing/transitioning signal after all in your code.

However, you should use an (preferrably) asynchronous reset. Of course, vendors nowadays say that for FPGA design, resets are not even necessary; they happen at boot time. Or they propose a synchronous reset.

Still, an asynchronous reset is valuable for a board-based environment.

In short: add a reset to your design and fix its behavior properly.

Kind regards Nikolaos Kavvadias

Bort answered 23/6, 2011 at 10:45 Comment(0)
A
0

The following VHDL code is level sensitive state machine. The level sensitive process in this example will make “out1” out of phase with “clk” and “out2” in phase with “clk”.

entity main_code is
    Port ( clk : in  STD_LOGIC;
           in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           out1 : out  STD_LOGIC;
           out2 : out  STD_LOGIC);
end main_code;

architecture Behavioral of main_code is

-- here are temp signals to associate or assign output (out1 and out2) values indirectly
signal out1_temp : std_logic := '0';  
signal out2_temp : std_logic := '0';

-- counter registers 
signal counter : integer range 0 to 255 := 0;
signal counter_8th_clk : integer range 0 to 255 := 0;
-- state machines definition
type state_machine_type is (s0,s1);
signal state : state_machine_type := s0;
begin
-- concurrent assignments
out1 <= out1_temp;
out2 <= out2_temp;

--half clock generator process
half_clock : process (clk) is
begin
if rising_edge(clk) then
--out1_temp <= not out1_temp;
end if;
end process half_clock;

-- max counter = ndiv -1; here ndiv=4; counter starts from zero;
one_fourth_clock : process (clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
    if (counter >= 3) then 
        counter <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_fourth_clock;


one_eighth_clock : process (clk)
begin
if rising_edge(clk) then
counter_8th_clk <= counter_8th_clk + 1;
    if (counter_8th_clk>=7) then
        counter_8th_clk <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_eighth_clock;

-- state_process creates two half clock (speed) with out1 out of phase with clk
-- and out2 in-phase with clk
-- following process is sensitive to clk level not edge
state_process_level_sensitive : process (clk)
begin
case state  is

    when s0 =>
        out1_temp <= not out1_temp;
        state <= s1;
    when s1 =>
        out2_temp <= not out2_temp;
        state <= s0;
end case;
end process state_process_level_sensitive;



end Behavioral;

here is the test bench

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY my_test_bench IS
END my_test_bench;

ARCHITECTURE behavior OF my_test_bench IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT main_code
    PORT(
         clk : IN  std_logic;
         in1 : IN  std_logic;
         in2 : IN  std_logic;
         out1 : OUT  std_logic;
         out2 : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal in1 : std_logic := '0';
   signal in2 : std_logic := '0';

    --Outputs
   signal out1 : std_logic;
   signal out2 : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: main_code PORT MAP (
          clk => clk,
          in1 => in1,
          in2 => in2,
          out1 => out1,
          out2 => out2
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
--      wait for 100 ns;    
--
--      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;
Ariadne answered 14/12, 2019 at 5:20 Comment(0)
A
0

The following VHDL code is edge sensitive state machine. The edge sensitive process in this example will make both “out1” and “out2” in phase with “clk”.

entity main_code is
    Port ( clk : in  STD_LOGIC;
           in1 : in  STD_LOGIC;
           in2 : in  STD_LOGIC;
           out1 : out  STD_LOGIC;
           out2 : out  STD_LOGIC);
end main_code;

architecture Behavioral of main_code is

-- here are temp signals to associate or assign output (out1 and out2) values indirectly
signal out1_temp : std_logic := '0';  
signal out2_temp : std_logic := '0';

-- counter registers 
signal counter : integer range 0 to 255 := 0;
signal counter_8th_clk : integer range 0 to 255 := 0;
-- state machines definition
type state_machine_type is (s0,s1);
signal state : state_machine_type := s0;
begin
-- concurrent assignments
out1 <= out1_temp;
out2 <= out2_temp;

--half clock generator process
half_clock : process (clk) is
begin
if rising_edge(clk) then
--out1_temp <= not out1_temp;
end if;
end process half_clock;

-- max counter = ndiv -1; here ndiv=4; counter starts from zero;
one_fourth_clock : process (clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
    if (counter >= 3) then 
        counter <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_fourth_clock;


one_eighth_clock : process (clk)
begin
if rising_edge(clk) then
counter_8th_clk <= counter_8th_clk + 1;
    if (counter_8th_clk>=7) then
        counter_8th_clk <= 0;
--      out2_temp <= not out2_temp;
    end if;
end if;
end process one_eighth_clock;

-- state_process creates two half clock (speed) with out1 out of phase with clk
-- and out2 in-phase with clk
-- following process is sensitive to clk level not edge
state_process_edge_sensitive : process (clk)
begin
if rising_edge (clk) then
case state  is

    when s0 =>
        out1_temp <= not out1_temp;
        state <= s1;
    when s1 =>
        out2_temp <= not out2_temp;
        state <= s0;
end case;
end if;

end process state_process_edge_sensitive;



end Behavioral;

here is the test bench

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;

ENTITY my_test_bench IS
END my_test_bench;

ARCHITECTURE behavior OF my_test_bench IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT main_code
    PORT(
         clk : IN  std_logic;
         in1 : IN  std_logic;
         in2 : IN  std_logic;
         out1 : OUT  std_logic;
         out2 : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal in1 : std_logic := '0';
   signal in2 : std_logic := '0';

    --Outputs
   signal out1 : std_logic;
   signal out2 : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: main_code PORT MAP (
          clk => clk,
          in1 => in1,
          in2 => in2,
          out1 => out1,
          out2 => out2
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
--      wait for 100 ns;    
--
--      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;
Ariadne answered 14/12, 2019 at 5:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.