FSM 2 进程 VHDL

FSM 2 process VHDL

我试图为我的项目的控制单元的 FSM 写下 VHDL 代码。我选择了 2 进程方式,一个进程用于状态寄存器,另一个进程用于下一个状态和输出逻辑。无论如何,我在设置解决方案时遇到了一些问题,因为某些信号会发出合成锁存器警告(我知道它们为什么会出现)。我找到的唯一解决方案(没有对状态寄存器和输出逻辑使用一个进程,也没有再添加 3 个状态)是在管理状态逻辑的进程中添加一些输出逻辑。

令人惊讶的是,它确实有效,但这在概念上是否正确?我的意思是,用一些输出逻辑弄脏状态寄存器进程的代码是否正确,或者我是否打破了 2 进程模式?

这是我的工作代码和 "latch-free warning" 控制单元。无论如何,闩锁涉及信号 sel_mode,因为我不知道如何在状态 IDLE 的其他分支中指定类似 "keep the previous value of "sel_mode 的东西(没有闩锁警告)。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity control_unit is
Port ( clock : in  STD_LOGIC;
       reset_n_in : in  STD_LOGIC;
       primo_operando : in  STD_LOGIC;
       secondo_operando : in  STD_LOGIC;
       add_sub : in  STD_LOGIC;
          ov : in STD_LOGIC;
          subtract_in : in STD_LOGIC;
          led_ov : out STD_LOGIC;
          reset_n_out : out STD_LOGIC;
       subtract_out : out  STD_LOGIC;
          en_w_primo_op : out STD_LOGIC;
          en_w_secondo_op : out STD_LOGIC;
          en_w_risultato : out STD_LOGIC;
          sel_mode : out STD_LOGIC_VECTOR(1 downto 0)
          );
end control_unit;

architecture Behavioral of control_unit is

type state is (IDLE, PRIMO_OP, SECONDO_OP, RISULTATO);
signal curr, nxt : state := IDLE;

begin

change_state : process(clock, reset_n_in)
begin
    if reset_n_in = '0' then
        curr <= IDLE;
-- in the following lines I mix the state register logic with output logic
    elsif rising_edge(clock) then
        if curr = PRIMO_OP then
            sel_mode <= "10";
        elsif curr = SECONDO_OP then
            sel_mode <= "01";
        elsif curr = RISULTATO then
            sel_mode <= "00";
        end if;
        curr <= nxt;
    end if;
end process;

fsm: process(curr, reset_n_in, primo_operando, secondo_operando, add_sub)
begin
    if reset_n_in = '0' then
        reset_n_out <= '0';
    else
        reset_n_out <= '1';
    end if;
    en_w_primo_op <= '0';
    en_w_secondo_op <= '0';
    en_w_risultato <= '0';
    case curr is
        when IDLE =>
            if primo_operando = '1' then
             --   sel_mode <= "10";
                nxt <= PRIMO_OP;
            elsif secondo_operando = '1' then
             -- sel_mode <= "01";
                nxt <= SECONDO_OP;
            elsif add_sub = '1' then
             -- sel_mode <= "00";
                nxt <= RISULTATO;
            else
                nxt <= IDLE;
             -- how to specify keep sel_mode to the previous value??
            end if;
        when PRIMO_OP =>
         -- sel_mode <= "10";
            en_w_primo_op <= '1';
            nxt <= IDLE;
        when SECONDO_OP =>
         -- sel_mode <= "01";
            en_w_secondo_op <= '1';
            nxt <= IDLE;
        when RISULTATO =>
         -- sel_mode <= "00";
            en_w_risultato <= '1';
            nxt <= IDLE;
    end case;
end process;

led_ov <= ov;
subtract_out <= subtract_in;

end Behavioral;

已添加:

With much surprise it works but is this conceptually correct? I mean, is it correct to dirty the code of the state register process with some output logic or am I breaking the 2 process pattern?

您的设计状态始终由设计中的所有个寄存器组成,而不仅仅是设计中的curr个寄存器。你可以这样想:

  • FSM 的(部分)状态由 curr 定义。这是使用双进程形式描述的。

  • 您发布的代码中的寄存器 sel_mode 是数据路径寄存器,因此定义了数据路径部分的状态。您已经使用单进程形式对其进行了描述。使用双进程形式的替代解决方案,我将在下面描述。


Anyway the latch involved the signal "sel_mode" as I don't know how to specify in the else branch of the state IDLE something like "keep the previous value of "sel_mode"(without having the latch warning).

为了防止锁存器的推断,你必须将sel_mode的当前值保存到一个时钟边沿触发的寄存器中,并在你想要输出前一个值时分配寄存器值。由于寄存器代表的是之前的值,所以我称之为sel_mode_prev。寄存器的分配与一些复位逻辑一​​起发生在时钟进程中:

change_state : process(clock, reset_n_in)
begin
    if reset_n_in = '0' then
        curr <= IDLE;
        sel_mode_prev <= "00"; -- or some other value
    elsif rising_edge(clock) then
        curr <= nxt;
        sel_mode_prev <= sel_mode_i; -- save current value
    end if;
end process;

输出sel_mode在组合部分赋值。但是,由于您无法读取上面 sel_mode_prev 赋值中的输出值,您必须将 sel_mode 的所需值赋给一个中间值,我称之为 sel_mode_i。组合过程fsm然后分配这个信号。输出 sel_mode <= sel_mode_i; 的分配与其他输出分配一起在流程下方组合完成。

这里是完整的修改后的架构,带有注释:

architecture Behavioral of control_unit is

type state is (IDLE, PRIMO_OP, SECONDO_OP, RISULTATO);
signal curr, nxt : state := IDLE;

signal sel_mode_i    : std_logic_vector(1 downto 0); -- internal version of output
signal sel_mode_prev : std_logic_vector(1 downto 0); -- previous version of sel_mode

begin

change_state : process(clock, reset_n_in)
begin
    if reset_n_in = '0' then
        curr <= IDLE;
        sel_mode_prev <= "00"; -- or some other value
    elsif rising_edge(clock) then
        curr <= nxt;
        sel_mode_prev <= sel_mode_i; -- save current value
    end if;
end process;

fsm: process(curr, reset_n_in, primo_operando, secondo_operando, add_sub,
             sel_mode_prev) -- also add sel_mode_prev here
begin
    if reset_n_in = '0' then
        reset_n_out <= '0';
    else
        reset_n_out <= '1';
    end if;
    en_w_primo_op <= '0';
    en_w_secondo_op <= '0';
    en_w_risultato <= '0';
    case curr is
        when IDLE =>
            if primo_operando = '1' then
                sel_mode_i <= "10"; -- assign internal signal
                nxt <= PRIMO_OP;
            elsif secondo_operando = '1' then
                sel_mode_i <= "01"; -- assign internal signal
                nxt <= SECONDO_OP;
            elsif add_sub = '1' then
                sel_mode_i <= "00"; -- assign internal signal
                nxt <= RISULTATO;
            else
                sel_mode_i <= sel_mode_prev; -- output old value at default
                nxt <= IDLE;
            end if;
        when PRIMO_OP =>
            sel_mode_i <= "10"; -- assign internal signal
            en_w_primo_op <= '1';
            nxt <= IDLE;
        when SECONDO_OP =>
            sel_mode_i <= "01"; -- assign internal signal
            en_w_secondo_op <= '1';
            nxt <= IDLE;
        when RISULTATO =>
            sel_mode_i <= "00"; -- assign internal signal
            en_w_risultato <= '1';
            nxt <= IDLE;
    end case;
end process;

sel_mode <= sel_mode_i; -- assign internal signal to output
led_ov <= ov;
subtract_out <= subtract_in;

end Behavioral;