Conception de filtre FIR série

Description de la conception

Les paramètres de conception sont inchangés et cohérents avec les paramètres du filtre FIR parallèle. Autrement dit, pour les signaux d'entrée sinusoïdaux mixtes ayant des fréquences de 7.5 MHz et 250 KHz, après passage dans le filtre FIR, le signal haute fréquence à 7.5 MHz est filtré, ne laissant que le signal à 250 KHz.

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

Dans la conception série, les 16 échantillons de données retardés sont traités séquentiellement par des opérations de multiplication et d'addition en répartition temporelle sur 16 cycles d'horloge, puis la valeur filtrée est émise sous la commande de l'horloge. Compte tenu de la symétrie des coefficients du filtre FIR, la période de calcul d'une valeur de sortie filtrée peut être réduite à 8 cycles. Dans la conception série, une seule opération de multiplication est effectuée par cycle, donc un seul multiplieur est nécessaire dans la conception. À ce stade, les données doivent être entrées de manière valide une fois tous les 8 cycles d'horloge. Cependant, pour garantir l'exactitude de la fréquence du signal de sortie, l'horloge de travail doit être 8 fois la fréquence d'échantillonnage, c'est-à-dire 400 MHz. L'avantage de cette méthode est une faible consommation de ressources, mais elle nécessite une fréquence de travail élevée et les données ne peuvent pas être émises en continu.

Conception série

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

Pour une simulation pratique et rapide, le signe de multiplication peut également être utilisé directement*pour réaliser l'opération de multiplication. Une définition de macro SAFE_DESIGN est ajoutée dans la conception pour sélectionner le multiplieur à utiliser.

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

Exemple

/**********************************************************
>> Description : étude FIR avec technique série
>> V190403 : Fs:50Mhz, fstop:1-6Mhz, order:16, sys clk:400MHz
***********************************************************/

`define SAFE_DESIGN
 
module fir_serial_low(
    input                rstn,
    input                clk,   // Horloge de travail du système, 400MHz
    input                en ,   // Signal de validité des données d'entrée
    input        [11:0]  xin,   // Données de signal d'entrée à fréquences mixtes
    output               valid, // Signal de validation des données de sortie
    output       [28:0]  yout   // Données de sortie
    );
 
   //retard de validation des données d'entrée
    reg [11:0]            en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r[11:0]      <= 'b0 ;
        end
        else begin
            en_r[11:0]      <= {en_r[10:0], en} ;
        end
    end
 
    //coefficient FIR
    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 ;
 
    //(1) Section de décalage des données d'entrée
    reg [2:0]            cnt ;
    entier              i, j ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt <= 3'b0 ;
        end
        else if (en || cnt != 0) begin
            cnt <= cnt + 1'b1 ;    //comptage sur 8 cycles
        end
    end
 
    reg [11:0]           xin_reg[15:0];
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            for (i=0; i<16; i=i+1) begin
                xin_reg[i]  <= 12'b0;
            end
        end
        else if (cnt == 3'd0 && en) begin    //Lire les données valides une fois tous les 8 cycles
            xin_reg[0] <= xin ;
            for (j=0; j<15; j=j+1) begin
                xin_reg[j+1] <= xin_reg[j] ; // Décalage de données
            end
        end
    end
 
    //(2) Les coefficients sont symétriques ; additionner la première et la dernière donnée des 16 registres à décalage
    reg  [11:0]          add_a, add_b ;
    reg  [11:0]          coe_s ;
    wire [12:0]          add_s ;
    wire [2:0]           xin_index = cnt>=1 ? cnt-1 : 3'd7 ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            add_a  <= 13'b0 ;
            add_b  <= 13'b0 ;
            coe_s  <= 12'b0 ;
        end
        else if (en_r[xin_index]) begin //depuis en_r[1]
            add_a  <= xin_reg[xin_index] ;
            add_b  <= xin_reg[15-xin_index] ;
            coe_s  <= coe[xin_index] ;
        end
    end
    assign add_s = {add_a} + {add_b} ;  
 
    //(3) Opération de multiplication, en utilisant un seul multiplicateur
    reg        [24:0]    mout ;
`ifdef SAFE_DESIGN
    wire                 en_mult ;
    wire [3:0]           index_mult = cnt>=2 ? cnt-1 : 4'd7 + cnt[0] ;
    mult_man #(13, 12)   u_mult_single    //Instancier le multiplicateur pipeline auto-conçu
        (.clk        (clk),
         .rstn       (rstn),
         .data_rdy   (en_r[index_mult]),  //Faites attention à la correspondance temporelle des données
         .mult1      (add_s),
         .mult2      (coe_s),
         .res_rdy    (en_mult),  
         .res        (mout)
        );
 
`else
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            mout   <= 25'b0 ;
        end
        else if (|en_r[8:1]) begin
            mout   <= coe_s * add_s ;  //Multiplication directe
        end
    end
    wire                 en_mult = en_r[2];
`endif
 
    //(4) Accumulation, 8 groupes de données de 25 bits -> 1 groupe de données de 29 bits
    reg        [28:0]    sum ;
    reg                  valid_r ;
    //compteur d'activation de sortie mult
    reg [4:0]            cnt_acc_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt_acc_r <= 'b0 ;
        end
        else if (cnt_acc_r == 5'd7) begin  //Compter 8 cycles
            cnt_acc_r <= 'b0 ;
        end
        else if (en_mult || cnt_acc_r != 0) begin //Tant que en est valide, le comptage continue
            cnt_acc_r <= cnt_acc_r + 1'b1 ;
        end
    end
 
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            sum      <= 29'd0 ;
            valid_r  <= 1'b0 ;
        end
        else if (cnt_acc_r == 5'd7) begin //Sortir la valeur filtrée au 8e cycle d'accumulation
            sum      <= sum + mout;
            valid_r  <= 1'b1 ;
        end
        else if (en_mult && cnt_acc_r == 0) begin //Initialisation
            sum      <= mout ;
            valid_r  <= 1'b0 ;
        end
        else if (cnt_acc_r != 0) begin //accumulation entre les cycles
            sum      <= sum + mout ;
            valid_r  <= 1'b0 ;
        end
    end
 
    //Verrouiller les données de sortie valides sur le front d'horloge afin que le signal de sortie ne change pas trop fréquemment
    reg [28:0]           yout_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            yout_r <= 'b0 ;
        end
        else if (valid_r) begin
            yout_r <= sum ;
        end
    end
    assign yout = yout_r ;
 
    //(5) Délai de validité des données de sortie ; autrement dit, écarter les 15 premières valeurs filtrées
    reg [4:0]    cnt_valid ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt_valid      <= 'b0 ;
        end
        else if (valid_r && cnt_valid != 5'd16) begin
            cnt_valid      <= cnt_valid + 1'b1 ;
        end
    end
    assign valid = (cnt_valid == 5'd16) & valid_r ;

endmodule

testbench

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

Ici, la fréquence de travail est de 400MHz, mais les données d'entrée et le signal de validation des données d'entrée doivent tous deux être fournis à une fréquence de 50MHz.

Exemple

module test ;
    //entrée
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg  [11:0]  xin ;
    //sortie
    wire [28:0]  yout ;
    wire         valid ;

    parameter    SIMU_CYCLE   = 64'd1000 ;
    parameter    SIN_DATA_NUM = 200 ;

//=====================================
// Génération de l'horloge clk à 8*50MHz
    localparam   TCLK_HALF     = (10_000 >>3);
    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 * 8  * SIMU_CYCLE) ;
        $finish ;
    end
 
//=======================================
// lire les données cos dans le registre
    reg          [11:0] stimulus [0: SIN_DATA_NUM-1] ;
    entier      i ;
    initial begin
        $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ;
        en = 0 ;
        i = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            repeat(7)  @(negedge clk) ; //Inactif pendant 7 cycles, fournir les données au 8e cycle
            en          = 1 ;
            xin         = stimulus[i] ;
            @(negedge clk) ;
            en          = 0 ;         //Le signal de validation des données d'entrée ne doit durer qu'un cycle
            if (i == SIN_DATA_NUM-1)  i = 0 ;
            else  i = i + 1 ;
        end
    end
 
    fir_serial_low u_fir_serial (
        .clk         (clk),
        .rstn        (rst_n),
        .en          (en),
        .xin         (xin),
        .valid       (valid),
        .yout        (yout));

endmodule

Résultats de simulation

D'après les résultats de simulation de la figure ci-dessous, on peut voir que le signal après le filtre FIR ne contient qu'un seul signal basse fréquence (250KHz), et que le signal haute fréquence (7.5MHz) a été filtré. Pour obtenir une forme d'onde plus esthétique, les données filtrées après les 16 premiers échantillons sont prises comme sortie valide.

La forme d'onde agrandie localement est illustrée dans la figure ci-dessous. À ce moment, le signal de validation des données d'entrée en et le signal de validation des données de sortie valid sont des signaux impulsionnels de même période (50MHz), et ne sont pas valides en continu. Cependant, comme l'horloge de travail est de 400MHz, la sortie apparaîtra également comme un signal sinusoïdal d'une fréquence de 250KHz à une fréquence d'échantillonnage de 50MHz.

Annexe : Utilisation de MATLAB (identique à« Parallel FIR Filter Design »cohérent)

Générer les coefficients du filtre FIR

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

La fenêtre suivante s'ouvrira alors. Configurez-la selon les paramètres du filtre FIR, comme indiqué ci-dessous.

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

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 moment, la variable coef doit être de type flottant. Multipliez-la par un certain facteur pour la mettre à l'échelle, puis prenez ses données approximatives en virgule fixe comme paramètres du filtre FIR dans la conception. Ici, le facteur d'échelle est 2048, et les résultats sont les suivants.

Générer le 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 une donnée en virgule fixe non signée d'une largeur de 12 bits, stockée dans le 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 par 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 à (-1,1) plage
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 dans le domaine temporel
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
 
%Forme d'onde dans le domaine fréquentiel
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