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 项目
我正在尝试对 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 项目