Daha önce buradaki yazımda FIR filtre katsayılarının nasıl hesaplandığı ile ilgili bir yazı yazmıştım. Bu yazımda ise FPGA üzerinde üzerinde uygulamasını göstereceğim.
Tasarımı 3 parçaya bölebiliriz:
İlk adımda giriş verileri shift register mantığında flip-floplar üzerinde ilerler. Aynı zamanda, aynı katsayı ile çarpılacak değerler kendi aralarında toplanarak bir sonraki adıma gönderilir.
İkinci adımda değerler önceden hesaplanmış katsayılar ile çarpılır ve bir sonraki adıma gönderilir.
Üçüncü adımda ise bütün değerler toplanarak çıkışa aktarılır. Toplama işlemi yapılırken çalışma frekansını arttırmak için pipeline yapı kullanılmalıdır.
VHDL kodu:
---------------------- FIRFilter.vhd --------------------------- -- Bertan Taşkın -- 27.7.2017 -- -- Tip 1 FIR Filtre. Kesim frekansları, filtre derecesi, filtre -- tipi gibi parametreler generic kısmından değiştirilebilir. -- Bütün işlemler pipeline tabanlı yapılmıştır. -- -- SamplingFrequency : Örnekleme frekansı (Hz cinsinden) -- -- Frequency1 : Alçak ve yüksek geçiren filtre için kesim frekansı, -- bant geçiren ve bant durduran fitre için de 1. kesim frekansı. -- (Hz cinsinden) -- -- Frequency2 : Bant geçiren ve bant durduran filtre için 2. kesim -- frekansı. (Hz cinsinden) -- -- FilterOrder : Filtre derecesi. Tip 1 filtre olduğundan dolayı -- çift sayı olmalıdır. -- -- FilterType : Filtre tipi. Lowpass, Highpass, Bandpass ya da -- Bandstop olabilir. -- -- WindowType : Pencere fonksiyonu. Rectangular, Bartlett, Blackman, -- Hanning ya da Hamming olabilir. -- -- CoefficientWidth : Katsayı Genişliği. İşlemlerin hassasiyetini -- ayarlar. -- -- SignalWidth : Giriş ve çıkış sinyallerinin genişliği. -- -- Grup Gecikmesi = FilterOrder/2+ceil(log2(FilterOrder/2+1))+2 -- -- Grup Gecikmesi otomatik olarak hesaplanıp, not olarak konsola -- yazdırılır. -- -- --Kütüphaneler library IEEE; use IEEE.STD_LOGIC_1164.ALL; USE ieee.numeric_std.ALL; use ieee.math_real.all; entity FIRFilter is generic(SamplingFrequency : real := 50000000.0; --Örnekleme Frekansı Frequency1 : real := 10000000.0; --1.Kesim Frekansı Frequency2 : real := 20000000.0; --2.Kesim Frekansı FilterOrder : natural := 80; --Filtre Derecesi FilterType : string := "Lowpass"; --Filtre Tipi WindowType : string := "Hamming"; --Pencere Tipi CoefficientWidth : natural := 16; --Katsayı Genişliği SignalWidth : natural := 8); --Sinyal Genişliği port(Clk, Enable : in std_logic; --Clock ve Enable Girişleri SignalIn : in signed(SignalWidth - 1 downto 0); --Sinyal Girişi SignalOut : out signed(SignalWidth - 1 downto 0)); --Sinyal Çıkışı end FIRFilter; architecture Behavioral of FIRFilter is --Toplama işleminin kaç adımda gerçekleşeceğini hesaplamada --kullanılan yararlı fonksiyon --logorder(x) = ceil(log2(x)) + 1 function logorder(order : integer) return integer is variable x : integer := order; variable y : integer := 0; begin while x > 1 loop x := x / 2 + x mod 2; y := y + 1; end loop; return(y + 1); end logorder; --Katsayıların hesaplandığı fonksiyon function Coefficient(x : integer) return signed is --Kesim frekanslarının örnekleme frekansı ile normallize edilmesi constant wc1 : real := 2.0 * MATH_PI * Frequency1 / SamplingFrequency; constant wc2 : real := 2.0 * MATH_PI * Frequency2 / SamplingFrequency; constant i : real := real(x - FilterOrder / 2); variable sincx : real := 0.0; variable coeff : real := 0.0; variable coeffint : integer := 0; begin ------- Filtre Katsatılarının Hesaplanması --------- --Alçak geçiren filtre --h(t) = sin(w*t)/(pi*t) if FilterType = "Lowpass" then if i /= 0.0 then coeff := sin(wc1 * i) / (MATH_PI * i); else coeff := wc1 / MATH_PI; end if; --Yüksek geçiren filtre --h(t) = [sin(pi*t) - sin(w*t)]/(pi*t) elsif FilterType = "Highpass" then if i /= 0.0 then coeff := -sin(wc1 * i) / (MATH_PI * i); else coeff := 1.0 - wc1 / MATH_PI; end if; --Bant geçiren filtre --h(t) = [sin(w2*t)- sin(w1*t)]/(pi*t) elsif FilterType = "Bandpass" then if i /= 0.0 then coeff := (sin(wc2 * i) - sin(wc1 * i)) / (MATH_PI * i); else coeff := (wc2 - wc1) / MATH_PI; end if; --Bant durduran filtre --h(t) = [sin(pi*t) - sin(w2*t) + sin(w1*t)]/(pi*t) elsif FilterType = "Bandstop" then if i /= 0.0 then coeff := (sin(wc1 * i) - sin(wc2 * i)) / (MATH_PI * i); else coeff := 1.0 + (wc1 - wc2) / MATH_PI; end if; --Geçersiz filtre tipi else report "Invalid Filter Type" severity error; end if; ------------- Pencere Fonksiyonları ----------------- --Rectangular Pencere --w(n) = 1 if WindowType = "Rectangular" then coeff := coeff * 1.0; --Bartlett Pencere --w(n) = 1 - 2 * |x - N/2| / N elsif WindowType = "Bartlett" then coeff := coeff * (1.0 - 2.0 * real(abs(x - FilterOrder / 2)) / real(FilterOrder)); --Blackman Pencere --w(n) = 0.42 - 0.5 * cos(2* pi * x / N) + 0.08 * cos(2* pi * x / N) elsif WindowType = "Blackman" then coeff := coeff * (0.42 - 0.5 * cos(2.0 * MATH_PI * real(x) / real(FilterOrder)) + 0.08 * cos(4.0 * MATH_PI * real(x) / real(FilterOrder))); --Hanning Pencere --w(n) = 0.5 - 0.5 * cos(2 * pi * x / N) elsif WindowType = "Hanning" then coeff := coeff * (0.5 - 0.5 * cos(2.0 * MATH_PI * real(x) / real(FilterOrder))); --Hamming Pencere --w(n) = 0.54 - 0.46 * cos(2* pi * x / N) elsif WindowType = "Hamming" then coeff := coeff * (0.54 - 0.46 * cos(2.0 * MATH_PI * real(x) / real(FilterOrder))); --Geçersiz pencere fonksiyonu else report "Invalid Window Type" severity error; end if; ----- Katsayının floating point'den fixed point'e dönüştürülmesi ----- --Integer'a dönüştürme coeffint := integer(coeff * real(2**(CoefficientWidth - 1))); --Signed'a dönüştürme return(to_signed(coeffint, CoefficientWidth)); end Coefficient; --Toplama işleminde kullanılan dizinin boyutu constant SumArraySize : integer := logorder(FilterOrder / 2 + 1) - 1; type SignedTable is array(FilterOrder downto 0) of signed(SignalWidth - 1 downto 0); type SignedTable1 is array(FilterOrder / 2 downto 0) of signed(SignalWidth downto 0); type SignedTable2 is array(FilterOrder downto 0) of signed(SignalWidth + CoefficientWidth + logorder(FilterOrder) - 1 downto 0); type SignedTable3 is array(SumArraySize downto 0) of SignedTable2; signal Data : SignedTable := (others=>(others=>'0')); signal DataSum : SignedTable1 := (others=>(others=>'0')); signal DataSumArray : SignedTable3 := (others=>(others=>(others=>'0'))); begin --Eğer filtre derecesi tek ise hata ver assert FilterOrder mod 2 = 0 report "FilterOrder must be even number" severity error; process(Clk, Enable) variable ArraySize : integer := 0; begin --Grup gecikmesi not olarak konsola yazdırılıyor report "Group Delay = " & integer'image(FilterOrder / 2 + logorder(FilterOrder / 2 + 1) + 1) severity note; --Yükselen kenar ile işlemler tetiklenir if rising_edge(Clk) and Enable = '1' then --------------------- Verilerin Kaydırılması ------------------ for i in 0 to FilterOrder - 1 loop Data(FilterOrder - i) <= Data(FilterOrder - i - 1); end loop; --Yeni veri diziye aktarılır Data(0) <= SignalIn; --Daha az çarpma işlemi yapmak için aynı sayı ile çarpılacak --verileri topla for i in 0 to FilterOrder / 2 loop if i /= FilterOrder / 2 then DataSum(i) <= resize(Data(i), SignalWidth + 1) + resize(Data(FilterOrder - i), SignalWidth + 1); else DataSum(i) <= resize(Data(i), SignalWidth + 1); end if; end loop; --------------- Verilerin Katsayılar ile Çarpılması ----------------- for i in 0 to FilterOrder / 2 loop DataSumArray(0)(i) <= resize(DataSum(i) * Coefficient(i), SignalWidth + CoefficientWidth + logorder(FilterOrder)); end loop; --------------------- Sonuçların toplanması ---------------------- --Dizideki eleman sayısı ArraySize := FilterOrder / 2 + 1; for i in 1 to SumArraySize loop for j in 0 to ArraySize / 2 + ArraySize mod 2 - 1 loop --Dizideki elemanlar 2'şer 2'şer toplanarak bir sonraki --diziye aktarılır. Dizinin eleman sayısı tek ise son --eleman bir sonraki diziye direkt aktarılır. if 2 * j + 1 > ArraySize - 1 then --Tek kalan eleman DataSumArray(i)(j) <= DataSumArray(i - 1)(2 * j); else DataSumArray(i)(j) <= DataSumArray(i - 1)(2 * j) + DataSumArray(i - 1)(2 * j + 1); end if; end loop; --Bir sonraki dizideki eleman sayısı ArraySize := ArraySize / 2 + ArraySize mod 2; end loop; --Hesaplanan sonucun kırpılarak çıkışa aktarılması if DataSumArray(SumArraySize)(0) > 2**(SignalWidth + CoefficientWidth - 2) - 1 then SignalOut <= to_signed(2**(SignalWidth - 1) - 1, SignalWidth); elsif DataSumArray(SumArraySize)(0) < -2**(SignalWidth + CoefficientWidth - 2) + 1 then SignalOut <= to_signed(-2**(SignalWidth - 1), SignalWidth); else SignalOut <= DataSumArray(SumArraySize)(0)(SignalWidth + CoefficientWidth - 2 downto CoefficientWidth - 1); end if; end if; end process; end Behavioral;
Kaynak kullanımı:
Quartus II tarafından oluşturulan RTL şeması:
EP4CE22F17C6 (Cyclone IV, -6 Speed Grade) için FMax Değerleri: