Filtro Verilog FIR usando FPGA

Estoy implementando un filtro FIR en Verilog, usando la placa DE2. Por alguna razón, la salida de los parlantes está llena de estática, aunque parece filtrar algunas frecuencias. Aquí está el código para el FIR:

// Local wires.
wire read_ready, write_ready, read, write;
wire [23:0] readdata_left, readdata_right;
wire [23:0] writedata_left, writedata_right;

assign writedata_left = output_sample;

assign writedata_right = output_sample;
assign read = 1;
assign write = 1;

wire [23:0] input_sample = readdata_left;

reg [23:0] output_sample;

La muestra de entrada se pasa a través del FIR y la muestra de salida se pasa a los altavoces izquierdo y derecho para simplificar.

//The FIR filter
parameter N = 40;
reg signed[23:0] coeffs[39:0];
reg [23:0] holderBefore[39:0];

wire [23:0] toAdd[39:0];

// -- 1000-1100
always @(*)
begin
    coeffs[0]=24'b100000000110101001111110; // -- 1
    coeffs[1]=24'b100000000110100011011011; // -- 2
    coeffs[2]=24'b100000000111000100001100; // -- 3
    coeffs[3]=24'b100000000111111000101000;// -- 4
    coeffs[4]=24'b100000001000011111111100;// -- 5
    coeffs[5]=24'b100000001000011001011001;// -- 6
    coeffs[6]=24'b100000000111010001010011;// -- 7
    coeffs[7]=24'b100000000100100110111010;// -- 8
    coeffs[8]=24'b100000000000011010001101;// -- 9
    coeffs[9]=24'b000000000101101111000000;// -- 10
    coeffs[10]=24'b000000001101100001000100;// -- 11
    coeffs[11]=24'b000000010110111100000000;// -- 12
    coeffs[12]=24'b000000100001011111000001;// -- 13
    coeffs[13]=24'b000000101100101001010111;// -- 14
    coeffs[14]=24'b000000111000000000110100;// -- 15
    coeffs[15]=24'b000001000010101010011001;// -- 16
    coeffs[16]=24'b000001001100001011111000;// -- 17
    coeffs[17]=24'b000001010011111101111100;// -- 18
    coeffs[18]=24'b000001011001011001010010;// -- 19
    coeffs[19]=24'b000001011100010000110010;// -- 20
    coeffs[20]=24'b000001011100010000110010;// -- 20
    coeffs[21]=24'b000001011001011001010010;// -- 19
    coeffs[22]=24'b000001001100001011111000;// -- 18
    coeffs[23]=24'b000001001100001011111000;// -- 17
    coeffs[24]=24'b000001000010101010011001;// -- 16
    coeffs[25]=24'b000000111000000000110100;// -- 15
    coeffs[26]=24'b000000101100101001010111;// -- 14
    coeffs[27]=24'b000000100001011111000001;// -- 13
    coeffs[28]=24'b000000010110111100000000;// -- 12
    coeffs[29]=24'b000000001101100001000100;// -- 11
    coeffs[30]=24'b000000000101101111000000;// -- 10
    coeffs[31]=24'b100000000000011010001101;// -- 9
    coeffs[32]=24'b100000000100100110111010;// -- 8
    coeffs[33]=24'b100000000111010001010011;// -- 7
    coeffs[34]=24'b100000001000011001011001;// -- 6
    coeffs[35]=24'b100000001000011111111100;// -- 5
    coeffs[36]=24'b100000000111111000101000;// -- 4
    coeffs[37]=24'b100000000111000100001100;// -- 3
    coeffs[38]=24'b100000000110100011011011;// -- 2
    coeffs[39]=24'b100000000110101001111110;// -- 1
end

genvar i;

generate
for (i=0; i<N; i=i+1)
    begin: mult
        multiplier mult1(
          .dataa(coeffs[i]),
          .datab(holderBefore[i]),
          .out(toAdd[i]));
    end
endgenerate

always @(posedge CLOCK_50 or posedge reset)
begin
    if(reset)
        begin
            holderBefore[39]     <= 0;
            holderBefore[38]     <= 0;
            holderBefore[37]     <= 0;
            holderBefore[36]     <= 0;
            holderBefore[35]     <= 0;
            holderBefore[34]     <= 0;
            holderBefore[33]     <= 0;
            holderBefore[32]     <= 0;
            holderBefore[31]     <= 0;
            holderBefore[30]     <= 0;
            holderBefore[29]     <= 0;
            holderBefore[28]     <= 0;
            holderBefore[27]     <= 0;
            holderBefore[26]     <= 0;
            holderBefore[25]     <= 0;
            holderBefore[24]     <= 0;
            holderBefore[23]     <= 0;
            holderBefore[22]     <= 0;
            holderBefore[21]     <= 0;
            holderBefore[20]     <= 0;
            holderBefore[19]     <= 0;
            holderBefore[18]     <= 0;
            holderBefore[17]     <= 0;
            holderBefore[16]     <= 0;
            holderBefore[15]     <= 0;
            holderBefore[14]     <= 0;
            holderBefore[13]     <= 0;
            holderBefore[12]     <= 0;
            holderBefore[11]     <= 0;
            holderBefore[10]     <= 0;
            holderBefore[9]      <= 0;
            holderBefore[8]      <= 0;
            holderBefore[7]      <= 0;
            holderBefore[6]      <= 0;
            holderBefore[5]      <= 0;
            holderBefore[4]      <= 0;
            holderBefore[3]      <= 0;
            holderBefore[2]      <= 0;
            holderBefore[1]      <= 0;
            holderBefore[0]      <= 0;
            output_sample        <= 0;
        end
    else
        begin
            holderBefore[39]     <= holderBefore[38];
            holderBefore[38]     <= holderBefore[37];
            holderBefore[37]     <= holderBefore[36];
            holderBefore[36]     <= holderBefore[35];
            holderBefore[35]     <= holderBefore[34];
            holderBefore[34]     <= holderBefore[33];
            holderBefore[33]     <= holderBefore[32];
            holderBefore[32]     <= holderBefore[31];
            holderBefore[31]     <= holderBefore[30];
            holderBefore[30]     <= holderBefore[29];
            holderBefore[29]     <= holderBefore[28];
            holderBefore[28]     <= holderBefore[27];
            holderBefore[27]     <= holderBefore[26];
            holderBefore[26]     <= holderBefore[25];
            holderBefore[25]     <= holderBefore[24];
            holderBefore[24]     <= holderBefore[23];
            holderBefore[23]     <= holderBefore[22];
            holderBefore[22]     <= holderBefore[21];
            holderBefore[21]     <= holderBefore[20];
            holderBefore[20]     <= holderBefore[19];
            holderBefore[19]     <= holderBefore[18];
            holderBefore[18]     <= holderBefore[17];
            holderBefore[17]     <= holderBefore[16];
            holderBefore[16]     <= holderBefore[15];
            holderBefore[15]     <= holderBefore[14];
            holderBefore[14]     <= holderBefore[13];
            holderBefore[13]     <= holderBefore[12];
            holderBefore[12]     <= holderBefore[11];
            holderBefore[11]     <= holderBefore[10];
            holderBefore[10]     <= holderBefore[9];
            holderBefore[9]      <= holderBefore[8];
            holderBefore[8]      <= holderBefore[7];
            holderBefore[7]      <= holderBefore[6];
            holderBefore[6]      <= holderBefore[5];
            holderBefore[5]      <= holderBefore[4];
            holderBefore[4]      <= holderBefore[3];
            holderBefore[3]      <= holderBefore[2];
            holderBefore[2]      <= holderBefore[1];
            holderBefore[1]      <= holderBefore[0];
            holderBefore[0]      <= input_sample;
            output_sample <= (input_sample + toAdd[0] + toAdd[1] + 
                              toAdd[2] + toAdd[3] + toAdd[4] + toAdd[5] +
                              toAdd[6] + toAdd[7] + toAdd[8] + toAdd[9] + 
                              toAdd[10] + toAdd[11] + toAdd[12]+ toAdd[13] + toAdd[14] + 
                              toAdd[15] + toAdd[16] + toAdd[17] + toAdd[18] +
                              toAdd[19] + toAdd[20] + toAdd[21] + toAdd[22] + 
                              toAdd[23] + toAdd[24] + toAdd[25] +toAdd[26] + toAdd[27] + toAdd[28] + toAdd[29] +
                              toAdd[19] + toAdd[20] + toAdd[21] + toAdd[22] + 
                              toAdd[30] + toAdd[31] + toAdd[32]+ toAdd[33] + toAdd[34] + toAdd[35] + toAdd[36] +
                              toAdd[37] + toAdd[38] + toAdd[39]);
        end
end

//The multiplier
module multiplier (dataa,datab,out);
input [23:0]dataa;
input [23:0]datab;
reg [47:0]result;
output[23:0]out;
always@(*)begin
    result = dataa*datab;
end
assign out = result[46:24]; 
endmodule

Dado que los coeficientes son correctos, ¿hay algún problema con el código? Supongo que hay un problema con la representación de los coeficientes en binario, o el multiplicador está mal, pero no puedo resolverlo.

¿Qué sucede si pasa los datos de entrada directamente a la salida sin filtrarlos? Además, ¿qué tipo de representación numérica está utilizando para los datos de audio y cómo tiene en cuenta la escala de los coeficientes?
sin el filtrado, el sonido es exactamente lo que se habla en el micrófono. estamos usando representación firmada; uno de los problemas era que el multiplicador no tenía "firmado" al lado del resultado del registro. así que arreglamos eso pero todavía no parece funcionar, ¿tal vez hay un desbordamiento cuando realizamos la suma?
¿Se comporta bien en simulación?
Puede intentar establecer todos, excepto un coeficiente en cero y al máximo (suponga que esto es 24'b1000000000000000000000000). Entonces, su filtro se comportaría como una línea de retardo.
Me pregunto acerca de "asignar = resultado [46:24];" al final de su multiplicador, ¿no debería ser "asignar = resultado [47:24];"?
Si desea verificar el desbordamiento, simule con una secuencia de muestra tal que cada coeficiente pos se multiplique por +max y cada coeficiente neg por -max y viceversa (pos * -max y neg * max). Esto debería darle el límite superior e inferior de su señal de salida.

Respuestas (2)

Su código será un poco más fácil de leer con un solo toque como módulo como (pseudocódigo verilog, ignorando, por ejemplo, cambios de bit después de mul, etc.)

module tap(reset, clk, samplein, sampleout, coef, sumin, sumout) 
  always@(posedge clk) begin
    if(reset) begin
      sumout <= 0;
      sampleout <= 0;
    end else begin
      sampleout <= samplein;
      sumout <= sumin + coef * samplein;
    end
endmodule

úsalo como:

tap tap0(reset, clk, input,    buf[0],  coef[0],  0, sum[0]);
tap tap1(reset, clk, buf[0],   buf[1],  coef[0],  0, sum[1]);
...
tap tap39(reset, clk, buf[38], buf[39], coef[39], 0, output);

Creo que esto es mucho más fácil de usar en un banco de pruebas

para cosas de audio, esta sigue siendo una implementación muy derrochadora debido a su bajo clk. frecuencia. Los multiplicadores son caros, y aquí se usan 40 multiplicadores a una frecuencia muy baja. Una solución más eficiente sería usar un clk que sea al menos 40 veces más alto que la frecuencia de muestra de audio y tener un módulo multiplicador y de adición que haga todas las operaciones.

Lo ideal sería hacer una grabación de los datos de entrada con SignalTap o similar para ver cómo llegan los datos dentro de la FPGA, o en su defecto generar algunos datos de prueba en el formato esperado usando un micrófono y matlab. Luego puede escribir Verilog para leer sus muestras y depurar el diseño usando un simulador y una herramienta de visor de forma de onda. Icarus Verilog y GTKWave son lo suficientemente buenos para hacer este trabajo sin costo alguno.

Altera tiene un núcleo FIR que tal vez quieras probar.