Fehler bei der Implementierung des IIR-Filters auf dem FPGA

Ich möchte mehrere IIR-Filter auf einem FPGA mit VHDL implementieren. Der Filter ist für Audio. Ich beginne mit der Implementierung eines einzelnen Filters mit der folgenden Übertragungsfunktion:

H 1 ( z ) = 304 304 z 2 16384 32109 z 1 + 16076 z 2

Diese Übertragungsfunktion soll folgenden Frequenzgang haben:

Geben Sie hier die Bildbeschreibung ein

Und sollte mit folgender Differenzengleichung realisierbar sein:

16384 j [ N ] = 304 X [ N ] 304 X [ N 2 ] + 32109 j [ N 1 ] 16076 j [ N 2 ]

Ich habe versucht, den Filter mit den beiden folgenden Codeteilen zu implementieren:

library ieee;
use ieee.std_logic_1164.all;
use IEEE.numeric_std.all;

entity FILTER is
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic; --50 MHz clock
    CLK                     : in  std_logic;   --3.072 MHz clock
    CHANNEL                 : in std_logic;    --Channel select 48 kHz
    data_left_in            : in std_logic_vector(15 downto 0);
    data_right_in           : in std_logic_vector(15 downto 0);
    data_left_out           : out std_logic_vector(15 downto 0);
    data_right_out          : out std_logic_vector(15 downto 0)
    );  
end FILTER;

architecture behave of FILTER is
    signal input_left       : signed (15 downto 0);
    signal input_right      : signed (15 downto 0);
    signal output_left      : signed (15 downto 0);
    signal output_right     : signed (15 downto 0);

    signal i_0_left : signed (15 downto 0);
    signal i_0_right : signed (15 downto 0);

    COMPONENT filter_class
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic;   --50 MHz clock
    CLK                     : in  std_logic;     --Channel select 48 kHz
    sample                  : in signed (15 downto 0); --filter input
    sample_filtered         : inout signed (15 downto 0); --filter output

    b00                     : in integer range -32768 to 32767; --filter coefficients
    b01                     : in integer range -32768 to 32767;
    b02                     : in integer range -32768 to 32767;
    a01                     : in integer range -32768 to 32767;
    a02                     : in integer range -32768 to 32767;

    scaling                 : in integer range 0 to 16; --scaling for fixed point

    gain                    : in integer range -32768 to 32767;
    gain_scaling            : in integer range 0 to 15
    );   
    END COMPONENT;

    begin

    Filt_0_r        : filter_class  PORT MAP (GPIO(35 downto 0), CLK_50, NOT CHANNEL, input_right, i_0_right , 16384, 0, 0, 0, 0, 14, 1, 0); --no filtering
    Filt_0_l        : filter_class  PORT MAP (GPIO(35 downto 0), CLK_50, CHANNEL, input_left, i_0_left, 16384, 0, -16384, -32113, 16081, 14, 1, 0); --filter with tf H1(z)

    process (CHANNEL) --send output to DAC
    begin
        if RISING_EDGE(CHANNEL) then
            input_left <= signed(data_left_in);
            data_left_out <= std_logic_vector(i_0_left);
        end if;
        if FALLING_EDGE(CHANNEL) then
            input_right <= signed(data_right_in);
            data_right_out <= std_logic_vector(i_0_right);
        end if;
    end process;
end behave;

Und

    library ieee;
use ieee.std_logic_1164.all;
use IEEE.numeric_std.all;

entity filter_class is
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic;   --50 MHz clock
    CLK                     : in  std_logic;     --Channel select 48 kHz
    sample                  : in signed (15 downto 0); --filter input
    sample_filtered         : inout signed (15 downto 0); --filter output

    b00                     : in integer range -32768 to 32767; --filter coefficients
    b01                     : in integer range -32768 to 32767;
    b02                     : in integer range -32768 to 32767;
    a01                     : in integer range -32768 to 32767;
    a02                     : in integer range -32768 to 32767;

    scaling                 : in integer range 0 to 16; --scaling for fixed point

    gain                    : in integer range -32768 to 32767;
    gain_scaling            : in integer range 0 to 15
    );     
end filter_class;

architecture behave of filter_class is

    TYPE multipliers IS ARRAY (NATURAL RANGE <>) OF SIGNED (17 DOWNTO 0);
    TYPE result IS ARRAY (NATURAL RANGE <>) OF SIGNED (35 DOWNTO 0);

    signal y00 : signed (15 downto 0);

    signal sum_1 : signed (37 downto 0);

    signal samp : multipliers(0 to 5);
    signal coef : multipliers(0 to 5);
    signal resu : result(0 to 5);

    signal channel_state : std_logic;

begin

    process (CLK_50) --calculate filter 
        variable cnt : integer := 0;
        variable flag : std_logic := '0';
    begin                   
        if RISING_EDGE(CLK_50) then 

            channel_state <= CLK; 

            if channel_state = '0' AND CLK = '1' then --if new sample

                flag := '1';

            elsif flag = '1' then --calculate

                cnt := cnt + 1;

                if cnt = 4 then --save coefficients in array

                    coef(0) <= to_signed(b00,18);
                    coef(1) <= to_signed(b01,18);
                    coef(2) <= to_signed(b02,18);
                    coef(3) <= to_signed(-a01,18);
                    coef(4) <= to_signed(-a02,18);

                    coef(5) <= to_signed(gain,18);

                    samp(5) <= resize(y00, 18);

                end if;

                if cnt = 29 then --reset count if all done
                    cnt := 0;
                    flag := '0';
                elsif cnt > 5 AND cnt < 12 then
                    resu(cnt - 6) <= coef(cnt - 6) * samp(cnt - 6); --multiply coefficients and sample, and gain
                elsif cnt = 12 then
                    sum_1 <= to_signed(0, 38); --reset filter sum
                elsif cnt > 12 then
                    sum_1 <= sum_1 + resu(cnt - 8); --calculate sum
                end if;
            end if;
        end if;
    end process;

    process (CLK)
        variable y00_temp_1 : signed (37 downto 0);
        variable y00_temp_2 : signed (37 downto 0);

        variable sample_filtered_temp_1 : signed (35 downto 0);
    begin
        if RISING_EDGE(CLK) then
            --delay line
            samp(2) <= samp(1); 
            samp(1) <= samp(0);
            samp(0) <= resize(sample, 18);

            samp(4) <= samp(3);
            samp(3) <= resize(y00, 18);
            y00_temp_1 := sum_1; --set output
            y00_temp_2 := shift_right(y00_temp_1, scaling); --divide by 2^14 for scaling
            y00 <= y00_temp_2 (15 downto 0); --filter output

            sample_filtered_temp_1 := shift_right(resu(5), gain_scaling); filter gain scaling
            sample_filtered <= sample_filtered_temp_1 (15 downto 0); --filter output * gain
        end if; 
    end process;
end behave;

Dies ist mein erster VHDL-Code, und es können viele Fehler auftreten.

Mit diesem Code funktioniert der Filter nicht.

Der rechte Kanalfilter funktioniert (nur Passthrough) und gibt 1/1 Ausgabe.

Der linke Filter funktioniert nicht und gibt unabhängig vom Eingangssignal nur Rauschen aus. Bei einem Inout-Signal von 1 kHz und 1 V Amplitude ist das Ausgangssignal zu sehen:

Geben Sie hier die Bildbeschreibung ein

Die rote Wellenform ist der Ausgang des rechten Filters, die blaue ist der linke Filter.

Bei Verwendung eines niedrigeren Skalierungsfaktors, z. 2^15 oder 2^16, sieht die Ausgabe anders aus. Bei 2 ^ 16 ist die Ausgabe 0. Aus diesem Grund vermute ich, dass das Problem eine Art falsches Abschneiden des Signals ist.

Hat jemand eine Idee, was ich mit dem Filter falsch mache?

Zeichnen Sie ein paar tausend Punkte in Excel und sehen Sie, was passiert. Wenn es gleich aussieht, können die Gleichungen als fehlerhaft angesehen werden.
@Andyaka - Ich habe das gerade gemacht, nicht in Excel, sondern in MATLAB, aber ich denke, das sollte dasselbe sein. Die Vaweform ist in den beiden ersten Perioden etwas gestört, aber danach sieht es gut aus. Der Frequenzgang sieht auch richtig aus.
Ja, das ist gut - zumindest weißt du, dass die Formel gut ist. Sie haben das Problem halbiert.

Antworten (2)

Ich verfolge nicht alles, was Sie tun, aber dieser Code sieht verdächtig aus:

            if cnt = 29 then --reset count if all done
              -- ...
            elsif cnt > 12 then
                sum_1 <= sum_1 + resu(cnt - 8); --calculate sum
            end if;

Dieser Zweig wird für Werte von 13 bis 28 ausgeführt cntund generiert Indizes für das resuArray von 5 bis 20, aber das Array hat nur Elemente für die Indizes 0 bis 5.

Es scheint mir, dass Sie wirklich möchten, dass das Zurücksetzen erfolgt, wenn cnt = 19und die Indizes sein sollten resu(cnt - 13).

Ich stimme zu, dass es verdächtig aussieht. Ich glaube, ich könnte etwas geändert und diesen Teil vergessen haben. Allerdings habe ich es geändert, und das Problem ist immer noch da.

Auch ich habe Probleme, dem zu folgen, aber ein Vorschlag wäre, die Koeffizienten auf einige einfache Werte einzustellen und das Design zu simulieren, indem die mathematischen Ergebnisse untersucht werden. Arbeiten Sie sich dann bis zu den komplexeren Koeffizienten vor. Ohne eine vollständige numerische Analyse der Simulation kann man nicht wirklich sagen, was schief läuft.