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を実行している場合。

回答:


8

最初にDSPの側面を理解してから、FPGAに実装する必要があるようです。

  • DSPをC、Matlab、Excel、またはその他の場所で整理する
  • それから学んだことを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

@hjf-それはすべて整数です。すべてが32ビットの範囲内にある限り、問題はありません。それ以上の幅が必要な場合は、UNSIGNEDまたはSIGNEDベクトルを好きなだけ使用できます。または、VHDL2008のfixed_point型を使用(ここを参照:vhdl.org/fphdl
Martin Thompson

5

試すことができる最も単純なローパスFIRフィルターは、y(n)= x(n)+ x(n-1)です。これはVHDLで非常に簡単に実装できます。以下は、実装するハードウェアの非常に単純なブロック図です。

単純なローパスフィルターのブロック図

式に従って、適切な出力を取得するには、現在および以前のADCサンプルが必要です。行うべきことは、クロックの立ち下がりエッジで着信ADCサンプルをラッチし、適切な出力を取得するために立ち上がりエッジで適切な計算を実行することです。2つの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 Thompson

@Martin私はあなたが私よりも多くのFPGAについて知っていると思いますが、クラス割り当てのために立ち下がりエッジで入力データをラッチし、立ち上がりエッジで出力をラッチしたので、これはうまくいったと思いました。そのようなプロセスが機能しない理由を説明できますか?
dhsieh2

3
シミュレーターでは問題なく動作します。ただし、デバイスのフリップフロップは1つのエッジでのみクロックを供給できるため、シンセサイザは(私の経験では)制限します。
Martin Thompson

@ dhsieh2ありがとう、これは私が探していた種類の答えです。もう1つの質問、署名された数値を使用している場合はどうすればよいですか(私のADCでは-32kから+ 32kの値が得られます)。
hjf

4
@Martinザイリンクス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;

合成回路

これは直接の実装です。乗数が必要になります。この回路の合成は、アルテラのCyclone IIIをターゲットとしており、明示的な乗算器を使用していませんが、350のロジックエレメントが必要でした。

これは小さなFIRフィルターであり、次の応答があります(それほど大きくはありません)が、例として役立つはずです。

フィルター応答

さらに、ここここに、いくつかの例があります。

また、あなたの質問は、「適切な固定小数点表現とは何ですか?」DSP機能を実装する場合、フィルターの分析を簡素化するため、固定小数点表現がよく使用されます。先に述べたように、固定小数点は単なる整数の人工です。実際の実装は整数を処理するだけですが、preceived表現は小数です。
通常、実装整数(固定小数点)から設計浮動小数点への変換、または浮動小数点設計への変換時に問題が発生します。

VHDLの固定小数点型と浮動小数点型がどの程度サポートされているかわかりません。シミュレーションでは問題なく機能しますが、ほとんどの合成ツールで合成できるかどうかはわかりません。これについて別の質問を作成しました 。


3

OpenCoresには、BiQuadを含む、いくつかのDSPサンプル、IIRおよびFIRがあります。ファイルをダウンロードするには登録が必要です。

編集
リンク切れに関するKortukのコメントを理解しています。実際、OpenCoresへのリンクが機能しなくなった場合、答えは役に立たなくなります。私はこれが起こらないと確信しています。私のリンクは一般的なものであり、完全なOpenCoresドメインが消える場合にのみ死にます。
この回答に使用できるいくつかの例を探しましたが、それらはすべてここに表示するには長すぎます。だから私は自分のサイトに自分で登録するようにアドバイスし(自分の故郷が受け入れられなかったため、ニューヨークに移動する必要がありました)、そこに表示されているコードを確認します。


すべてのものと同様に、リンクは壊れます。以前に、それ自体のリンクでは答えが得られないことを説明しました。そこにあるものをいくつか持ち帰って、詳細を知るための参照としてそのリンクを含む詳細な回答を作成できますか?
Kortuk、2011

@Kortuk-昨日これをやりたかった。私は昨日、いくつかの詳細を得るために
オープンコア

それを聞いてうれしかったです、何か邪魔になったのかと正直に思っていました。それについてもっと聞くのを楽しみにしています。
Kortuk、2009

1

IIRフィルターのオーソマティック実装用のスクリプトを実装しようとしました。設計を可能な限り高速にする(各乗算を専用の乗算器で実行する)か、できるだけ小さくする(各乗算器を再利用する)かを定義できます。

ソースは、alt.sourcesで「VHDLのIIRフィルターの動作はするが合成可能な実装」として公開されています(Googleアーカイブで見つけることもできます:https : //groups.google.com/group/alt.sources/msg/c8cf038b9b8ceeec ?dmode = source

alt.sourcesへの投稿は「shar」形式であるため、メッセージをテキストとして保存し、ソースを取得するには(「unshar」ユーティリティを使用して)共有解除する必要があります。


0

これはどう? https://github.com/MauererM/VIIRF

固定小数点の実装を処理するバイカッド(SOS、2次セクション型)ベースのIIRフィルターを実装します。また、フィルターの設計と検証のためのPythonスクリプトも備えています。ベンダー固有のFPGAコンストラクトを使用せず、高速使用と低面積使用の間のトレードオフを選択できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.