VHDL - FSM 未启动(仅在时序仿真中)

VHDL - FSM not starting (JUST in timing simulation)

我正在为我的硕士论文工作,我对 VHDL 还很陌生,但我仍然必须实现一些复杂的东西。这是我必须编写的最简单的结构之一,但我仍然遇到了一些问题。

这是一个 FSM,它实现了一个带有 active-low 同步信号的 24 位移位寄存器(用于对 DAC 进行编程)。这只是我为我的项目创建的复杂细化链的终点。我尽可能地遵循了 FSM 的示例模型。

行为模拟工作正常,实际上,就行为模拟而言,我创建的整个精化链工作得非常好。但是,一旦我尝试 Post-translate 模拟,事情就开始出错了:很多 'X' 输出信号。

使用这个简单的移位寄存器我没有得到任何 'X',但是我无法进入 load_and_prepare_data 阶段。 current_state 似乎发生了变化(通过检查一些信号),但详细说明没有继续进行。

请记住,由于我是该语言的新手,所以我不知道应该在此 FSM 上设置什么时间限制(而且我不知道如何将它们写在 top.ucf 无论如何)

你能看出哪里出了问题吗? 提前致谢

编辑

我听从了您的建议,使用单状态进程清理了 FSM。我仍然对 "where to put what" 有一些疑问,但我真的很喜欢新的实现。无论如何,我现在得到了一个干净的行为模拟,但是 'X' 在 post 翻译模拟中的所有输出。 是什么原因造成的? 我将 post 新代码和测试平台:

----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    14:44:03 11/28/2014 
-- Design Name: 
-- Module Name:    dac_ad5764r_24bit_sr_programmer_v2 - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: This is a PISO shift register that gets a 24bit parallel input word.
--              It outputs the 24bit input word starting from the MSB and enables
--              an active low ChipSelect line for 24 clock periods.
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
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;

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

entity dac_ad5764r_24bit_sr_programmer_v2 is
    Port ( clk : in  STD_LOGIC;
           start : in  STD_LOGIC;
           reset : in  STD_LOGIC; -- Note that this reset is for the FSM not for the DAC
           reset_all_dac : in STD_LOGIC;
           data_in : in  STD_LOGIC_VECTOR (23 downto 0);
           serial_data_out : out  STD_LOGIC;
           sync_out : out  STD_LOGIC; -- This is a chip select
           reset_out : out STD_LOGIC;
           busy : out STD_LOGIC
         );
end dac_ad5764r_24bit_sr_programmer_v2;

architecture Behavioral of dac_ad5764r_24bit_sr_programmer_v2 is

-- Stati
type state_type is (idle, load_and_prepare_data, transmission);
--ATTRIBUTE ENUM_ENCODING : STRING; 
--ATTRIBUTE ENUM_ENCODING OF state_type: TYPE IS "001 010 100";
signal state: state_type := idle;
--signal next_state: state_type := idle;

-- Clock counter
--signal clk_counter_enable : STD_LOGIC := '0';
signal clk_counter : unsigned(4 downto 0) := (others => '0');

-- Shift register
signal stored_data: STD_LOGIC_VECTOR (23 downto 0) := (others => '0');

begin

FSM_single_process: process(clk)
begin
    if rising_edge(clk) then
        if reset = '1' then
            serial_data_out <= '0';
            sync_out <= '1';
            reset_out <= '1';
            busy <= '0';
            state <= idle;
        else
            -- Default
            serial_data_out <= '0';
            sync_out <= '1';
            reset_out <= '1';
            busy <= '0';

            case (state) is
                when transmission =>
                    serial_data_out <= stored_data(23);
                    sync_out <= '0';
                    busy <= '1';
                    clk_counter <= clk_counter + 1;
                    stored_data <= stored_data(22 downto 0) & "0";
                    state <= transmission;
                    if (clk_counter = 23) then
                        state <= idle;
                    end if;
                when others => -- Idle
                    if start = '1' then
                        serial_data_out <= data_in(23);
                        sync_out <= '0';
                        reset_out <= '1';
                        busy <= '1';
                        stored_data <= data_in;
                        clk_counter <= "00001";
                        state <= transmission;
                    end if;
            end case;

--            if (reset_all_dac = '1') then
--              reset_out <= '0';
--          end if;
        end if;
    end if;
end process;


end;

和测试平台:

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 dac_ad5764r_24bit_sr_programmer_tb IS
END dac_ad5764r_24bit_sr_programmer_tb;

ARCHITECTURE behavior OF dac_ad5764r_24bit_sr_programmer_tb IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT dac_ad5764r_24bit_sr_programmer_v2
    PORT(
         clk : IN  std_logic;
         start : IN  std_logic;
         reset : IN  std_logic;
         data_in : IN  std_logic_vector(23 downto 0);
         serial_data_out : OUT  std_logic;
         reset_all_dac : IN std_logic;
         sync_out : OUT  std_logic;
         reset_out : OUT  std_logic;
         --finish : OUT  std_logic;
         busy : OUT  std_logic
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal start : std_logic := '0';
   signal reset : std_logic := '0';
   signal data_in : std_logic_vector(23 downto 0) := (others => '0');
   signal reset_all_dac : std_logic := '0';

    --Outputs
   signal serial_data_out : std_logic;
   signal sync_out : std_logic;
   signal reset_out : std_logic;
   --signal finish : std_logic;
   signal busy : std_logic;

   -- Clock period definitions
   constant clk_period : time := 100 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: dac_ad5764r_24bit_sr_programmer_v2 PORT MAP (
          clk => clk,
          start => start,
          reset => reset,
          data_in => data_in,
          reset_all_dac => reset_all_dac,
          serial_data_out => serial_data_out,
          sync_out => sync_out,
          reset_out => reset_out,
          --finish => finish,
          busy => busy
        );

   -- 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 clk_period*10;
      reset <= '1' after 25 ns;
      wait for clk_period*1;
      reset <= '0' after 25 ns;
      wait for clk_period*3; 
      reset_all_dac <= '1' after 25 ns;
      wait for clk_period*1;
      reset_all_dac <= '0' after 25 ns;
      wait for clk_period*5; 
      data_in <= "111111111111111111111111" after 25 ns;
      wait for clk_period*3;
        start <= '1' after 25 ns;
      wait for clk_period*1;
        start <= '0' after 25 ns;


      wait;
   end process;

END;

更新 1

更新了最后一个设计:此代码不会导致任何 'X'(无法弄清楚为什么,这个不会,但以前的会)。然而,它并没有像前 3 个进程机器那样启动(在 POST-TRANSLATE 模拟中),并且信号 sync_out 卡在 0 而默认情况下它应该是 '1'。

更新 2

我一直在研究技术原理图,从 sync_out=0 的问题开始:它是用 FDS 实现的,S 是 FSM 复位信号,D 来自 LUT3 和 I = state&reset&start 和 INIT = 45 = "00101101"。我在模拟中寻找了这个 LUT3,我注意到它有 INIT = "00000000"!

关于如何 运行 这个模拟,我是否遗漏了什么?好像设计中的每一个LUT都没有设置好!

更新 3 似乎 Post-Translate 模拟在某些方面有问题,或者由于某种原因我没有正确配置它:Post-Map 和 Post-PAR 模拟工作并显示一些输出。 但是有一个奇怪的错误:stored_data 寄存器没有用完整的 data_in 向量更新,之后,FSM 正确运行并输出存储的数据。 我在综合后查看了技术原理图,由于某种原因,位 23、22、21、19、18 未连接到相应的 data_in 位。您可以在 Post-Map 模拟的此屏幕截图中看到效果。同样发生在Post-PAR,但似乎这个问题直接来自综合!

已解决:奇怪的输出来自综合优化。该工具意识到精化链中的前一个块永远不会为那些特定位输出与 0 不同的位。我的错误是假设我可以单独测试单个块:我真正测试的是为 FPGA 综合的块,同时考虑了设计中的所有其他内容!

感谢大家的帮助,我会听从大家的建议!

这里有一些改进代码的提示:

  1. 您可以删除 Xilinx 对 UNISIM 的依赖性,因为您没有使用任何 Xilinx Primitves。
  2. 应用属性 ENUM_ENCODING 对状态编码没有影响,除非您还定义属性 FSM_ENCODING 并将其值设置为用户。可以通过将 FSM_ENCODING 设置为 one-hot 来强制使用 One-Hot 编码。通常合成足够聪明,可以找到最好的编码。
    read more ...
  3. None 个寄存器具有默认值:
    signal current_state : state_type := idle;
  4. 你的FSM在Xilinx综合工具(XST)眼里不是FSM。我敢肯定,如果您查看综合报告,您不会发现 XST 报告了 current_state.
    的 FSM 那么你的 FSM 出了什么问题?
    • 您的 FSM 没有初始状态。
    • 您的 FSM 有多个重置状态(空闲,load_and_prepare_data)
    • 你的 FSM 没有从空闲到 load_and_prepare_data 的转换(重置是没有转换)
    • 为当前状态编写 next_state 转换会导致 XST 认为它不是 FSM
      默认分配 next_state <= current_state; 就足够了。
  5. 如果将信号类型 clk_counter 更改为无符号,则可以更轻松地进行算术运算。
    增量:clk_counter <= clk_counter + 1;
    清除:clk_counter <= (others => '0');
    比较:if (clk_counter = 23) then
  6. 在 FSM 进程之外使用 FSM 的状态信号不是好的方式。

    FSM_next_state_process: process(current_state, start,  clk_counter, reset_all_dac)
    begin
      next_state  <= current_state;
    
      OutReg_busy        <= '1';
      OutReg_reset_out   <= '1';
      OutReg_sync_out    <= '1';
      clk_counter_enable <= '0';
    
      case (current_state) is
        when idle =>
          OutReg_busy      <= '0';
          if (reset_all_dac = '1') then
            OutReg_reset_out <= '0';
          end if;
    
        when load_and_prepare_data =>
          next_state <= transmission;
    
        when transmission =>
          clk_counter_enable <= '1';
          OutReg_sync_out <= '0';
    
          if (clk_counter = 23) then
            next_state <= idle;
          end if;
    
        when others =>
          next_state <= idle;
    
      end case ;
    end process;
    

我更喜欢状态机的单进程形式,它更干净、更简单并且更不容易出现敏感列表错误等错误。我也赞同 Paebbels 的出色回答中的观点。但是我认为这些都不是这里的问题。

在 post-synth 和 post-PAR 模拟中需要注意的一件事是它们的时间模型与行为模型不同。行为模型遵循我在 this answer 中描述的简单规则,并确保在典型的设计流程中您可以直接进入硬件 - 无需 post-synth 仿真,无需担心。

事实上,如果我正在追查可疑的工具错误,我只会使用 post-synth 或 post-PAR 模拟。 (对于 FPGA 设计,不是 ASIC,就是这样!)

但是,这种简单的时序模型有其局限性。您可能熟悉诸如通过信号分配分配的时钟信号(通常隐藏在您不期望的第 3 方模型中)之类的问题,它消耗增量周期,并确保您的时钟数据在 [之前到达] =53=] 你的时钟而不是 之后,随后发生的一切都比预期提前一个周期...

在行为建模中,稍加纪律就可以避免这样的麻烦。但 post-PAR 建模并非如此。

您的测试平台可能与行为模型的设置方式相同。如果是这样,那很可能就是问题所在。

这是我在这种情况下所做的:我没有正式的授权,只是经验。在将 FPGA 连接到具有实际时序的外部存储器模型时,它也能很好地工作。

1) 我假设简单(行为)时序模型适用于设计内部的所有信号。

2) 我对设计的输入和输出不做任何假设。

3) 我注意到输入端的估计设置和保持时序,(a) 来自 FPGA 数据表或更好,(b) 来自 post-synth 中显示的最坏情况值或post-PAR 报告,并围绕它们构建 testbench。 工作示例:设置时间 1 ns,保持时间 2 ns,时钟周期 10 ns。这意味着保证正确读取时钟边沿后 2 ns 和 9 ns 之间的任何输入。我选择(任意)5 ns。

signal_to_fpga <= driving_value after 5 ns;

(请注意,Xilinx 通过将它们表示为 "offset in/out before/after" 来使这荒谬地违反直觉,它指的是前一个或未来时钟边沿的时序,而不是您正在查看的那个)

或者,如果输入来自 CPU 或现实世界中的内存,我会使用该设备的数据表时序规范。

4) 我注意到数据表或报告中报告的最坏情况 clk-out 时序,并围绕它们构建 design。 (比如说,7 纳秒)

fpga_output_pin <= driving_value after 7 ns;

注意这个"after"子句显然被综合忽略了;然而 post-synth 反向注释会引入一些非常相似的东西。
5) 如果结果证明这还不够好,那么(可能在包装器组件中以避免污染可合成代码)提高准确性,如

fpga_output_pin <= 'X' after 1 ps, driving_value after 7 ns;

6) 我重新运行行为模拟。通常,它现在失败了,因为它是在没有考虑实际时间的情况下编写的。

7) 我修复了这些故障。这可能包括在测试从设计输出的值之前添加实际延迟。它可以是一个迭代过程。

现在,我有一个合理的期望,即 post-PAR 仿真模型将直接进入测试平台并正常工作。