3

我有在船上工作的 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 时钟不工作的原因

在此处输入图像描述

4

1 回答 1

2

您可以(应该)在代码中改进几件事。正如布赖恩已经解释的那样,在您的Behavioral架构中scaler_clk,您应该具有:

if(masterReset = '1') then

代替:

if(masterReset <= '1') then

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

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

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

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

    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,并且只使用那个。它声明SIGNEDandUNSIGNED类型(与 声明相同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>在时钟生成过程中使用 only 。在其他任何地方,最好与时钟同步:

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

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

于 2017-08-28T07:01:01.777 回答