Error al implementar el filtro IIR en FPGA

0

Quiero implementar varios filtros IIR en un FPGA, usando VHDL. Los filtros son para audio. Empiezo implementando un único filtro con la siguiente función de transferencia:

$$ H_1 (z) = \ frac {304 -304z ^ {- 2}} {16384 - 32109z ^ {- 1} + 16076z ^ {- 2}} $ $

Esta función de transferencia debe tener la siguiente respuesta de frecuencia:

Ydeberíaserposibleimplementarloconlasiguienteecuacióndediferencia:

$$ 16384 \ cdot y [n] = 304 \ cdot x [n] -304 \ cdot x [n-2] +32109 \ cdot y [n-1] - 16076 \ cdot y [n-2] $$

He intentado implementar el filtro con las dos piezas de código siguientes:

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;

y

    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;

Este es mi primer código VHDL, y los errores pueden ser muchos.

Con este código el filtro no funciona.

El filtro de canal derecho funciona (solo pasa) y da una salida de 1/1.

El filtro izquierdo no funciona y solo emite ruido, independientemente de la señal de entrada. Con una señal de entrada de 1 kHz y una amplitud de 1 V, se puede ver la señal de salida:

La forma de onda roja es la salida del filtro derecho, el azul es el filtro izquierdo.

Si usa un factor de escala más bajo, ej. 2 ^ 15 o 2 ^ 16, la salida se verá diferente. En 2 ^ 16, la salida será 0. Debido a esto, sospecho que el problema es algún tipo de truncamiento incorrecto de la señal.

¿Alguien tiene una idea de lo que estoy haciendo mal con el filtro?

    
pregunta keffe

2 respuestas

2

No estoy siguiendo todo lo que estás haciendo, pero este fragmento de código parece sospechoso:

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

Esta rama se ejecuta para valores de cnt de 13 a 28, generando índices para la matriz resu de 5 a 20, pero la matriz solo tiene elementos para los índices de 0 a 5.

Me parece que realmente quieres que el reinicio se produzca cuando cnt = 19 y los índices deberían ser resu(cnt - 13) .

    
respondido por el Dave Tweed
0

Yo también tengo problemas para seguir esto, pero una sugerencia sería establecer los coeficientes en algunos valores simples y simular el diseño que examina los resultados matemáticos. Luego, continúa tu camino hasta los coeficientes más complejos. Sin un análisis numérico completo de la simulación, realmente no se puede decir qué está mal.

    
respondido por el Ray Haynes

Lea otras preguntas en las etiquetas