VHDL'deki FIR / IIR filtreleri için kod örneği?


11

Spartan-3 kartımda DSP kullanmaya başlamaya çalışıyorum. Eski bir anakarttan çipli bir AC97 kartı yaptım ve şimdiye kadar ADC'yi yaptım, örnekleri <1 (hacmi azalt) ve sonra DAC için çarptım.

Şimdi bir düşük geçiş filtresi, yüksek geçiş vb. Gibi bazı temel DSP şeyler yapmak istiyorum. Ama gerçekten sayısal temsil (tamsayılar? Sabit nokta? Q0.15? Taşma veya doygunluk?) Hakkında kafam karıştı.

Sadece başlamak için gerçek bir basit filtre bazı örnek kod istiyorum . Yüksek verimli, hızlı veya benzeri bir şey yok. Sadece VHDL'de uygulanan teorik filtre.

Arama yapıyordum ama sadece teorik formüller buluyorum - anladım, anlamadığım şey ADC'den aldığım imzalı 16 bit, 48KHz ses örneklerinin nasıl işleneceğidir. Şu kütüphaneleri kullanıyorum: http://www.vhdl.org/fphdl/ . Örneklerimi 0,5, 0,25 vb. İle çarparsam farkı duyabiliyorum. Ama daha büyük bir filtre bana sadece gürültü veriyor.

Teşekkürler.


2
Elimdeki her şeyi bir şeyler öğrenmek için kullandığım halde, bir FPGA'da ses filtreleri yapmanın bunu yapmanın çok verimli veya uygun maliyetli bir yolu olmadığını belirtmek isterim. Yani, gerçek bir proje yaparsanız, bunun yerine düşük maliyetli bir DSP kullanmanızı öneririm. İstisnalar: Aynı anda ungodly sayıda ses kanalı yaparken veya FIR'ları çok sayıda dokunuşla yaparken.

Yanıtlar:


8

İlk olarak DSP yönlerini bulmanız, sonra FPGA'da bir uygulama yapmanız gerektiği anlaşılıyor.

  • DSP'yi C, Matlab, Excel veya başka bir yerde sıralayın
  • Bundan öğrendiklerinizi FPGA alanına nasıl aktaracağınızı düşünün ve düşünün.
  • Uygulama hakkında iyi çalışmayan bazı varsayımlar yaptığınızı keşfedin (örneğin, kayan nokta kullanımı gibi)
  • Geri dönün ve bunu dikkate almak için çevrimdışı DSP öğelerinizi güncelleyin.
  • N kez yineleyin :)

Veri türleriyle ilgili olarak, tamsayıları iyi kullanabilirsiniz.

İşte size örnek bir kod. Birçok gerçek dünya sorununun eksik olduğunu unutmayın (örneğin sıfırlama, taşma yönetimi) - ancak umarım öğretici olur:

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;

Cevabınız için teşekkürler. Bu az çok yaptım ama sayı temsili ile ilgili bazı sorunlar yaşıyorum. ADC'm bana -32k ila + 32k (imzalı 16 bit) değerleri veriyor. Ayrıca filtre sabiti problemim var - bunu nasıl temsil ederim? Ve numune ile sabit arasında çarpmanın sonucu mu? Beni en çok şaşırtan şey bu.
HJF

@hjf - hepsi sadece tamsayılar. Her şey 32 bit içinde kaldığı sürece sorun yok. Bundan daha fazla genişliğe ihtiyacınız varsa, UNSIGNED veya SIGNED vektörlerini istediğiniz kadar kullanabilirsiniz. Veya VHDL2008'den fixed_point türlerini kullanın (buraya bakın: vhdl.org/fphdl )
Martin Thompson

5

Deneyebileceğiniz en basit düşük geçişli FIR filtresi y (n) = x (n) + x (n-1) 'dir. Bunu VHDL'de kolayca uygulayabilirsiniz. Aşağıda, uygulamak istediğiniz donanımın çok basit bir blok şeması bulunmaktadır.

Basit Düşük Geçiş Filtresi için Blok Şeması

Formüle göre, uygun çıktıyı elde etmek için mevcut ve önceki ADC örneklerine ihtiyacınız vardır. Yapmanız gereken, gelen ADC örneklerini saatin düşen kenarına kilitlemek ve uygun çıkışı elde etmek için yükselen kenarda uygun hesaplamaları yapmaktır. Birlikte iki 16 bit değer eklediğiniz için, 17 bitlik bir yanıt alabilirsiniz. Girişi 17 bitlik kayıtlara kaydetmeli ve 17 bitlik bir toplayıcı kullanmalısınız. Ancak çıktınız yanıtın en düşük 16 biti olacaktır. Kod böyle bir şeye benzeyebilir, ancak test etmediğim için tamamen çalışacağını garanti edemem, yalnız sentezlemeliyim.

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

Gördüğünüz gibi, bu genel fikri, katsayıları olanlar gibi daha karmaşık formüller eklemek için kullanabilirsiniz. IIR filtreleri gibi daha karmaşık formüller, algoritma mantığını doğru hale getirmek için değişkenlerin kullanılmasını gerektirebilir. Son olarak, katsayılar olarak gerçek sayılara sahip filtreleri dolaşmanın kolay bir yolu, bir ölçek faktörü bulmaktır, böylece tüm sayılar mümkün olduğunca tam sayılara yakın olur. Doğru sonucu elde etmek için nihai sonucunuzun aynı faktörle yeniden ölçeklenmesi gerekecektir.

Umarım bu sizin için yararlı olabilir ve topu yuvarlamanıza yardımcı olabilir.

* Bu, veri mandallama ve çıkış mandallama ayrı işlemlerde olacak şekilde düzenlenmiştir. Ayrıca std_logic_vector yerine imzalı türleri kullanıyor. ADC girişinizin bir std_logic_vector sinyali olacağını varsayıyorum.


2
Her iki kenarı da tetikleyen işlemlerin (açıkladığınız gibi) sentezlenmesi pek olası değildir
Martin Thompson

@Martin FPGA'lar hakkında benden çok daha fazla şey bildiğinizi varsayıyorum, ancak gelen veriyi düşen kenardan ve bir sınıf ödevi için yükselen kenardan mandallanmış çıktıdan aldım, bu yüzden bunun işe yarayacağını düşündüm. Bu süreçlerin neden işe yaramadığını açıklayabilir misiniz?
dhsieh2

3
Bir simülatörde iyi çalışır. Cihazdaki flipfloplar sadece bir kenarda saat yapabildiğinden, sentezleyiciler bunu (deneyime göre) boğar .
Martin Thompson

@ dhsieh2 Teşekkürler, aradığım cevap budur. Başka bir soru, İmzalı sayılar kullansaydım nasıl yapardım (ADC'm bana -32k ila + 32k arasında değerler verir).
HJF

4
@Martin Xilinx FPGA'larda her zaman her iki saat kenarından işleri seyrediyorum, sorun değil. Aynı FF'yi her iki kenardan da saatte gösteremezsiniz. Zamanlama analizörü çıkışına baktığınızda, karşıt kenarlar yaptığınızı gerçekten netleştirir ve zamanlama bütçesini buna göre ayarlar.

5

Başka bir basit kod snippet'i (sadece cesaret). Not VHDL'yi doğrudan yazmadım, VHDL'yi oluşturmak için MyHDL'yi kullandım.

-- 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;

sentezlenmiş devre

Bu doğrudan bir uygulamadır. Çarpanları gerektirecektir. Bir Altera Cyclone III için hedeflenen bu devrenin sentezi, herhangi bir açık çarpan kullanmamış, ancak 350 mantık elemanı gerektirmiştir.

Bu küçük bir FIR filtresidir ve aşağıdaki yanıta (çok büyük değil) sahip olacaktır, ancak örnek olarak yararlı olmalıdır.

filtre yanıtı

Ayrıca burada ve burada birkaç örnek var , bu yararlı olabilir.

Ayrıca, sorunuz şu soruyu soruyor: “uygun sabit nokta gösterimi nedir?” Sıklıkla DSP fonksiyonlarını uygularken, filtrelerin analizini kolaylaştırdığı için sabit nokta gösterimi kullanılır. Belirtildiği gibi, sabit nokta tamsayı artemetiktir. Gerçek uygulama sadece tamsayılarla çalışmaktadır, ancak önceden temsil ettiğimiz temsil kesirli.
Sorunlar genellikle uygulama tamsayısından (sabit nokta) / fro tasarım kayan noktasına dönüştürülürken ortaya çıkar.

VHDL sabit nokta ve kayan nokta tiplerinin ne kadar iyi desteklendiğini bilmiyorum. Simülasyonda iyi çalışırlar, ancak çoğu sentez aracıyla sentezlenip sentezlenmeyeceklerini bilmiyorum. Bunun için ayrı bir soru yarattım .


3

OpenCores , BiQuad dahil olmak üzere IIR ve FIR gibi bir dizi DSP örneğine sahiptir. Dosyaları indirmek için kaydolmanız gerekir.

edit
Kortuk'un ölü bağlantılar hakkındaki yorumunu anlıyorum ve gerçekten de OpenCores bağlantısı ölürse cevap işe yaramaz hale gelecektir. Bunun olmayacağından oldukça eminim; bağlantım genel bir bağlantı ve yalnızca tam OpenCores etki alanı kaybolursa ölür.
Bu cevap için kullanabileceğim bazı örnekler aramaya çalıştım, ancak burada temsil edilmek için çok uzunlar. Bu yüzden siteye kaydolmak için tavsiyeme bağlı kalacağım (New York'a taşınmak zorunda kaldım, çünkü memleketim kabul edilmedi çünkü) ve orada sunulan koda bir göz atın.


Her şeyde olduğu gibi, bağlantılar kopar. Daha önce kendi başına bir bağın cevap vermediğini tartışmıştık. Orada bazı şeyleri getirebilir ve daha fazla bilgi edinmek için referans olarak bu bağlantıya sahip etli bir cevap verebilir misiniz?
Kortuk

@Kortuk - Bunu dün yapmak istedim. Dün bazı ayrıntıları almak için opencores ile kaydoldum, ancak bana sahip olup olmadıklarını düşünmek için birkaç güne ihtiyaçları var
stevenvh

duyduğuma sevindim, dürüstçe yolunda bir şey olup olmadığını merak ediyordum. Bu konuda daha fazla şey duymayı dört gözle bekliyoruz.
Kortuk

1

Tasarımın olabildiğince hızlı (yani her çarpma, özel çarpanla gerçekleştirilir) veya olabildiğince küçük (yani her çarpanın yeniden kullanılması) olup olmadığını tanımlayabileceğiniz IIR filtrelerinin otomatik olarak uygulanması için komut dosyaları uygulamaya çalıştım.

Kaynaklar alt.sources sitesinde "VHDL'de IIR filtrelerinin davranışsal ancak sentezlenebilir uygulaması" olarak yayınlanmıştır (bunu Google arşivinde de bulabilirsiniz: https://groups.google.com/group/alt.sources/msg/c8cf038b9b8ceeec ? dmode = kaynak )

Alt.sources'a gönderilen gönderiler "shar" biçimindedir, bu nedenle mesajı almak için iletiyi metin olarak kaydetmeniz ve paylaşımdan kaldırmanız gerekir ("unshar" yardımcı programıyla).


0

Buna ne dersin? https://github.com/MauererM/VIIRF

Sabit nokta uygulamasıyla ilgilenen biquad (SOS, ikinci dereceden bölümler) tabanlı bir IIR filtresi uygular. Ayrıca, filtrenin tasarımı ve doğrulanması için Python komut dosyaları içerir. Satıcıya özel FPGA yapıları kullanmaz ve yüksek hızlı ve düşük alan kullanımı arasındaki ödünleşimi seçebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.