如何重写FSM不使用锁存器

时间:2013-12-05 20:31:36

标签: vhdl

我有一个FSM,它有效。然而,合成器抱怨存在“acc_x”,“acc_y”和“data_out”的锁存器,我理解为什么以及为什么它是坏的。但是,我不知道如何重写FSM,因此状态部分会进入时钟进程。任何想法从哪里开始?以下是FSM的代码:


library IEEE;

use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity storage is
    port
    (
        clk_in                           : in  std_logic;
        reset                            : in  std_logic;
        element_in                       : in  std_logic;
        data_in                          : in  signed(11 downto 0);
        addr                             : in  unsigned(9 downto 0);
        add                              : in  std_logic; -- add = '1' means add to RAM
                                                          -- add = '0' means write to RAM
        dump                             : in  std_logic;
        element_out                      : out std_logic;
        data_out                         : out signed(31 downto 0)
    );
end storage;

architecture rtl of storage is
    component bram is
    port
    (
        clk                              : in  std_logic;
        we                               : in  std_logic;
        en                               : in  std_logic;
        addr                             : in  unsigned(9 downto 0);
        di                               : in  signed(31 downto 0);
        do                               : out signed(31 downto 0)
    );
    end component bram;

    type state is (st_startwait, st_add, st_write);

    signal current_state                 : state := st_startwait;
    signal next_state                    : state := st_startwait;

    signal we                            : std_logic;
    signal en                            : std_logic;
    signal di                            : signed(31 downto 0);
    signal do                            : signed(31 downto 0);

    signal acc_x                         : signed(31 downto 0);
    signal acc_y                         : signed(31 downto 0);
begin
    ram : bram port map
    (
        clk  => clk_in,
        we   => we,
        en   => en,
        addr => addr,
        di   => di,
        do   => do  
    );

    process(clk_in)
    begin
        if rising_edge(clk_in) then
            if (reset = '1') then
                current_state           <= st_startwait;
            else
                current_state           <= next_state;
            end if;
        end if;
    end process;

    process(current_state, element_in, add, dump, data_in, do, acc_x, acc_y)
    begin
        element_out                     <= '0';

        en                              <= '1';
        we                              <= '0';

        di                              <= (others => '0');

        case current_state is
            when st_startwait =>          
                if (element_in = '1') then
                    acc_x               <= resize(data_in, acc_x'length);

                    next_state          <= st_add;
                else
                    next_state          <= st_startwait;
                end if;
            when st_add =>
                if (add = '1') then
                    acc_y               <= acc_x + do;
                else
                    acc_y               <= acc_x;
                end if;

                next_state              <= st_write;
            when st_write =>      
                if (dump = '1') then
                    data_out            <= acc_y;
                    element_out         <= '1';
                else
                    di                  <= acc_y;
                    we                  <= '1';
                end if;

                next_state              <= st_startwait;
        end case;
    end process;  
end rtl;

3 个答案:

答案 0 :(得分:2)

这是个人偏好,但我认为这里的大多数人都会同意我这一点......不要使用两个进程来控制你的状态机。在我看来,整个previous_state next_state事情是完全垃圾。这真的很令人困惑,它往往会产生闩锁 - 惊喜 - 你发现了这一点。尝试使用单个时钟进程重写您的状态机,只使用一个状态机信号。

这是我尝试重写你的状态机。请注意,我不确定下面的功能对您有用。模拟它以确保它的行为符合您的预期。例如,信号en始终与'1'相关联,不确定是否需要...

process (clk_in)
begin
  if rising_edge(clk_in) then
    element_out <= '0';
    en <= '1';                      -- this is set to 1 always?
    we <= '0';

    di <= (others => '0');

    case state is

      when st_startwait =>
        if (element_in = '1') then
          acc_x <= resize(data_in, acc_x'length);
          state <= st_add;
        end if;

      when st_add =>
        if (add = '1') then
          acc_y <= acc_x + do;
        else
          acc_y <= acc_x;
        end if;
        state <= st_write;

      when st_write =>
        if (dump = '1') then
          data_out    <= acc_y;
          element_out <= '1';
        else
          di <= acc_y;
          we <= '1';
        end if;
        state <= st_startwait;
    end case;
  end if;
end process;

答案 1 :(得分:0)

显然,你需要补充(时钟控制)寄存器(D触发器)。

你需要问问自己“如果FSM处于(假设)状态st_add,会对acc_x发生什么?”。你的答案是“我不想在这种状态下修改acc_x”。所以:使用时钟寄存器(例如用于状态的寄存器)来明确地写入它;您可以使用这些补充寄存器来增加时钟进程。做到处都是。这是规则。否则,合成器将推断透明锁存器以记忆acc_x的先前值:但是这些透明锁存器违反了同步设计原则。它们在结构上意味着你设计中的组合环,这很糟糕。

换句话说:问问自己什么是组合,寄存器在哪里?如果您有记录,请明确编码。不要在同一过程中分配和读取组合信号。

答案 2 :(得分:0)

推断锁存器的原因是最后一个进程中的情况 不能以敏感信号的所有可能组合驱动所有信号。 因此,该过程可以在不改变某些输出数据的情况下完成 过程的信号值。以这种方式保持输出是操作 因此,锁存器的锁存器由合成工具推断。

此锁存仅适用于acc_xacc_ydata_out,因为所有其他 在过程开始时,信号被分配一个默认值。

你可以通过驱动最后3个信号的默认值来解决这个问题 该过程的开始,例如'X'所有位允许合成 自由:

data_out <= (others => 'X');
acc_x    <= (others => 'X');
acc_y    <= (others => 'X');

或者,您可以确保所有输出都在所有分支中驱动 在这种情况下,您还应该在案例中添加when others =>分支。

我建议您使用默认值分配给所有信号,因为这样 更容易编写和维护,而不必跟踪分配 案件所有分支中的所有驱动信号。