VHDL状态机测试平台 - 在船上工作但不在模拟时工作

时间:2017-08-26 09:16:41

标签: vhdl simulation test-bench

我有在板上工作的VHDL实现,它检测序列01110并将为2个时钟计数引发一个标志。它也会检测重叠序列,其中011101110会将标志抬起两次。

我已经在电路板上用逻辑分析仪检查了我的实现,并且相信它有效。我在电路板上以10 kHz的重复序列0111进行馈电,它的时钟频率为100 MHz,我用预分频器将其缩放到10 kHz。

我的问题是,当尝试使用模拟重建类似场景时,我没有按预期获得任何输出

来自电路板enter image description here的逻辑分析仪的图像

来自Test Bench的图片 enter image description here

测试台代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity test_FSM_prac4 is
--  Port ( );
end test_FSM_prac4;

architecture Behavioral of test_FSM_prac4 is

component FSM_prac4 is 
    port ( 
       inputSignal : in STD_LOGIC;
       pushButton : in  STD_LOGIC;
       clk100mhz : in STD_LOGIC;
       logic_analyzer : out STD_LOGIC_VECTOR (7 downto 0);
       LEDs: out STD_LOGIC
); end component;

signal inputSignal : std_logic := '0';
signal pushButton: std_logic := '0';
signal clk100mhz: std_logic := '0';
signal logic_analyzer: std_logic_vector(7 downto 0);
signal LEDs : std_logic;

begin

uut : FSM_prac4 port map(
    inputSignal => inputSignal,
    pushButton => pushButton,
    clk100mhz => clk100mhz,
    logic_analyzer => logic_analyzer,
    LEDs => LEDs
    );

--generate clock 100mhz
clock_tic: process begin
    loop
    clk100mhz <= '0';
    wait for 5ns;
    clk100mhz <= '1';
    wait for 5ns;
    end loop;
end process;

input_changes: process begin
    loop
    inputSignal <= '0';
    wait for 100us;
    inputSignal <= '1';
    wait for 100us;
    inputSignal <= '1';
    wait for 100us;
    inputSignal <= '1';
    wait for 100us;
    end loop;
end process;   

end Behavioral;

显示逻辑分析器的映射

logic_analyzer(0) <= masterReset;
logic_analyzer(1) <= newClock -- 10Khz Clock;
logic_analyzer(2) <= outputZ;
--FSM States
logic_analyzer(3) <= '1' when y = A ELSE '0';
logic_analyzer(4) <= '1' when y = B ELSE '0';
logic_analyzer(5) <= '1' when y = C ELSE '0';
logic_analyzer(6) <= '1' when y = D ELSE '0';
logic_analyzer(7) <= '1' when y = E ELSE '0';

如果有人可以指导我在测试台上做错了什么以及如何复制以获得与第一张图像相似的结果,因为它表明在模拟中,它始终保持在状态A并且新时钟不会切换意义clk100mhz以某种方式没有连接,但我无法弄清楚原因。

非常感谢任何帮助,谢谢你们

编辑:

我写了一个简单的程序来测试我的标量时钟

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity scaler_clk is
Port ( 
           pushButton : in std_logic;
           indicator : out std_logic;
           clk100mhz : in STD_LOGIC;
           clk10khz: out STD_LOGIC
           );
end scaler_clk;

architecture Behavioral of scaler_clk is
    signal clockScalers : std_logic_vector (12 downto 0):= (others => '0') ;
    signal prescaler: std_logic_vector(12 downto 0) := "1001110001000";
    signal newClock: std_logic := '0'; 
    signal masterReset : std_logic;     

begin

    clk10khz <= newClock;
    masterReset <= pushButton;

    process (clk100mhz,masterReset) begin
        if(masterReset <= '1') then <--- error occurs here
            clockScalers <= "0000000000000";
            newClock <= '0';
            indicator <= '1';
        elsif (clk100mhz'event and clk100mhz = '1')then
            indicator <= '0';
            clockScalers <= clockScalers + 1;
            if(clockScalers > prescaler) then
                newClock <= not newClock;
                clockScalers <= (others => '0');
            end if;
        end if;
    end process;

end Behavioral;

测试台代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity test_scaler_clk is
--  Port ( );
end test_scaler_clk;

architecture Behavioral of test_scaler_clk is

    component scaler_clk Port ( 
            pushButton : in std_logic;
            indicator : out std_logic;
           --input clock
           clk100mhz : in STD_LOGIC;
           clk10khz: out STD_LOGIC
           );end component;

    signal clk100mhz: std_logic := '0';
    signal clk10khz : std_logic;
    signal pushButton: std_logic;
    signal indicator :  std_logic;

begin

uut: scaler_clk port map(
    pushButton => pushButton,
    indicator => indicator,
    clk100mhz => clk100mhz,
    clk10khz => clk10khz
    );    

    pushButton <= '0';
    clock_tic: process begin
        loop
        clk100mhz <= '0';
        wait for 5ns;
        clk100mhz <= '1';
        wait for 5ns;
        end loop;
    end process;


end Behavioral;

即使我将pushButton设置为'0',它仍然触发masterReset,任何人都知道原因,这就是10 kHz时钟不工​​作的原因

enter image description here

1 个答案:

答案 0 :(得分:2)

您可以(应该)改进代码中的一些内容。正如Brian已经解释过的那样,在Behavioral的{​​{1}}架构中,您应该:

scaler_clk

而不是:

if(masterReset = '1') then

现在,让我们从最初可能的原因开始:未绑定的组件。您的测试平台将设计实例化为组件验证。 VHDL组件是实体实体的原型。原型足以编译,因为编译器可以执行所有必要的语法和类型检查。但它们还不足以模拟,因为模拟器还需要原型背后的实现。某些工具对未绑定的组件具有默认绑定策略:如果它们找到具有相同名称的实体,并且它只有一个体系结构,则使用它。你的模拟器显然没有使用这样的策略(至少在默认情况下没有,可能有一个选项,但它被禁用)。请注意,我知道的大多数模拟器在找到未绑定的组件时会发出警告。你可能错过了这些警告。

无论如何,您的组件实例是未绑定的(它们没有关联的实体/体系结构),模拟器将它们视为黑盒子。除了您声明的初始值(1)之外,它们的输出不会被驱动。

如何解决这个问题?两个选项:

  1. 使用配置指定每个组件实例应使用哪个实体/体系结构对:

    if(masterReset <= '1') then
    
  2. 使用实体实例化而不是组件:

    for all: scaler_clk use entity work.scaler_clk(Behavioral);
    
  3. 现在,让我们来看看代码中可以改进的其他方面:

    1. 您使用的是非标准套餐,它们通常不兼容:uut: entity work.scaler_clk(Behavioral) port map... IEEE.STD_LOGIC_ARITH。由于它们不是标准的,因此它们甚至不应该在标准的IEEE.STD_LOGIC_UNSIGNED库中。您应该使用IEEE而不是那个。它声明IEEE.NUMERIC_STDSIGNED类型(与UNSIGNED具有相同的声明)并重载它们上的算术运算符。

    2. 您的测试平台产生100MHz时钟:

      STD_LOGIC_VECTOR

      无限循环是无用的:进程已经是无限循环:

      clock_tic: process begin
          loop
          clk100mhz <= '0';
          wait for 5ns;
          clk100mhz <= '1';
          wait for 5ns;
          end loop;
      end process;
      

      也会这样做。您clock_tic: process begin clk100mhz <= '0'; wait for 5ns; clk100mhz <= '1'; wait for 5ns; end process clock_tic; 进程的评论相同。

    3. 您的input_changes进程使用input_changes语句。这不是一个好主意,因为与时钟相比,您不知道wait for <duration>信号何时切换。它是在inputSignal的上升边缘之前,之后,还是与clk100mhz的上升边缘完全同时?如果它恰好在同一时间,将会发生什么?当然,您可以仔细选择<durations>以避免这种含糊不清,但它容易出错。您应该仅在时钟生成过程中使用wait for <duration>。在其他地方,最好与时钟同步:

      input_changes: process
      begin
          inputSignal <= '0';
          for i in 1 to 10000 loop
              wait until rising_edge(clk100mhz);
          end loop;
          inputSignal <= '1';
          for i in 1 to 10000 loop
              wait until rising_edge(clk100mhz);
          end loop;
          inputSignal <= '1';
          for i in 1 to 10000 loop
              wait until rising_edge(clk100mhz);
          end loop;
          inputSignal <= '1';
          for i in 1 to 10000 loop
              wait until rising_edge(clk100mhz);
          end loop;
      end process input_changes;
      

      这可以保证inputSignal在时钟上升沿之后发生变化。你可以用更优雅的方式重写它(并且可能更容易维护):

      input_changes: process
          constant values: std_logic_vector(0 to 3) := "0111";
      begin
          for i in values'range loop
              inputSignal <= values(i);
              for i in 1 to 10000 loop
                  wait until rising_edge(clk100mhz);
              end loop;
          end loop;
      end process input_changes;
      
    4. 您正在使用已解析的类型(STD_LOGICSTD_LOGIC_VECTOR)。这些类型允许多个驱动器,即具有由多个设备(VHDL进程)驱动的硬件线(VHDL信号)。通常你不想要这个。通常你甚至想像瘟疫一样避免这种情况,因为它可能导致短路。在大多数情况下,使用非解析类型(STD_ULOGICSTD_ULOGIC_VECTOR)更明智,因为如果您在设计中意外造成短路,编译器和/或模拟器将引发错误。

    5. 最后一件事:如果顾名思义,你打算使用clk10khz信号作为真正的时钟,你应该重新考虑这个决定。这是您使用自定义逻辑生成的信号。时钟具有非常特定的电气和时序约束,常规信号无法真正满足这些限制。在使用clk10khz作为时钟之前,你必须处理时钟偏差,时钟缓冲...不是不可能,但很棘手。如果您确实将它用作时钟,那么您的合成器可能会发出您也错过的严重警告(请查看时间报告)。此外,在您的情况下,这可能是无用的:可能会使用clk100mhz生成的启用信号,避免所有这些问题。而不是:

      process (clk100mhz,masterReset) begin
          if(masterReset = '1') then
              clockScalers <= "0000000000000";
              newClock <= '0';
              indicator <= '1';
          elsif (clk100mhz'event and clk100mhz = '1')then
              indicator <= '0';
              clockScalers <= clockScalers + 1;
              if(clockScalers > prescaler) then
                  newClock <= not newClock;
                  clockScalers <= (others => '0');
              end if;
          end if;
      end process;
      

      使用:

      signal tick10khz: std_ulogic;
      ...
      process(clk100mhz, masterReset) begin
          if masterReset = '1') then
              clockScalers <= "0000000000000";    
              tick10khz <= '0';
          elsif rising_edge(clk100mhz) then
              clockScalers <= clockScalers + 1;
              tick10khz <= '0'
              if(clockScalers > prescaler) then
                  tick10khz <= '1';
                  clockScalers <= (others => '0');
              end if;
          end if;
      end process;
      

      然后,而不是:

      process(clk10khz)
      begin
          if rising_edge(clk10khz) then
              register <= register_input;
          end if;
      end process;
      

      使用:

      process(clk100mhz)
      begin
          if rising_edge(clk100mhz) then
              if tick10khz = '1' then
                  register <= register_input;
              end if;
          end if;
      end process;
      

      结果将是相同的,但只有一个100MHz时钟,这可以避免时钟偏移,时钟缓冲和时钟域交叉问题。

    6. (1)这说明了为什么用初始值声明变量和信号通常不是一个好主意:它隐藏了潜在的问题。如果没有这个,你的信号将被卡在'U'(未初始化),这可能有助于理解问题的来源。