VHDL 中的奇怪行为

Strange behaviour in VHDL

我正在尝试对 50 Mhz 的 ADC 的 14 位信号进行积分(求和)。积分从信号 "trigger" 的上升沿开始。如果积分达到定义的阈值 (6000000),则数字信号 ("dout") 应设置为 0(当 "trigger" 变为 1 时变为 1)。到目前为止,这是一项非常容易的任务。 虽然在硬件本身 (Cyclone V) 上我意识到了一个奇怪的行为。虽然我保持 ADC 的电压电平恒定,但输出信号 "dout" 的脉冲宽度有时会波动(尽管对于 ADC 的恒定 14 位值,它应该保持几乎恒定,它具有低噪声) .脉冲宽度随着电压电平的升高而减小,因此集成本身工作正常。但它一直在波动。

这是我的代码:

library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;

entity integrator is
port(
  trigger:  in std_logic;
  adc:  in std_logic_vector(13 downto 0);
  clk:  in std_logic;
  dout: out std_logic);
end integrator ;

architecture rtl of integrator is
  signal sum : integer;
begin
  process(clk) is
  begin     
        if rising_edge(clk) then                
            if (trigger='1') and (sum<6000000) then
                sum<=sum+to_integer(unsigned(adc));
                dout<='1';
            else
                dout<='0';
                if (trigger='0') then
                    sum<=0;
                end if;
            end if;
        end if;
  end process;
end rtl;

我使用 Quartus Prime 的 SignalTab II 检查了信号。我意识到 "sum" 的值在上升,但并不完全正确(比较我手动计算的 "adc" 值的总和)。

我使用 PLL 将 50 Mhz 时钟 ("clk") 相移了大约 90 度。生成的时钟用作 ADC 时钟的输入。我遗漏了 PLL 和匹配的 "sum" 的值。尽管如此,我还是看到了 "dout" 信号(示波器)的波动。

更奇怪:我把"sum"的类型改成了unsigned,最后波动就消失了。但仅在不使用 PLL 的情况下!但是在对下面的代码进行调整时,波动又回来了。也许整数和无符号的总和导致了另一个时间?!?

现在的问题是: - 为什么在使用 PLL 时 "sum" 的值不正确(我虽然 "adc" 的值在相移 90 度时应该保持半个时钟周期不变)? - 为什么我看到 "dout" 的波动?代码有问题吗?

编辑 1:添加测试平台

这是我的测试台:

library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;

entity testbench is
end testbench; 

architecture tb of testbench is


component integrator is
port(
  trigger:  in std_logic;
  adc:  in std_logic_vector(13 downto 0);
  clk:  in std_logic;
  dout: out std_logic);
end component;


signal trigger_in, clk_in, dout_out: std_logic;
signal adc_in:  std_logic_vector(13 downto 0);
begin

  DUT: integrator port map(trigger_in, adc_in, clk_in, dout_out);

  process
  begin
    for I in 1 to 4500 loop
      clk_in <= '0';
      wait for 10 ns;
      clk_in <= '1';
      wait for 10 ns;
    end loop; 
    wait;
  end process;

  process
  begin
      trigger_in <= '0';
      wait for 10 us;
      trigger_in <= '1';
      wait for 30 us;
      trigger_in <= '0';
      wait for 10 us;
      trigger_in <= '1';
      wait for 30 us;
      trigger_in <= '0';
      wait for 10 us;
      wait;
  end process;

  process
  begin
      adc_in <= (others => '0');
      wait for 10 us;
      adc_in <= std_logic_vector(to_unsigned(6000, 14));
      wait for 30 us;
      adc_in <= (others => '0');
      wait for 10 us;
      adc_in <= std_logic_vector(to_unsigned(6000, 14));
      wait for 30 us;
      adc_in <= (others => '0');
      wait for 10 us;
      wait;
  end process;


end tb;

结果输出:

我要了一个测试平台,因为你的代码看起来有点奇怪。正如 user1155120 一样,我注意到求和发生在任何可能导致溢出的条件之外。您没有看到溢出,因为您没有在测试台上对其进行测试。

我可以提出更改代码的建议,但问题在于规范:

If the integral reaches a defined threshold (6000000),...

您没有指定在这种情况下总和应该做什么。继续?抓住? 如果你让它继续下去,它会在某个时候扭曲并变成负值。

一个可能的解决方案是:

if sum<some_maximum_value_you_define then
   sum<=sum+to_integer(unsigned(adc));
end if;

可能的最大值为 231-214-1。

或者,您必须确保 trigger_in 足够快,以使总和永远不会溢出。使用 50MHz 采样和 14 位 ADC,这意味着至少 382Hz。


我会添加一些 VHDL 代码来检查 ADC 信号。例如看到的最大值和最小值。将这些与实际(或多或少恒定的)输入值进行比较。这可能会让您了解采样的 stability/reliability。

感谢所有回复。它帮助我更进一步。我检查了 ADC 信号,但只有大约 10(14 位)的噪声并且没有意外值。此外,所有其他信号(没有逻辑尝试)都很好。

我还找到了不一致求和行为的解决方案。我只是在计算之前将它保存在 temp_adc 中。我尝试了变量和信号,但我选择了信号,因为我可以在 SignalTap 中将其可视化(当然现在有一个时钟周期的延迟):

library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;

entity integrator is
port(
  trigger:  in std_logic;
  adc:  in std_logic_vector(13 downto 0);
  clk:  in std_logic;
  dout: out std_logic);
end integrator ;

architecture rtl of integrator is
  signal sum : integer;
  signal temp_adc : unsigned(13 downto 0);
begin
  process(clk) is
  begin     
        temp_adc<=unsigned(adc);
        if rising_edge(clk) then                
            if (trigger='1') and (sum<6000000) then
                sum<=sum+to_integer(tem_adc);
                dout<='1';
            else
                dout<='0';
                if (trigger='0') then
                    sum<=0;
                end if;
            end if;
        end if;
  end process;
end rtl;

在 SignalTap 中,它现在大部分时间都很好地拟合 (sum=sum+temp_adc)。回到问题上来:我在 SignalTap 中找到了一种触发意外事件的方法。我发现了一个非常奇怪的行为:

设 t=0 为 trigger 进入 '1' 的周期。输出如下所示:

这意味着由于 sum 中的高值,dout 仅在单个时钟周期内变为 '1'。这是随机发生的,但大约每 300 个脉冲发生一次。 看起来像溢出一样,在 sum 中添加了一个 adc。你知道这是从哪里来的吗?

此外,我还尝试了 ADC 时钟的 PLL。我尝试了不同的相移(0°、90°、180°),但结果或多或少是一样的。

抱歉各位。问题是配置错误的 Quartus 项目