Quiero implementar varios filtros IIR en un FPGA, usando VHDL. Los filtros son para audio. Comienzo implementando un solo filtro con la siguiente función de transferencia:
Esta función de transferencia debe tener la siguiente respuesta de frecuencia:
Y debería ser posible implementar con la siguiente ecuación en diferencias:
He intentado implementar el filtro con las dos siguientes piezas de código:
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 proporciona una salida de 1/1.
El filtro izquierdo no funciona y solo da ruido, independientemente de la señal de entrada. Con una señal de entrada y salida de 1 kHz y una amplitud de 1 V, la señal de salida se puede ver:
La forma de onda roja es la salida del filtro derecho, la 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?
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 cnt
valores del 13 al 28, generando índices para la resu
matriz de 5 a 20, pero la matriz solo tiene elementos para los índices de 0 a 5.
Me parece que realmente desea que el reinicio ocurra cuando cnt = 19
y los índices deberían ser resu(cnt - 13)
.
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 examinando los resultados matemáticos. Luego avance hasta los coeficientes más complejos. Sin un análisis numérico completo de la simulación, no se puede decir realmente qué está fallando.
Andy alias
café
Andy alias