FSM 2处理VHDL

时间:2016-02-03 00:41:31

标签: vhdl fpga fsm

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

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

这是我工作的代码和“无闩锁警告”控制单元。无论如何,锁存器涉及信号sel_mode,因为我不知道如何在状态IDLE的else分支中指定类似“保留前一个值”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;

1 个答案:

答案 0 :(得分:3)

<强>加

  

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

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

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

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

  

无论如何,锁存器涉及信号“sel_mode”,因为我不知道如何在状态IDLE的else分支中指定类似“保留前一个值”sel_mode“(没有锁存警告)。

为了防止锁存器的推断,必须将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;