Le filtre FIR (réponse impulsionnelle finie) est un filtre à réponse impulsionnelle unitaire de longueur finie, également appelé filtre non récursif.

Les filtres FIR ont des caractéristiques phase-fréquence linéaires strictes, et leur réponse unitaire est finie, donc ce sont des systèmes stables et ils sont largement utilisés dans les communications numériques, le traitement d'images et d'autres domaines.

Principe du filtre FIR

Le filtre FIR est un filtre à réponse impulsionnelle unitaire de longueur finie. La structure de forme directe est la suivante :

Le filtre FIR est essentiellement la convolution du signal d'entrée avec la fonction de réponse impulsionnelle unitaire, exprimée comme suit :

Le filtre FIR présente les caractéristiques suivantes :

  • (1) La réponse est une séquence de longueur finie.
  • (2) La fonction système converge pour |z| > 0, tous les pôles sont en z=0, et il s'agit d'un système causal.
  • (3) Structurellement, il est non récursif, sans rétroaction de la sortie vers l'entrée.
  • (4) La réponse de phase du signal d'entrée est linéaire car les coefficients de la fonction de réponse h(n) sont symétriques.
  • (5) Les différences de phase relatives entre les composantes fréquentielles du signal d'entrée sont également fixes.
  • (6) La convolution dans le domaine temporel équivaut à une multiplication dans le domaine fréquentiel, donc cette convolution est équivalente au facteur de gain pour le filtrage de chaque composante fréquentielle du spectre. Certaines composantes fréquentielles sont conservées, tandis que d'autres sont atténuées, ce qui permet d'obtenir l'effet de filtrage.

Conception de filtre FIR parallèle

Description de la conception

L'entrée est un signal sinusoïdal mixte avec des fréquences de 7.5 MHz et 250 KHz. Après passage à travers le filtre FIR, le signal haute fréquence de 7.5 MHz est supprimé, et seul le signal de 250 KHz est conservé. Les paramètres de conception sont les suivants :

输入频率:    7.5MHz 和 250KHz
采样频率:    50MHz
阻带:           1MHz ~ 6MHz
阶数:           15(N-1=15)

D'après la structure du filtre FIR, lorsque l'ordre est de 15, l'implémentation nécessite 16 multiplicateurs, 15 additionneurs et 15 ensembles de registres à retard. Pour stabiliser les données du premier temps, un ensemble supplémentaire de registres à retard peut être utilisé, soit un total de 16 ensembles de registres à retard. En raison de la symétrie des coefficients du filtre FIR, le nombre de multiplicateurs peut être réduit de moitié, avec un total de 8 multiplicateurs.

La conception parallèle consiste à effectuer la multiplication et l'addition sur les 16 éléments de données retardés simultanément dans un cycle d'horloge, puis à sortir la valeur filtrée sous la commande de l'horloge. L'avantage de cette méthode est un court délai de filtrage, mais elle présente des contraintes de timing relativement élevées.

Conception parallèle

Pour le code du module multiplicateur utilisé dans cette conception, reportez-vous au multiplicateur de la conception en pipeline précédente.

Pour faciliter une simulation rapide, vous pouvez également utiliser directement le signe de multiplication*pour effectuer l'opération de multiplication. Une définition de macro SAFE_DESIGN est ajoutée dans la conception pour sélectionner le multiplicateur à utiliser.

Les coefficients du filtre FIR peuvent être générés par matlab ; voir l'annexe pour les détails.

Exemple

/***********************************************************
>> V201001 : Fs:50Mhz, fstop:1Mhz-6Mhz, ordre: 15
************************************************************/

`define SAFE_DESIGN
 
module fir_guide    (
    input                rstn,  // Réinitialisation, actif à l'état bas
    input                clk,   // Fréquence de fonctionnement, c'est-à-dire fréquence d'échantillonnage
    input                en,    // Signal de validité des données d'entrée
    input        [11:0]  xin,   // Données du signal d'entrée à fréquences mélangées
    output               valid, // Signal de validation des données de sortie
    output       [28:0]  yout   // Données de sortie, signal basse fréquence, soit 250KHz
    );
 
    //retard d'activation des données
    reg [3:0]            en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r[3:0]      <= 'b0 ;
        end
        else begin
            en_r[3:0]      <= {en_r[2:0], en} ;
        end
    end
 
   //(1) 16 ensembles de registres à décalage
    reg        [11:0]    xin_reg[15:0];
    reg [3:0]            i, j ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            for (i=0; i<15; i=i+1) begin
                xin_reg[i]  <= 12'b0;
            end
        end
        else if (en) begin
            xin_reg[0] <= xin ;
            for (j=0; j<15; j=j+1) begin
                xin_reg[j+1] <= xin_reg[j] ; // Opération de décalage périodique
            end
        end
    end
 
   //Seuls 8 multiplicateurs sont nécessaires en raison de la symétrie des coefficients du filtre FIR
   //(2) Les coefficients sont symétriques ; additionnez les première et dernière données des 16 registres à décalage
    reg        [12:0]    add_reg[7:0];
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            for (i=0; i<8; i=i+1) begin
                add_reg[i] <= 13'd0 ;
            end
        end
        else if (en_r[0]) begin
            for (i=0; i<8; i=i+1) begin
                add_reg[i] <= xin_reg[i] + xin_reg[15-i] ;
            end
        end
    end
 
    //(3) 8 multiplicateurs
    // Coefficients du filtre, déjà amplifiés par un certain facteur
    wire        [11:0]   coe[7:0] ;
    assign coe[0]        = 12'd11 ;
    assign coe[1]        = 12'd31 ;
    assign coe[2]        = 12'd63 ;
    assign coe[3]        = 12'd104 ;
    assign coe[4]        = 12'd152 ;
    assign coe[5]        = 12'd198 ;
    assign coe[6]        = 12'd235 ;
    assign coe[7]        = 12'd255 ;
    reg        [24:0]   mout[7:0];
 
`ifdef SAFE_DESIGN
    // Multiplicateur pipeline
    wire [7:0]          valid_mult ;
    genvar              k ;
    generate
        for (k=0; k<8; k=k+1) begin
            mult_man #(13, 12)
            u_mult_paral          (
              .clk        (clk),
              .rstn       (rstn),
              .data_rdy   (en_r[1]),
              .mult1      (add_reg[k]),
              .mult2      (coe[k]),
              .res_rdy    (valid_mult[k]), // Toutes les validations de sortie sont exactement identiques
              .res        (mout[k])
            );
        end
    endgenerate
    wire valid_mult7     = valid_mult[7] ;
 
`else
    // Si les contraintes de timing ne sont pas strictes, vous pouvez directement utiliser le signe de multiplication
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            for (i=0 ; i<8; i=i+1) begin
                mout[i]     <= 25'b0 ;
            end
        end
        else if (en_r[1]) begin
            for (i=0 ; i<8; i=i+1) begin
                mout[i]     <= coe[i] * add_reg[i] ;
            end
        end
    end
    wire valid_mult7 = en_r[2];
`endif
 
    //(4) Accumulation, 8 groupes de données de 25 bits -> 1 groupe de données de 29 bits
    // Délai de validité des données
    reg [3:0]            valid_mult_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            valid_mult_r[3:0]  <= 'b0 ;
        end
        else begin
            valid_mult_r[3:0]  <= {valid_mult_r[2:0], valid_mult7} ;
        end
    end

`ifdef SAFE_DESIGN
    // Lors de l'addition, pipeline sur plusieurs cycles pour optimiser le timing
    reg        [28:0]    sum1 ;
    reg        [28:0]    sum2 ;
    reg        [28:0]    yout_t ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            sum1   <= 29'd0 ;
            sum2   <= 29'd0 ;
            yout_t <= 29'd0 ;
        end
        else if(valid_mult7) begin
            sum1   <= mout[0] + mout[1] + mout[2] + mout[3] ;
            sum2   <= mout[4] + mout[5] + mout[6] + mout[7] ;
            yout_t <= sum1 + sum2 ;
        end
    end
 
`else
    // Calculer le résultat d'accumulation en une seule étape, mais en pratique le timing est très dangereux
    reg signed [28:0]    sum ;
    reg signed [28:0]    yout_t ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            sum    <= 29'd0 ;
            yout_t <= 29'd0 ;
        end
        else if (valid_mult7) begin
            sum    <= mout[0] + mout[1] + mout[2] + mout[3] + mout[4] + mout[5] + mout[6] + mout[7];
            yout_t <= sum ;
        end
    end
`endif
    assign yout  = yout_t ;
    assign valid = valid_mult_r[0];

endmodule

testbench

Le banc de test est écrit comme suit. Sa fonction principale est d'injecter en continu les données de signal mixte sinusoïdal de 250KHz et 7.5MHz sans interruption. Les données de signal mixte d'entrée peuvent également être générées par matlab ; voir l'annexe pour plus de détails.

Exemple

`timescale 1ps/1ps
 
module test ;
   //entrée
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg [11:0]   xin ;
    //sortie
    wire         valid ;
    wire [28:0]  yout ;
 
    parameter    SIMU_CYCLE   = 64'd2000 ;  // fréquence d'échantillonnage de 50MHz
    parameter    SIN_DATA_NUM = 200 ;      // période de simulation

//=====================================
// génération de clk à 50MHz
    localparam   TCLK_HALF     = 10_000;
    initial begin
        clk = 1'b0 ;
        forever begin
            # TCLK_HALF ;
            clk = ~clk ;
        end
    end
 
//============================
// réinitialiser et terminer
    initial begin
        rst_n = 1'b0 ;
        # 30   rst_n = 1'b1 ;
        # (TCLK_HALF * 2 * SIMU_CYCLE) ;
        $finish ;
    end
 
//=======================================
// lire les données de signal dans le registre
    reg          [11:0] stimulus [0: SIN_DATA_NUM-1] ;
    entier      i ;
    initial begin
        $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ;
        i = 0 ;
        en = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            @(negedge clk) begin
                en          = 1'b1 ;
                xin         = stimulus[i] ;
                if (i == SIN_DATA_NUM-1) begin  // Contrôle d'alimentation périodique des données
                    i = 0 ;
                end
                else begin
                    i = i + 1 ;
                end
            end
        end
    end
 
    fir_guide u_fir_paral (
      .xin         (xin),
      .clk         (clk),
      .en          (en),
      .rstn        (rst_n),
      .valid       (valid),
      .yout        (yout));
 
endmodule

Résultats de simulation

D'après les résultats de simulation de la figure ci-dessous, le signal après le filtre FIR ne contient qu'un seul signal basse fréquence (250KHz), et le signal haute fréquence (7.5MHz) a été filtré. De plus, la forme d'onde de sortie est continue et peut être délivrée en continu.

Cependant, comme le montre le cercle rouge, le début de la forme d'onde est irrégulier. Zoomez sur cette partie.

Après avoir zoomé sur le début de la forme d'onde, comme le montre la figure ci-dessous, on peut voir que la période de la forme d'onde irrégulière, c'est-à-dire l'intervalle de temps entre les deux lignes verticales, est de 16 cycles d'horloge.

Comme les données sont entrées en série et que 16 ensembles de registres de retard sont utilisés dans la conception, le premier point normal après filtrage doit être retardé de 16 cycles d'horloge par rapport au premier moment de sortie des données filtrées. Autrement dit, le signal valid de sortie de données doit être retardé de 16 cycles d'horloge supplémentaires, ce qui rendra la forme d'onde de sortie plus parfaite.

Annexe : Utilisation de matlab

Génération des coefficients du filtre FIR

Ouvrez matlab, puis saisissez la commande dans la fenêtre de commande : fdatool.

Ensuite, la fenêtre suivante s'ouvrira ; définissez les paramètres en fonction du filtre FIR.

La méthode d'implémentation FIR sélectionnée ici est celle des moindres carrés (Least-squares). Différentes méthodes d'implémentation produisent différents effets de filtrage.

Cliquez sur Fichier -> Exporter

Exportez les paramètres du filtre et stockez-les dans la variable coef, comme le montre la figure ci-dessous.

À ce stade, la variable coef doit contenir des données à virgule flottante. Multipliez-la par un certain facteur pour l'amplifier, puis prenez ses données approximatives en virgule fixe comme paramètres du filtre FIR dans la conception. Ici, le facteur d'amplification est 2048, et les résultats sont les suivants.

Génération du signal d'entrée mixte

Le code de référence pour générer le signal d'entrée mixte à l'aide de matlab est le suivant.

Le signal est constitué de données non signées en virgule fixe d'une largeur de 12 bits, stockées dans un fichiercosx0p25m7p5m12bit.txt。

Exemple

clear all;close all;clc;
%=======================================================
% génération de données d'onde cos avec format hexadécimal txt
%=======================================================

fc          = 0.25e6 ;      %Fréquence centrale
fn          = 7.5e6 ;       %Fréquence du fouillis
Fs          = 50e6 ;        %Fréquence d'échantillonnage
T           = 1/fc ;        %Période du signal
Num         = Fs * T ;      %Nombre de points d'échantillonnage du signal dans une période
t           = (0:Num-1)/Fs ;      %Temps discret
cosx        = cos(2*pi*fc*t) ;    %Signal sinusoïdal de fréquence centrale
cosn        = cos(2*pi*fn*t) ;    %Signal de fouillis
cosy        = mapminmax(cosx + cosn) ;     %Amplitude étendue à la plage (-1,1)
cosy_dig    = floor((2^11-1) * cosy + 2^11) ;     %Amplitude étendue à0~4095
fid         = fopen('cosx0p25m7p5m12bit.txt', 'wt') ;  %Écrire le fichier de données
fprintf(fid, '%x\n', cosy_dig) ;
fclose(fid) ;
 
%Forme d'onde temporelle
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
 
%Forme d'onde fréquentielle
fft_cosy    = fftshift(fft(cosy, Num)) ;
f_axis      = (-Num/2 : Num/2 - 1) * (Fs/Num) ;
figure(5) ;
plot(f_axis, abs(fft_cosy)) ;

Téléchargement du code source

Téléchargement