Реализация FSM на VHDL

Просто интересно, реализую ли я конечный автомат на VHDL, нужно ли мне указывать, какие все выходные данные находятся в каждом возможном состоянии? Даже если я знаю, что некоторые выходы не будут меняться от одного состояния к другому, и я знаю, что порядок состояний будет таким же?

Например, в этом (принудительном) примере:

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;

Насколько я понимаю, если я этого не сделаю, тогда будут созданы защелки?

Это не имеет большого значения в чем-то вроде этого примера, но если у меня есть машина с более чем 10 выходами и более чем 10 состояниями, то мои файлы VHDL начинают выглядеть невероятно беспорядочно, и я уверен, что копирование и вставка должно быть плохой практикой. одно и то же снова и снова. Есть ли лучший способ сделать это?

edit: Могу ли я определить состояние «по умолчанию» для вывода? IE устанавливает b равным 1 вне всех процессов, а затем определяет, что это такое, только в операторах case, где оно равно 0? Будет ли это работать?


person Sam    schedule 14.05.2011    source источник


Ответы (6)


Да, вы сделаете вывод о защелках, если будете управлять только сигналами, предназначенными для комбинаторной обработки, в некоторых ветвях процесса.

Однако вы можете определить состояние «по умолчанию» для сигнала, просто присвоив ему значение перед оператором case (но в том же процессе). Например:

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;
person Tomi Junnila    schedule 14.05.2011
comment
Спасибо Томи! Итак, в моем примере я мог бы установить b ‹= '1' в процессе (текущее состояние), а затем мне нужно было бы только установить значение в состоянии s3? Придется ли мне по-прежнему устанавливать его как для if, так и для else, или будет достаточно установить его только там, где b равно 0? То есть только в операторе else? - person Sam; 15.05.2011
comment
В основном мне интересно, я могу установить значение по умолчанию, которое может быть «унаследовано» всеми другими операторами case/if в первом операторе case. Или мне нужно установить новое значение по умолчанию во всех операторах case/if? - person Sam; 15.05.2011
comment
Вам просто нужно убедиться, что сигналу присваивается значение в каждой ветви ветви (т. е. нет пути через процесс, в котором сигналу не присваивается значение). Этого проще всего добиться, просто назначив разумное значение по умолчанию вне оператора case. В операторе case это сделать невозможно. - person Tomi Junnila; 16.05.2011
comment
Я обновил свой ответ, чтобы включить пример. При этом я понял, что, вероятно, неправильно понял ваш комментарий. Да, можно присвоить значение внутри оператора when, но вне оператора if, содержащегося в нем. Нет необходимости иметь только один оператор в when (что уже очевидно в вашем коде, поскольку вы назначаете nextstate вне оператора if. - person Tomi Junnila; 16.05.2011

Три проблемы с вашим примером кода:

Последний порт в вашем списке портов не должен иметь точку с запятой:

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

В процессе регистрации у вас не должно быть оператора «else». Хотя это, вероятно, будет принято инструментами, это смутит ваших коллег-разработчиков VHDL.

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

В вашей комбинационной логике список чувствительности должен содержать все сигналы, которые вы считываете: process(a, currentstate). В этом конкретном случае (опять же) все, вероятно, будет работать нормально, но вы обязаны сделать вывод о защелках или вызвать другие проблемы, если ваш список чувствительности неверен.

Что касается вашего вопроса:

  1. Да, вам нужно присвоить значение (для каждого состояния) каждому сигналу в комбинационном процессе.
  2. Как упоминает Томи, вы можете легко сделать это, назначив значение по умолчанию в начале процесса.
  3. Но вы также можете написать весь конечный автомат в одном синхронном процессе. Таким образом, вам не нужно присваивать значение каждому сигналу в каждом состоянии.
person Philippe    schedule 15.05.2011
comment
Спасибо, Филипп! Очень ценю информацию. :) - person Sam; 16.05.2011

Просто примечание к ответу Филиппа (не можете прокомментировать его напрямую?)..

Я предпочитаю писать конечные автоматы в стиле двух процессов. Это очень ясно дает понять, где вы ожидаете предполагаемых триггеров, а где нет. Это также немного больше похоже на описание аппаратного обеспечения — представьте, например, создание конечного автомата с логикой на уровне платы. Зарегистрированное устройство соответствует процессу состояния ‹= next_state, а оператор case сопоставляется с массивом и/или перед регистром состояния.

Сказав это, я обычно использую один конечный автомат процесса для небольших простых задач и перехожу к двум машинам процесса для более крупных. Я даже иногда буду использовать третий процесс для организации вывода состояния в разные «задачные» группы... но не часто. Действительно большой конечный автомат говорит мне, что над архитектурой нужно поработать.

person Gord Wait    schedule 18.10.2012

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

Hi

описанный выше процесс проблематичен, но не из-за списка конфиденциальности. Можно объявить clk только для последовательного процесса. И у инструментов моделирования, и у инструментов синтеза не будет проблем с этим. clk — это самый быстро меняющийся/переходный сигнал в вашем коде.

Однако вы должны использовать (предпочтительно) асинхронный сброс. Конечно, в настоящее время производители говорят, что для проектирования FPGA сбросы даже не нужны; они происходят во время загрузки. Или предлагают синхронный сброс.

Тем не менее, асинхронный сброс ценен для среды на основе платы.

Короче говоря: добавьте сброс в свой дизайн и исправьте его поведение должным образом.

С уважением Николаос Каввадиас

person Nikolaos Kavvadias    schedule 23.06.2011

Следующий код VHDL представляет собой конечный автомат, чувствительный к границам. Процесс, чувствительный к фронту, в этом примере сделает и «out1», и «out2» в фазе с «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;

вот испытательный стенд

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;
person vahid_rowghanian    schedule 14.12.2019

Следующий код VHDL представляет собой конечный автомат, чувствительный к уровню. Чувствительный к уровню процесс в этом примере сделает «out1» не в фазе с «clk» и «out2» в фазе с «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;

вот испытательный стенд

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;
person vahid_rowghanian    schedule 14.12.2019