串行 FIR 濾波器設計

設計說明

設計參數不變,與並行 FIR 濾波器參數一致。即,輸入頻率為 7.5 MHz 和 250 KHz 的正弦波混合信號,經過 FIR 濾波器後,高頻信號 7.5MHz 被濾除,隻保留 250KMHz 的信號。

輸入頻率:    7.5MHz 和 250KHz
采樣頻率:    50MHz
阻帶:           1MHz-6MHz
階數:           15 (N=15)

串行設計,就是在 16 個時鍾周期內對 16 個延時數據分時依次進行乘法、加法運算,然後在時鍾驅動下輸出濾波值。考慮到 FIR 濾波器係數的對稱性,計算一個濾波輸出值的周期可以減少到 8 個。串行設計時每個周期隻進行一次乘法運算,所以設計中隻需一個乘法器即可。此時數據需要每 8 個時鍾周期有效輸入一次,但是為了保證輸出信號頻率的正確性,工作時鍾需要為采樣頻率的 8 倍,即 400MHz。這種方法的優點是資源耗費少,但是工作頻率要求高,數據不能持續輸出。

串行設計

設計中使用到的乘法器模塊代碼,可參考之前流水線式設計的乘法器。

為方便快速仿真,也可以直接使用乘號 * 完成乘法運算,設計中加入宏定義 SAFE_DESIGN 來選擇使用哪種乘法器。

FIR 濾波器係數可由 matlab 生成,具體見附錄。

實例

/**********************************************************
>> Description : fir study with serial tech
>> V190403     : Fs:50Mhz, fstop:1-6Mhz, order:16, sys clk:400MHz
***********************************************************/

`define SAFE_DESIGN
 
module fir_serial_low(
    input                rstn,
    input                clk,   // 係統工作時鍾,400MHz
    input                en ,   // 輸入數據有效信號
    input        [11:0]  xin,   // 輸入混合頻率的信號數據
    output               valid, // 輸出數據有效信號
    output       [28:0]  yout   // 輸出數據
    );
 
   //delay of input data enable
    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
 
    //fir coeficient
    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) 輸入數據移位部分
    reg [2:0]            cnt ;
    integer              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 ;    //8個周期計數
        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    //每8個周期讀入一次有效數據
            xin_reg[0] <= xin ;
            for (j=0; j<15; j=j+1) begin
                xin_reg[j+1] <= xin_reg[j] ; // 數據移位
            end
        end
    end
 
    //(2) 係數對稱,16個移位寄存器數據進行首位相加
    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 //from 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) 乘法運算,隻用一個乘法
    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    //例化自己設計的流水線乘法器
        (.clk        (clk),
         .rstn       (rstn),
         .data_rdy   (en_r[index_mult]),  //注意數據時序對應
         .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 ;  //直接乘
        end
    end
    wire                 en_mult = en_r[2];
`endif
 
    //(4) 積分累加,8組25bit數據 -> 1組 29bit 數據
    reg        [28:0]    sum ;
    reg                  valid_r ;
    //mult output en counter
    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  //計時8個周期
            cnt_acc_r <= 'b0 ;
        end
        else if (en_mult || cnt_acc_r != 0) begin //隻要en有效,計時不停
            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 //在第8個累加周期輸出濾波值
            sum      <= sum + mout;
            valid_r  <= 1'b1 ;
        end
        else if (en_mult && cnt_acc_r == 0) begin //初始化
            sum      <= mout ;
            valid_r  <= 1'b0 ;
        end
        else if (cnt_acc_r != 0) begin //acculating between cycles
            sum      <= sum + mout ;
            valid_r  <= 1'b0 ;
        end
    end
 
    //時鍾鎖存有效的輸出數據,為了讓輸出信號不是那麼頻繁的變化
    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) 輸出數據有效延遲,即濾波數據丟掉前15個濾波值
    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

testbench 編寫如下,主要功能就是不間斷連續的輸入 250KHz 與 7.5MHz 的正弦波混合信號數據。輸入的混合信號數據也可由 matlab 生成,具體見附錄。

其中,工作頻率為 400MHz,但輸入數據和輸入數據有效信號應當都保持 50MHz 的頻率輸入。

實例

module test ;
    //input
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg  [11:0]  xin ;
    //output
    wire [28:0]  yout ;
    wire         valid ;

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

//=====================================
// 8*50MHz clk generating
    localparam   TCLK_HALF     = (10_000 >>3);
    initial begin
        clk = 1'b0 ;
        forever begin
            # TCLK_HALF clk = ~clk ;
        end
     end
 
//============================
//  reset and finish
    initial begin
        rst_n = 1'b0 ;
        # 30        rst_n = 1'b1 ;
        # (TCLK_HALF * 2 * 8  * SIMU_CYCLE) ;
        $finish ;
    end
 
//=======================================
// read cos data into register
    reg          [11:0] stimulus [0: SIN_DATA_NUM-1] ;
    integer      i ;
    initial begin
        $readmemh("../tb/cosx0p25m7p5m12bit.txt", stimulus) ;
        en = 0 ;
        i = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            repeat(7)  @(negedge clk) ; //空置7個周期,第8個周期給數據
            en          = 1 ;
            xin         = stimulus[i] ;
            @(negedge clk) ;
            en          = 0 ;         //輸入數據有效信號隻持續一個周期即可
            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

仿真結果

由下圖仿真結果可知,經過 FIR 濾波器後的信號隻有一種低頻率信號(250KHz),高頻信號(7.5MHz)被濾除了。為了波形更加的美觀,取 16 個之後的濾波數據作為有效輸出。

波形局部放大後如下圖所示,此時輸入數據有效信號 en 與輸出數據有效信號 valid 是周期(50MHz)相同的脈衝信號,不是持續有效的。但工作時鍾為 400MHz,所以輸出也會呈現為 50MHz 采樣頻率下的 250KHz 頻率的正弦波信號。

附錄:matlab 使用(與《並行 FIR 濾波器設計》一致)

生成 FIR 濾波器係數

打開 matlab,在命令窗口輸入命令: fdatool。

然後會打開如下窗口,按照 FIR 濾波器參數進行設置,如下所示。

這裏選擇的 FIR 實現方法是最小二乘法(Least-squares),不同的實現方式濾波效果也不同。

點擊 File -> Export

將濾波器參數輸出,存到變量 coef 中,如下圖所示。

此時 coef 變量應該是浮點型數據。對其進行一定倍數的相乘擴大,然後取其近似的定點型數據作為設計中的 FIR 濾波器參數。這裏取擴大倍數為 2048,結果如下所示。

生成輸入的混合信號

利用 matlab 生成混合的輸入信號參考代碼如下。

信號為無符號定點型數據,位寬寬度為 12bit,存於文件 cosx0p25m7p5m12bit.txt

實例

clear all;close all;clc;
%=======================================================
% generating a cos wave data with txt hex format
%=======================================================

fc          = 0.25e6 ;      % 中心頻率
fn          = 7.5e6 ;       % 雜波頻率
Fs          = 50e6 ;        % 采樣頻率
T           = 1/fc ;        % 信號周期
Num         = Fs * T ;      % 周期內信號采樣點數
t           = (0:Num-1)/Fs ;      % 離散時間
cosx        = cos(2*pi*fc*t) ;    % 中心頻率正弦信號
cosn        = cos(2*pi*fn*t) ;    % 雜波信號
cosy        = mapminmax(cosx + cosn) ;     %幅值擴展到(-1,1) 之間
cosy_dig    = floor((2^11-1) * cosy + 2^11) ;     %幅值擴展到 0~4095
fid         = fopen('cosx0p25m7p5m12bit.txt', 'wt') ;  %寫數據文件
fprintf(fid, '%x\n', cosy_dig) ;
fclose(fid) ;
 
%時域波形
figure(1);
subplot(121);plot(t,cosx);hold on ;
plot(t,cosn) ;
subplot(122);plot(t,cosy_dig) ;
 
%頻域波形
fft_cosy    = fftshift(fft(cosy, Num)) ;
f_axis      = (-Num/2 : Num/2 - 1) * (Fs/Num) ;
figure(5) ;
plot(f_axis, abs(fft_cosy)) ;

源碼下載

Download