VHDL 状态机测试平台 - 在板上工作但不在仿真中

VHDL state machine testbench - works when on board but not on simulation

我有在板上运行的 VHDL 实现,它检测到序列 01110 并将为 2 个时钟计数生成一个标志。它也检测重叠序列,其中 011101110 会升旗两次。

我已经使用电路板上的逻辑分析器检查了我的实现,并且非常有信心它可以正常工作。我以 10 kHz 的频率输入 0111 的重复序列,在板上,它有一个 100 MHz 的时钟,我用预分频器将其缩放到 10 kHz。

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

来自电路板逻辑分析仪的图像

图片来自测试台

测试平台代码

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 时钟不工作的原因

您可以(应该)改进您的代码中的几件事。正如 Brian 已经解释的那样,在 scaler_clkBehavioral 架构中,您应该:

if(masterReset = '1') then

而不是:

if(masterReset <= '1') then

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

无论如何,您的组件实例是未绑定的(它们没有关联 entity/architecture)并且模拟器将它们视为黑盒。它们的输出不受驱动,除了您声明的初始值 (1)。

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

  1. 使用配置指定每个组件实例应使用哪个 entity/architecture 对:

    for all: scaler_clk use entity work.scaler_clk(Behavioral);
    
  2. 使用实体实例化而不是组件:

    uut: entity work.scaler_clk(Behavioral) port map...
    

现在,让我们来看看您的代码中可以改进的其他一些方面:

  1. 您使用的是非标准包,通常甚至不兼容:IEEE.STD_LOGIC_ARITHIEEE.STD_LOGIC_UNSIGNED。因为它们不是标准的,所以它们甚至不应该出现在标准 IEEE 库中。您应该改用 IEEE.NUMERIC_STD,并且只使用那个。它声明了 SIGNEDUNSIGNED 类型(与 STD_LOGIC_VECTOR 的声明相同)并重载了它们的算术运算符。

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

    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;
    

    也会这样做。对您的 input_changes 流程也有同样的评论。

  3. 您的 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)更为明智,因为编译器 and/or 如果您在设计中不小心造成短路,模拟器将引发错误。

  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 时钟,这避免了时钟偏移、时钟缓冲和时钟域交叉问题。

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