Contoh kode untuk filter FIR / IIR di VHDL?

11

Saya mencoba memulai dengan DSP di papan Spartan-3 saya. Saya membuat papan AC97 dengan chip dari motherboard lama, dan sejauh ini saya mendapatkannya untuk melakukan ADC, gandakan sampel untuk angka <1 (turunkan volume) dan kemudian DAC.

Sekarang saya ingin melakukan beberapa hal dasar DSP, seperti filter low-pass, high-pass dll. Tapi saya benar-benar bingung tentang representasi numerik (integer? Titik tetap? Q0.15? Overflow atau saturation?).

Saya hanya ingin beberapa contoh kode filter sederhana aktual untuk memulai. Tidak ada efisiensi tinggi, cepat, atau semacamnya. Hanya filter teoritis yang diimplementasikan dalam VHDL.

Saya sudah mencari tetapi saya hanya menemukan formula teoritis - Saya mengerti, apa yang saya tidak mengerti adalah bagaimana memproses sampel audio 16-bit, 48KHz yang ditandatangani yang saya dapatkan dari ADC. Saya telah menggunakan perpustakaan ini: http://www.vhdl.org/fphdl/ . Jika saya mengalikan sampel saya dengan 0,5, 0,25, dll, saya bisa mendengar perbedaannya. Tetapi filter yang lebih besar memberi saya kebisingan.

Terima kasih.

hjf
sumber
2
Sementara saya semua menggunakan apa pun yang Anda miliki untuk mempelajari hal-hal, saya ingin menunjukkan bahwa melakukan filter audio dalam FPGA bukanlah cara yang sangat efisien atau biaya yang efektif untuk melakukannya. Jadi, jika Anda melakukan proyek nyata maka saya akan merekomendasikan menggunakan DSP biaya rendah sebagai gantinya. Pengecualian: Ketika Anda melakukan jumlah saluran audio yang tidak baik pada saat yang sama, atau Anda sedang melakukan FIR dengan jumlah ketukan yang tidak masuk akal.

Jawaban:

8

Kedengarannya Anda perlu mencari tahu aspek DSP terlebih dahulu, kemudian melakukan implementasi dalam FPGA.

  • Sortir DSP dalam C, Matlab, Excel, atau di mana pun
  • Coba dan pikirkan bagaimana Anda akan mentransfer apa yang telah Anda pelajari dari itu ke tanah FPGA
  • Temukan Anda telah membuat beberapa asumsi tentang implementasi yang tidak berfungsi dengan baik (seperti penggunaan floating point misalnya)
  • Kembali dan perbarui hal-hal DSP offline Anda untuk memperhitungkan ini.
  • Ulangi n kali :)

Mengenai tipe data, Anda dapat menggunakan bilangan bulat dengan baik.

inilah beberapa contoh kode untuk membuat Anda pergi. Perhatikan bahwa ini kehilangan banyak masalah di dunia nyata (misalnya reset, manajemen overflow) - tapi mudah-mudahan ini bermanfaat:

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;
Martin Thompson
sumber
Terima kasih atas jawaban anda. Itu kurang lebih apa yang saya lakukan tetapi saya mengalami beberapa masalah dengan representasi nomor. ADC saya memberi saya nilai dalam -32k hingga + 32k (masuk 16-bit). Saya juga memiliki masalah dengan filter konstan - bagaimana saya menyatakannya? Dan hasil dari penggandaan antara sampel dan konstanta? Itulah yang paling membingungkan saya.
hjf
@ HJF - itu semua hanya bilangan bulat. Selama semuanya tetap dalam 32 bit Anda OK. JIKA Anda membutuhkan lebar lebih dari itu, Anda dapat menggunakan vektor TANDA TANDA atau TANDA seluas yang Anda inginkan. Atau gunakan tipe fixed_point dari VHDL2008 (lihat di sini: vhdl.org/fphdl )
Martin Thompson
5

Filter low pass FIR paling sederhana yang dapat Anda coba adalah y (n) = x (n) + x (n-1). Anda dapat menerapkan ini dengan cukup mudah di VHDL. Di bawah ini adalah diagram blok yang sangat sederhana dari perangkat keras yang ingin Anda implementasikan.

Block Diagram untuk Filter Low Pass Sederhana

Menurut rumus, Anda memerlukan sampel ADC saat ini dan sebelumnya untuk mendapatkan hasil yang sesuai. Yang harus Anda lakukan adalah mengunci sampel ADC yang masuk di tepi jatuh jam, dan melakukan perhitungan yang sesuai pada tepi naik untuk mendapatkan output yang sesuai. Karena Anda menambahkan dua nilai 16-bit secara bersamaan, ada kemungkinan Anda akan mendapatkan jawaban 17-bit. Anda harus menyimpan input ke register 17-bit dan menggunakan adder 17-bit. Output Anda, bagaimanapun, akan menjadi 16 bit jawaban yang lebih rendah. Kode mungkin terlihat seperti ini, tetapi saya tidak dapat menjamin bahwa kode tersebut akan berfungsi sepenuhnya karena saya belum mengujinya, apalagi mensintesisnya.

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

Seperti yang Anda lihat, Anda dapat menggunakan ide umum ini untuk menambahkan formula yang lebih rumit, seperti yang memiliki koefisien. Rumus yang lebih rumit, seperti filter IIR, mungkin memerlukan penggunaan variabel untuk mendapatkan logika algoritma yang benar. Akhirnya, cara mudah untuk menyiasati filter yang memiliki bilangan real sebagai koefisien adalah menemukan faktor skala sehingga semua angka akhirnya sedekat mungkin dengan bilangan bulat. Hasil akhir Anda harus diperkecil oleh faktor yang sama untuk mendapatkan hasil yang benar.

Saya harap ini bisa berguna bagi Anda dan membantu Anda membuat bola bergulir.

* Ini telah diedit sehingga data latching dan output latching berada dalam proses yang terpisah. Juga menggunakan tipe bertanda bukan std_logic_vector. Saya berasumsi input ADC Anda akan menjadi sinyal std_logic_vector.

dhsieh2
sumber
2
Proses yang memicu kedua sisi (seperti yang telah Anda jelaskan) sangat tidak mungkin disintesis
Martin Thompson
@ Martin Saya berasumsi Anda tahu lebih banyak tentang FPGA daripada saya, tetapi saya telah mengunci data yang masuk pada edge yang jatuh dan mengunci output pada edge yang naik untuk tugas kelas jadi saya pikir ini akan berhasil. Bisakah Anda menjelaskan mengapa proses seperti itu tidak berhasil?
dhsieh2
3
Ini akan bekerja dengan baik di simulator. Sintesis akan mencekiknya (menurut pengalaman saya) karena flipflop di perangkat hanya dapat berputar di satu sisi.
Martin Thompson
@ dhsieh2 Terima kasih, ini adalah jawaban yang saya cari. Pertanyaan lain, bagaimana saya melakukannya jika saya menggunakan nomor yang sudah ditandatangani (ADC saya memberi saya nilai dalam -32k hingga + 32k).
hjf
4
@ Martin, saya mencatat segala sesuatu dari kedua sisi jam sepanjang waktu di Xilinx FPGA, tidak ada masalah. Anda tidak bisa clock FF yang sama dari kedua sisi. Ketika Anda melihat output penganalisa waktu itu benar-benar membuatnya sangat jelas bahwa Anda melakukan sisi yang berlawanan dan menyesuaikan anggaran waktu yang sesuai.
5

Cuplikan kode sederhana lainnya (hanya nyali). Catatan saya tidak menulis VHDL secara langsung, saya menggunakan MyHDL untuk menghasilkan 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;

sirkuit disintesis

Ini adalah implementasi langsung. Ini akan membutuhkan pengganda. Sintesis sirkuit ini, yang ditargetkan untuk Altera Cyclone III, tidak menggunakan pengganda eksplisit tetapi membutuhkan 350 elemen logika.

Ini adalah filter FIR kecil dan akan memiliki respons berikut (tidak terlalu bagus) tetapi harus bermanfaat sebagai contoh.

filter respons

Selain itu saya punya beberapa contoh, di sini dan di sini , yang mungkin berguna.

Selain itu, pertanyaan Anda tampaknya bertanya: "representasi titik tetap apa yang tepat?" Seringkali ketika menerapkan fungsi DSP, representasi titik tetap digunakan, karena menyederhanakan menganalisis filter. Seperti yang disebutkan, fixed-point hanyalah integer artimetik. Implementasi yang sebenarnya hanya bekerja dengan bilangan bulat tetapi representasi yang kami buat bersifat fraksional.
Masalah biasanya muncul ketika mengkonversi dari bilangan bulat implementasi (titik tetap) ke / dari titik buntu desain.

Saya tidak tahu seberapa baik VHDL fixed-point dan tipe floating-point didukung. Mereka akan bekerja dengan baik dalam simulasi tetapi saya tidak tahu apakah mereka akan mensintesis dengan sebagian besar alat sintesis. Saya membuat pertanyaan terpisah untuk ini.

Christopher Felton
sumber
3

OpenCores memiliki sejumlah contoh DSP, IIR dan FIR, termasuk BiQuad. Anda harus mendaftar untuk mengunduh file.

sunting
Saya mengerti komentar Kortuk tentang tautan mati, dan memang, jika tautan ke OpenCores mati jawabannya tidak akan berguna. Saya cukup yakin bahwa ini tidak akan terjadi; tautan saya adalah yang umum, dan itu hanya akan mati jika domain OpenCores yang lengkap akan hilang.
Saya mencoba mencari beberapa contoh yang bisa saya gunakan untuk jawaban ini tetapi terlalu lama untuk diwakili di sini. Jadi saya akan tetap berpegang pada saran saya untuk mendaftar sendiri untuk situs (saya harus pindah ke New York, karena kota asal saya tidak diterima) dan melihat kode yang disajikan di sana.

stevenvh
sumber
Seperti semua hal lainnya, tautan terputus. Kami telah membahas sebelumnya bahwa tautan itu sendiri tidak memberikan jawaban. Bisakah Anda membawa sebagian dari apa yang ada di sana dan membuat jawaban gemuk yang memiliki tautan itu sebagai referensi untuk mempelajari lebih lanjut?
Kortuk
@ Kruk - Saya ingin melakukan ini kemarin. Saya mendaftar kemarin dengan opencores untuk mendapatkan beberapa perincian, tetapi mereka perlu beberapa hari untuk berpikir apakah mereka akan memiliki saya
stevenvh
senang mendengarnya, sejujurnya aku bertanya-tanya apakah ada yang menghalangi jalanmu. Nantikan mendengar lebih banyak tentang hal itu.
Kortuk
1

Saya telah mencoba mengimplementasikan skrip untuk implementasi automatis filter IIR, di mana Anda dapat menentukan apakah desain harus secepat mungkin (sehingga setiap perkalian dilakukan dengan pengali khusus) atau sekecil mungkin (sehingga setiap pengali digunakan kembali).

Sumber-sumber tersebut telah dipublikasikan di alt.sources sebagai "Implementasi filter IIR yang dapat disiasati tetapi dapat disintesis di VHDL" (Anda juga dapat menemukannya di arsip google: https://groups.google.com/group/alt.sources/msg/c8cf038b9b8ceeec ? dmode = sumber )

Posting ke alt.source dalam format "shar", jadi Anda perlu menyimpan pesan sebagai teks, dan menghapusnya (dengan utilitas "unshar") untuk mendapatkan sumber.

WZab
sumber
0

Bagaimana dengan ini? https://github.com/MauererM/VIIRF

Ini mengimplementasikan filter IIR berbasis biquad (SOS, orde kedua) yang menangani implementasi titik tetap. Ini juga menampilkan skrip Python untuk desain dan verifikasi filter. Itu tidak menggunakan konstruksi FPGA khusus-Vendor dan Anda dapat memilih trade-off antara penggunaan area kecepatan tinggi dan rendah.

KeyNuts
sumber