¿Ejemplo de código para los filtros FIR / IIR en VHDL?

11

Estoy tratando de comenzar con DSP en mi tablero Spartan-3. Hice una placa AC97 con un chip de una placa base antigua, y hasta ahora lo hice para hacer ADC, multiplicar las muestras por un número < 1 (disminuir el volumen) y luego DAC.

Ahora me gustaría hacer algunas cosas DSP básicas, como un filtro de paso bajo, paso alto, etc. Pero estoy realmente confundido acerca de la representación numérica (¿enteros? ¿punto fijo? ¿Q0.15? ¿Desbordamiento o saturación? ).

Solo quiero un código de ejemplo de un filtro simple actual para que empiece. No es de alta eficiencia, rápido, ni nada de eso. Solo el filtro teórico implementado en VHDL.

He estado buscando, pero simplemente encuentro fórmulas teóricas: entiendo, lo que no entiendo es cómo procesar las muestras de audio firmadas de 16 bits y 48 KHz que recibo del ADC. He estado usando estas bibliotecas: enlace . Si multiplico mis muestras por 0.5, 0.25, etc., puedo escuchar la diferencia. Pero un filtro más grande solo me da ruido.

Gracias.

    
pregunta hjf

6 respuestas

8

Parece que primero necesitas resolver los aspectos de DSP y luego hacer una implementación en FPGA.

  • Ordene el DSP en C, Matlab, Excel o en cualquier otro lugar
  • Pruebe y piense cómo transferirá lo que aprendió de eso a FPGA-land
  • Descubra que ha hecho algunas suposiciones acerca de la implementación que no funciona bien (como el uso de punto flotante, por ejemplo)
  • Regresa y actualiza tus cosas DSP sin conexión para tener esto en cuenta.
  • Iterar n veces :)

Con respecto a los tipos de datos, puede usar los números enteros muy bien.

Aquí hay un código de ejemplo para que te pongas en marcha. Tenga en cuenta que falta muchos problemas del mundo real (por ejemplo, reinicio, gestión de desbordamiento), pero espero que sea instructivo:

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;
    
respondido por el Martin Thompson
5

El filtro FIR de paso bajo más simple que puede probar es y (n) = x (n) + x (n-1). Puedes implementar esto fácilmente en VHDL. A continuación se muestra un diagrama de bloques muy simple del hardware que desea implementar.

De acuerdo con la fórmula, necesita las muestras de ADC actuales y anteriores para obtener el resultado adecuado. Lo que debe hacer es bloquear las muestras de ADC entrantes en el flanco descendente del reloj y realizar los cálculos adecuados en el flanco ascendente para obtener la salida adecuada. Dado que está agregando dos valores de 16 bits juntos, es posible que termine con una respuesta de 17 bits. Debe almacenar la entrada en registros de 17 bits y usar un sumador de 17 bits. Tu salida, sin embargo, serán los 16 bits más bajos de la respuesta. El código puede tener un aspecto similar a este, pero no puedo garantizar que funcionará completamente ya que no lo he probado, y mucho menos sintetizado.

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

Como puede ver, puede usar esta idea general para agregar fórmulas más complicadas, como las que tienen coeficientes. Las fórmulas más complicadas, como los filtros IIR, pueden requerir el uso de variables para corregir la lógica del algoritmo. Finalmente, una forma fácil de evitar los filtros que tienen números reales como coeficientes es encontrar un factor de escala para que todos los números terminen lo más cerca posible de los números enteros. Tu resultado final tendrá que reducirse de nuevo por el mismo factor para obtener el resultado correcto.

Espero que esto te sea de utilidad y te ayude a poner la bola en marcha.

* Esto se ha editado para que el enclavamiento de datos y el enclavamiento de salida estén en procesos separados. También utilizando tipos firmados en lugar de std_logic_vector. Supongo que su entrada de ADC será una señal std_logic_vector.

    
respondido por el dhsieh2
5

Otro fragmento de código simple (solo las entrañas). Tenga en cuenta que no escribí el VHDL directamente, usé MyHDL para generar el VHDL.

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

Estaesunaimplementacióndirecta.Requerirámultiplicadores.losLasíntesisdeestecircuito,dirigidaaunAlteraCycloneIII,noseusócualquiermultiplicadorexplícitoperorequiere350elementoslógicos.

EsteesunfiltroFIRpequeñoytendrálasiguienterespuesta(notanbuena)perodeberíaserútilcomoejemplo.

Además, tengo un par de ejemplos, aquí y here , eso podría ser útil.

Además, su pregunta parece preguntar: "¿cuál es el punto fijo apropiado? ¿Representación? "Con frecuencia al implementar funciones DSP, punto fijo Se usa la representación, porque simplifica el análisis de los filtros. Como mencionado, el punto fijo es arthimetic entero. La implementación actual es simplemente trabajar con enteros, pero nuestra representación previa es fraccionada.
Los problemas suelen surgir al convertir desde un entero de implementación (punto fijo) a / para el diseño de punto flotante.

No sé qué tan bien están los tipos de punto fijo y flotante VHDL soportado. Trabajarán bien en la simulación, pero no sé si lo harán. sintetizar con la mayoría de las herramientas de síntesis. Creé un separado question para esto.

    
respondido por el Christopher Felton
3

OpenCores tiene varios ejemplos de DSP, IIR y FIR, incluyendo BiQuad. Tendrás que registrarte para descargar los archivos.

editar
Entiendo el comentario de Kortuk sobre los enlaces muertos, y de hecho, si el enlace a OpenCores muere, la respuesta será inútil. Estoy bastante seguro de que esto no sucederá; mi enlace es genérico y solo morirá si desaparece el dominio completo de OpenCores.
Traté de buscar algunos ejemplos que podría usar para esta respuesta, pero son demasiado largos para ser representados aquí. Así que seguiré mi consejo para registrarme en el sitio (tuve que mudarme a Nueva York, porque no aceptaron a mi ciudad natal) y echar un vistazo al código presentado allí.

    
respondido por el stevenvh
1

He intentado implementar scripts para la implementación automática de filtros IIR, donde puede definir si el diseño debería ser lo más rápido posible (por lo que cada multiplicación se realiza con un multiplicador dedicado) o lo más pequeño posible (por lo que cada multiplicador se reutiliza). ).

Las fuentes se han publicado en alt.sources como "Implementación de comportamiento pero sintetizable de filtros IIR en VHDL" (también puede encontrarlo en el archivo de Google: enlace )

Las publicaciones en alt.sources están en formato "shar", por lo que debe guardar el mensaje como texto y desactivarlo (con la utilidad "Unshar") para obtener las fuentes.

    
respondido por el WZab
0

¿Qué tal esto? enlace

Implementa un filtro IIR basado en biquad (SOS, secciones de segundo orden) que se encarga de la implementación de punto fijo. También cuenta con scripts de Python para el diseño y verificación del filtro. No utiliza construcciones de FPGA específicas del proveedor y puede elegir la compensación entre el uso de alta velocidad y el de área baja.

    
respondido por el KeyNuts

Lea otras preguntas en las etiquetas

Comentarios Recientes

Debido a que utilizaremos mi nombre y el del foro ILCA, escribí un pequeño HTML en las palabras a continuación y lo incluí en el contenido de mis pruebas. Ahora solo tienes que permitir Yo, mi profesional, que debería estar vinculado al proyecto VHDL. Así es como funciona: por favor, hágame saber todos sus comentarios. Debería haber traído tus capturas de pantalla personales de tus proyectos contigo. Estas son mis imágenes. Esto va con cada comentario, pero solo puede imprimir las imágenes que vinieron después... Lees verder