【问题标题】:VHDL state machine outputs not in syncVHDL 状态机输出不同步
【发布时间】:2013-07-22 17:12:21
【问题描述】:

这是一个通用的 FPGA 设计问题,我是 FPGA 设计的新手,刚刚开始了我的第一个大型项目,构建了一些不错的线性代数求解器。这些系统非常大,所以第一次就做好很重要。

在成功模拟之后,我现在正在合成,但我做噩梦了,我必须逐个组件地构建和测试它,因为在模拟中没有任何表现!我主要遇到输出未同步的状态机问题,例如这是我正在使用的数据加载器:

entity TriDiag_Data_Scheduler is
    generic( W : integer :=16;  
                AW : integer := 10 -- address width
                );
    Port ( clk : in  STD_LOGIC;
           rst : in  STD_LOGIC; --attatched to data finished
           d_ready : in  STD_LOGIC;
           din : in  STD_LOGIC_VECTOR (W-1 downto 0);
              wr_en : out STD_LOGIC_VECTOR (3 downto 0);
              dout_a, dout_b, dout_c, dout_y : out  STD_LOGIC_VECTOR (W-1 downto 0);
              addr_out : out STD_LOGIC_VECTOR (AW-1 downto 0));
end TriDiag_Data_Scheduler;

architecture Behavioral of TriDiag_Data_Scheduler is

type state is (a,b,c,y);
signal state_pr, state_next : state := y;
signal addr_reg, addr_next : std_logic_vector(AW-1 downto 0) :=(others =>'1');

signal wr_en_next : std_logic_vector(3 downto 0);

--data buffer
signal d_buff, d_buff_a, d_buff_b, d_buff_c, d_buff_y : std_logic_vector (W-1 downto 0) :=(others =>'0');
signal d_buff_a_reg, d_buff_b_reg, d_buff_c_reg, d_buff_y_reg : std_logic_vector (W-1 downto 0) :=(others =>'0');

begin

process(clk,rst)
begin
    if(clk'event and clk ='1') then
        state_pr <= state_next;
        d_buff_a <= d_buff_a_reg;
        d_buff_b <= d_buff_b_reg;
        d_buff_c <= d_buff_c_reg;
        d_buff_y <= d_buff_y_reg;

        addr_reg <= addr_next;
        wr_en <= wr_en_next;
    end if;

end process;

addr_out <= addr_reg;
dout_a <= d_buff_a;
dout_b <= d_buff_b;
dout_c <= d_buff_c;
dout_y <= d_buff_y;


--Data out logic
process(state_pr, d_buff_a, d_buff_b, d_buff_c, d_buff_y, d_buff)
begin

    d_buff_a_reg <= d_buff_a;
    d_buff_b_reg <= d_buff_b;
    d_buff_c_reg <= d_buff_c;
    d_buff_y_reg <= d_buff_y;

    case state_pr is
        when a => --move data to a reg
            d_buff_a_reg <= d_buff;
        when b => --move data to b reg
            d_buff_b_reg <= d_buff;
        when c => --move data to c reg
            d_buff_c_reg <= d_buff;
        when y => 
            d_buff_y_reg <= d_buff;
    end case; 
end process;

--next state and addr logic
process(state_pr, d_ready, rst, din)
begin

    state_next <= state_pr;
    addr_next <= addr_reg;
    wr_en_next <= (others => '0');

if(rst = '1') then
    state_next <= a;
    addr_next <= (others =>'1');
    wr_en_next <= (others => '0');
elsif(d_ready = '1') then
--Read in the data to the buffer
    d_buff <= din;
--next state logic
    case state_pr is
        when a => --move data to a reg
            addr_next <= addr_reg + 1;
        --  d_buff_a_reg <= din;
            wr_en_next <= "0001";
            state_next <= b;
        when b => --move data to b reg
            wr_en_next <= "0010";
        --  d_buff_b_reg <= din;
            state_next <= c;
        when c => --move data to c reg
            wr_en_next <= "0100";
        --  d_buff_c_reg <= din;
            state_next <= y;
        when y => 
        --  d_buff_y_reg <= din;
            wr_en_next <= "1000";
            state_next <= a;
    end case; 
end if;
end process;
end Behavioral;

基本上,当通过 UART 模块接收数据时,它的工作就是加载到正确的内存中(由 write_en 信号控制)。问题是在我所有的设计(这是修订版 7)中,所有 addr_out、wr_en 和正确的数据都是同步的,但在综合中我一直发现 addr 和 wr_en 与数据不同步,并且从前面读取了一半和前一个状态的一半。

我应该使用哪些设计实践以使我的 VHDL 更易于综合,因为按照这种速度,我将不得不为每个组件重新编写我之前的所有辛勤工作!

非常感谢 山姆

【问题讨论】:

  • 我的 VHDL 是在家教的,如果冒犯了任何人,我深表歉意!
  • 每当我看到具有大敏感度列表的非时钟进程时,我都会认为它们缺少某些东西。我没有详细了解它,但检查合成器警告,看看它是否发现了任何东西。就我个人而言,我会将其重写为单个时钟进程
  • 再次感谢马丁。问题是它对外部 d_ready 信号很敏感。我通过添加一个由 d_ready 信号设置但随后在 clk 上断言的同步滴答声解决了这个问题。我认为我在 d_ready 和 clk 之间遭受了如此好的偏差,这就是为什么它不完全同步的原因。但是现在一切都在同步滴答声中处于活动状态,并且似乎可以正常工作。
  • 我的经验是,软件人员使用更大的、单一的、有时钟的每个实体的进程和 变量 来处理不涉及与另一个进程通信的所有事情会取得更大的成功。当然是 YMMV,但可能值得一试......
  • 我同意@MartinThompson 的观点:作为对如此大的组合过程有多危险的演示,请查看为d_buff 生成的闩锁。只是一个小疏忽,不会出现在模拟中,但会对综合产生很大影响。此外,在不是寄存器的情况下命名 _reg 非常令人困惑(例如,d_buff_a_reg)!

标签: hardware vhdl fpga xilinx state-machine


【解决方案1】:

工作设计,旧设计在另一个模块断言的 d_ready 信号与此模块 clk 之间存在偏差。因此,未按最初希望同步的更改。为了解决这个问题,我在 d_tick_next 信号中声明了一个本地同步信号 d_tick,它允许所有正确的行为。吸取的一个教训(如果我错了,请纠正我)是你不能依赖假定的外部时钟信号(例如 d_ready)与接收模块 clk 同步。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;


-- ////////////////////////////////////////////////////////////////////
--Takes in the linear stream of data and builds up the memory structure

--INPUT DATA MUST FOLLOW SEQUENCE [a b c] x = [y] (0 if a or c do not exist) rows 0 and N respectivly 
--//////////////////////////////////////////////////////////////////////
entity TriDiag_Data_Scheduler is
    generic( W : integer :=16;  
                AW : integer := 10 -- address width
                );
    Port ( clk : in  STD_LOGIC;
           rst : in  STD_LOGIC; --attatched to data finished
           d_ready : in  STD_LOGIC;
           din : in  STD_LOGIC_VECTOR (W-1 downto 0);
              wr_en : out STD_LOGIC_VECTOR (3 downto 0);
              dout_a, dout_b, dout_c, dout_y : out  STD_LOGIC_VECTOR (W-1 downto 0);
              addr_out : out STD_LOGIC_VECTOR (AW-1 downto 0));
end TriDiag_Data_Scheduler;

architecture Behavioral of TriDiag_Data_Scheduler is

type state is (a,b,c,y);
signal state_pr, state_next : state := y;
signal addr_reg, addr_next : std_logic_vector(AW-1 downto 0) :=(others =>'1');

signal wr_en_next : std_logic_vector(3 downto 0);

signal d_tick, d_tick_next : std_logic;

--data buffer
signal d_buff, d_buff_a, d_buff_b, d_buff_c, d_buff_y : std_logic_vector (W-1 downto 0) :=(others =>'0');
signal d_buff_a_reg, d_buff_b_reg, d_buff_c_reg, d_buff_y_reg : std_logic_vector (W-1 downto 0) :=(others =>'0');

begin

process(clk,rst)
begin
    if(clk'event and clk ='1') then
        state_pr <= state_next;

        d_buff_a <= d_buff_a_reg;
        d_buff_b <= d_buff_b_reg;
        d_buff_c <= d_buff_c_reg;
        d_buff_y <= d_buff_y_reg;

        d_tick <= d_tick_next;

        addr_reg <= addr_next;
        wr_en <= wr_en_next;
    end if;

end process;

addr_out <= addr_reg;
dout_a <= d_buff_a;
dout_b <= d_buff_b;
dout_c <= d_buff_c;
dout_y <= d_buff_y;


--Data out logic
process(state_pr,d_tick,rst)
begin

    d_buff_a_reg <= d_buff_a;
    d_buff_b_reg <= d_buff_b;
    d_buff_c_reg <= d_buff_c;
    d_buff_y_reg <= d_buff_y;

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

if(rst = '1') then
    addr_next <= (others =>'1');
else
    addr_next <= addr_reg;
end if;

if(d_tick = '1') then
    case state_pr is
        when a => --move data to a reg
            d_buff_a_reg <= d_buff;
            addr_next <= addr_reg + 1;
            wr_en_next <= "0001";
        when b => --move data to b reg
            d_buff_b_reg <= d_buff;
            wr_en_next <= "0010";
        when c => --move data to c reg
            d_buff_c_reg <= d_buff;
            wr_en_next <= "0100";
        when y => 
            d_buff_y_reg <= d_buff;
            wr_en_next <= "1000";
    end case; 
end if;
end process;

--next state and d_tick
process(state_pr, d_ready, rst, din)
begin

    state_next <= state_pr;
    d_tick_next <='0';

if(rst = '1') then
    state_next <= y;
elsif(d_ready = '1') then
--Read in the data to the buffer
    d_buff <= din;
-- set sync tick
    d_tick_next <= '1';
--next state logic
    case state_pr is
        when a => 
            state_next <= b;
        when b => 
            state_next <= c;
        when c =>
            state_next <= y;
        when y => 
            state_next <= a;
    end case; 
end if;
end process;

结束行为;

【讨论】:

  • 您在第一个进程的敏感度列表中遗漏了很多信号(d_buff*、addr_reg,可能还有其他)。您还在为 d_buff 生成锁存器。这两个都应该在综合过程中显示为警告:不要只是忽略它们!因此,如果它在模拟中有效,但在硬件中无效,请不要感到惊讶。如果它目前似乎在硬件中工作,请注意您以后可能会遇到问题。
  • 您好 Zennehoy,需要闩锁来临时保存 din 的状态,因为 din 会随着时间而变化。关于敏感性,ISE 可以为我推断它们,但是是的,我确实收到了警告!但如果我错了,请纠正我,因为我的电子产品相当牛仔!
  • 我猜这篇文章解释了为什么doulos.com/knowhow/fpga/latches。你会如何推荐我写它,所以它使用边沿触发触发器来代替?我之前遇到的问题是,当它由于 clk 之间的偏差而处于边缘时,以及当 d_ready 发出信号时,d_buff 没有被完全填充。
  • 敏感度列表仅在仿真期间相关(我所知道的所有合成工具都会忽略敏感度列表),但敏感度列表不正确会导致仿真“错误”。我的建议是尽可能使用时钟进程,并在其他任何地方使用 VHDL-2008 构造 process(all)。至于锁存器:除了顶层端口(设备引脚)之外,信号与时钟不同步是非常罕见的。为什么不直接在 d_ready='1' 时在时钟上升沿注册 din?
  • 如果您看到偏斜,您应该修复偏斜,而不是寻找解决偏斜的方法。是什么在驱动 d_ready,为什么它不与 din 同步?如果 din 和 d_ready 是与时钟不同步的管脚,您将不得不添加同步逻辑(此评论的主题过于复杂)以将 din 和 d_ready 与时钟同步。
猜你喜欢
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
相关资源
最近更新 更多