VHDL中的FIR / IIR滤波器的代码示例?


11

我正在尝试在我的Spartan-3开发板上使用DSP。我用一块旧主板上的芯片制作了一个AC97板,到目前为止,我可以用它来做ADC,将样本乘以<1(减小音量),然后再乘DAC。

现在,我想做一些基本的DSP工作,例如低通滤波器,高通等。但是我对数字表示感到困惑(例如整数?定点?Q0.15?溢出还是饱和?)。

我只是想要一些实际的简单过滤器的示例代码来入门。没有高效,快速或类似的功能。只是在VHDL中实现的理论滤波器。

我一直在搜索,但我只是找到理论公式-我明白了,我不了解如何处理从ADC获得的带符号的16位,48KHz音频样本。我一直在使用这些库:http : //www.vhdl.org/fphdl/。如果我将样本乘以0.5、0.25等,则可以听到差异。但是更大的滤波器只会给我带来噪音。

谢谢。


2
尽管我全力以赴使用您现有的任何东西来学习知识,但我想指出的是,在FPGA中进行音频滤波器并不是一种非常有效或具有成本效益的方法。因此,如果您做一个真实的项目,那么我建议您使用低成本的DSP。例外:当您同时处理音频通道数量过多时,或者您在进行FIR时,抽头数量非常荒谬。

Answers:


8

听起来您需要首先弄清楚DSP方面,然后在FPGA中实现。

  • 在C,Matlab,Excel或其他任何地方整理出DSP
  • 尝试思考如何将从中学到的知识转移到FPGA领域
  • 发现您已经对执行不佳的情况做出了一些假设(例如,使用浮点)
  • 返回并更新您的离线DSP素材,以考虑到这一点。
  • 重复n次:)

关于数据类型,您可以使用整数就可以了。

这是一些示例代码,助您一臂之力。请注意,它缺少许多实际问题(例如,重置,溢出管理)-但希望它能提供指导:

library ieee;
use ieee.std_logic_1164.all;
entity simple_fir is
    generic (taps : integer_vector); 
    port (
        clk      : in  std_logic;
        sample   : in  integer;
        filtered : out integer := 0);
end entity simple_fir;
----------------------------------------------------------------------------------------------------------------------------------
architecture a1 of simple_fir is
begin  -- architecture a1
    process (clk) is
        variable delay_line : integer_vector(0 to taps'length-1) := (others => 0);
        variable sum : integer;
    begin  -- process
        if rising_edge(clk) then  -- rising clock edge
            delay_line := sample & delay_line(0 to taps'length-2);
            sum := 0;
            for i in 0 to taps'length-1 loop
                sum := sum + delay_line(i)*taps(taps'high-i);
            end loop;
            filtered <= sum;
        end if;
    end process;
end architecture a1;
----------------------------------------------------------------------------------------------------------------------------------
-- testbench
----------------------------------------------------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity tb_simple_fir is
end entity tb_simple_fir;
architecture test of tb_simple_fir is
    -- component generics
    constant lp_taps : integer_vector := ( 1, 1, 1, 1, 1);
    constant hp_taps : integer_vector := (-1, 0, 1);

    constant samples : integer_vector := (0,0,0,0,1,1,1,1,1);

    signal sample   : integer;
    signal filtered : integer;
    signal Clk : std_logic := '1';
    signal finished : std_logic;
begin  -- architecture test
    DUT: entity work.simple_fir
        generic map (taps => lp_taps)  -- try other taps in here
        port map (
            clk      => clk,
            sample   => sample,
            filtered => filtered);

    -- waveform generation
    WaveGen_Proc: process
    begin
        finished <= '0';
        for i in samples'range loop
            sample <= samples(i);
            wait until rising_edge(clk);
        end loop;
        -- allow pipeline to empty - input will stay constant
        for i in 0 to 5 loop
            wait until rising_edge(clk);
        end loop;
        finished <= '1';
        report (time'image(now) & " Finished");
        wait;
    end process WaveGen_Proc;

    -- clock generation
    Clk <= not Clk after 10 ns when finished /= '1' else '0';
end architecture test;

感谢您的回答。这差不多是我所做的,但是我在数字表示方面遇到了一些问题。我的ADC给我的值在-32k到+ 32k(带符号的16位)之间。我也有过滤器常数的问题-我该如何表示?以及样本和常数之间的相乘结果?那就是让我最困惑的地方。
hjf 2011年

@hjf-全部都是整数。只要一切都在32位以内,就可以了。如果需要的宽度比您需要的宽度大,可以使用任意宽度的UNSIGNED或SIGNED向量。或使用VHDL2008中的fixed_point类型(请参见此处:vhdl.org/fphdl
Martin Thompson

5

您可以尝试的最简单的低通FIR滤波器是y(n)= x(n)+ x(n-1)。您可以在VHDL中很容易地实现这一点。下面是您要实现的硬件的非常简单的框图。

简单的低通滤波器的框图

根据公式,您需要当前和以前的ADC采样才能获得适当的输出。您应该做的是在时钟的下降沿锁存输入的ADC采样,并在上升沿执行适当的计算以获得适当的输出。由于您将两个16位值相加在一起,因此最终可能会得到17位答案。您应将输入存储到17位寄存器中,并使用17位加法器。但是,您的输出将是答案的低16位。代码可能看起来像这样,但是由于我没有测试它,所以我不能保证它会完全起作用,更不用说对其进行合成了。

IEEE.numeric_std.all;
...
    signal x_prev, x_curr, y_n: signed(16 downto 0);
    signal filter_out: std_logic_vector(15 downto 0);
...
process (clk) is
begin
    if falling_edge(clk) then
        --Latch Data
        x_prev <= x_curr;
        x_curr <= signed('0' & ADC_output); --since ADC is 16 bits
    end if;
end process;

process (clk) is
begin
    if rising_edge(clk) then
        --Calculate y(n)
        y_n <= x_curr + x_prev;
    end if;
end process;

filter_out <= std_logic_vector(y_n(15 downto 0));  --only use the lower 16 bits of answer

如您所见,您可以使用此一般思想来添加更复杂的公式,例如带有系数的公式。更复杂的公式(例如IIR滤波器)可能需要使用变量来使算法逻辑正确。最后,绕开以实数为系数的滤波器的一种简单方法是找到比例因子,以使所有数字最终尽可能接近整数。您的最终结果将必须按相同的比例缩小以得到正确的结果。

希望对您有用,并帮助您取得成功。

*此文件已经过编辑,因此数据锁存和输出锁存处于单独的过程中。还使用带符号的类型而不是std_logic_vector。我假设您的ADC输入将是std_logic_vector信号。


2
该进程关闭触发两个边缘(如你所描述的)是不太可能合成
马丁汤普森

@Martin我假设您对FPGA的了解比我了解得多,但是对于类分配,我在下降沿锁存了传入数据,在上升沿锁存了输出,因此我认为这是可行的。您能解释为什么这样的程序不起作用吗?
dhsieh2 2011年

3
在模拟器中可以正常工作。综合(根据我的经验),合成器会阻塞,因为设备中的触发器只能在一个边沿上计时。
Martin Thompson

@ dhsieh2谢谢,这是我一直在寻找的答案。另一个问题,如果我使用带符号的数字(我的ADC给我的值在-32k到+ 32k之间),我该怎么办。
hjf 2011年

4
@Martin我一直在Xilinx FPGA的两个时钟边沿上进行时钟处理,没问题。您只是无法在两个边沿都输入相同的FF。当您查看时序分析器的输出时,它实际上很清楚您在做相反的事情,并相应地调整了时序预算。

5

另一个简单的代码片段(只是胆量)。注意,我没有直接编写VHDL,而是使用MyHDL生成VHDL。

-- VHDL code snip
architecture MyHDL of sflt is

type t_array_taps is array(0 to 6-1) of signed (15 downto 0);
signal taps: t_array_taps;

begin

SFLT_RTL_FILTER: process (clk) is
    variable sum: integer;
begin
    if rising_edge(clk) then
        sum := to_integer(x * 5580);
        sum := to_integer(sum + (taps(0) * 5750));
        sum := to_integer(sum + (taps(1) * 6936));
        sum := to_integer(sum + (taps(2) * 6936));
        sum := to_integer(sum + (taps(3) * 5750));
        sum := to_integer(sum + (taps(4) * 5580));
        taps(0) <= x;
        for ii in 1 to 5-1 loop
            taps(ii) <= taps((ii - 1));
        end loop;
        y <= to_signed(sum, 16);
    end if;
end process SFLT_RTL_FILTER;

end architecture MyHDL;

合成电路

这是直接实现。这将需要乘数。针对Altera Cyclone III的该电路综合并未使用任何显式乘法器,但需要350个逻辑元件。

这是一个小型FIR滤波器,将具有以下响应(不是很大),但应作为示例。

过滤器响应

另外,在这里这里,我还有几个示例可能会有用。

另外,您的问题似乎在问:“什么是合适的定点表示形式?” 在实现DSP功能时,经常使用定点表示,因为它简化了对滤波器的分析。如前所述,定点只是整数假想法。实际的实现只是使用整数,但是我们的先验表示是分数。
从实现整数(定点)转换为来回设计浮点时,通常会出现问题。

我不知道VHDL定点和浮点类型的支持程度。它们将在模拟中正常工作,但我不知道它们是否可以与大多数综合工具进行综合。我为此创建了一个单独的 问题


3

OpenCores有许多DSP示例,包括BiQuad,包括IIR和FIR。您必须注册才能下载文件。

编辑
我了解Kortuk对无效链接的评论,实际上,如果指向OpenCores的链接消失了,答案将变得毫无用处。我非常有信心不会发生这种情况。我的链接是通用链接,只有在完整的OpenCores域消失时它才会消失。
我试图寻找一些可以用于此答案的示例,但是它们太长了,无法在此处显示。因此,我会坚持我的建议自己注册该网站(我不得不搬到纽约,因为未接受我的家乡),并查看那里提供的代码。


与所有事物一样,链接断开。前面我们已经讨论过,链接本身不能解决问题。您能否提出一些建议,并给出一个肉味的答案,并以此链接作为参考以了解更多信息?
2011年

@Kortuk-我昨天想这样做。我昨天在opencores上注册以获取一些详细信息,但是他们需要几天时间才能考虑是否可以拥有我
stevenvh 2011年

很高兴听到这个消息,我实在想知道您是否遇到了麻烦。期待听到更多有关它的信息。
2011年

1

我尝试实现用于IIR过滤器的认证实现的脚本,您可以在其中定义设计是否应尽可能快(因此每个乘法都使用专用乘法器执行)或尽可能小(因此可以重复使用每个乘法器)。

来源已在alt.sources上发布为“ VHDL中IIR过滤器的行为性但可综合实现”(您也可以在google存档中找到它:https//groups.google.com/group/alt.sources/msg/c8cf038b9b8ceeec ?dmode = source

alt.sources上的帖子采用“共享”格式,因此您需要将消息另存为文本,并取消共享(使用“ unshar”实用程序)以获取源。


0

这个怎么样? https://github.com/MauererM/VIIRF

它实现了一个基于二阶(SOS,二阶部分)的IIR滤波器,该滤波器负责定点实现。它还具有Python脚本,用于设计和验证过滤器。它不使用特定于供应商的FPGA构造,您可以在高速和低面积使用之间进行权衡选择。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.