1. 程式人生 > >序列FIR濾波器---Verilog設計

序列FIR濾波器---Verilog設計

數字濾波器

數字濾波器從實現結構上劃分,有FIR和IIR兩種。FIR的特點是:線性相位、消耗資源多;IIR的特點是:非線性相位、消耗資源少。由於FIR系統的線性相位特點,設計中絕大多數情況都採用FIR濾波器。

線性相位系統的意義,這裡的線性相位指的是在設計者關心的通帶範圍內,LTI系統滿足線性相位要求:
1. 從延時的角度看:保證了輸入訊號的相位響應是線性的,即保證了輸入訊號的延時特性。
2. 從相位的角度看:輸入的各頻率成分的訊號之間,相對相位是固定的。通過線性相位系統後,相對相位關係保持不變。

對於關心相位的系統,比如調製解調系統,需要使用FIR濾波器;對於只關心頻率成分的系統,比如只是提取某一頻率分量,為了節省資源,使用IIR濾波器即可。

1、 FIR濾波器基本知識

FIR的最大特點就是其系統響應 h(n)是一個N點的有限長序列,FIR的輸出y(n)本質上就是輸入訊號x(n)和h(n)的卷積(根據傅立葉變換性質,時域卷積等於頻域相乘,因此卷積相當於篩選頻譜中的各頻率分量的增益倍數,某些頻率分量保留,某些頻率分量衰減,從而實現濾波效果)。FIR在實現上的本質是帶抽頭延遲的加法器和乘法器的組合,每一個乘法器對應一個係數。

由理論知識可知,只有當FIR的h(n)對稱時,FIR濾波器才具有線性相位特性。使用MATLAB等工具設計FIR時,得到的h(n)也都是具有對稱性的。

FIR濾波器的實現結構主要有直接型、級聯型、頻率取樣型、格型四種。其中最適合FPGA實現的是直接型。“直接”是指直接由卷積公式得到:
這裡寫圖片描述



這裡寫圖片描述

這裡寫圖片描述
本設計是基於FPGA實現一個8階的FIR數字低通濾波器。本次設計首先利用MATLAB中的FDAtool工具設計出一個取樣頻率為10KHZ、截止頻率為3KHZ的FIR低通濾波器,,通過FDAtool匯出8點係數,然後將係數放大256倍,以便於在FPGA中使用,最後通過Xilinx的Vivado進行Verilog語言編寫濾波器演算法,然後通過模擬結果和MATLAB模擬結果的比較來驗證該濾波器的正確性。系統的總體設計框圖如下:

這裡寫圖片描述

首先由MATLAB生成一個由三個正弦波疊加的待濾波訊號,三個正弦波的頻率分別是1KHZ,3KHZ,4KHZ。然後將待濾波訊號送入Modesim模擬,觀察濾波後的波形,再利用MATLAB裡面的conv函式將濾波器係數和待濾波訊號卷積並觀察濾波後的波形圖,將Modesim的模擬結果和MATLAB的模擬結果比較並驗證在FPGA中濾波器演算法的正確性。

2、FIR濾波器原理

在學習數字訊號處理時,濾波器是重點,數字濾波器有很多種,比如FIR、IIR、LMS等濾波演算法,FIR (Finite Impulse Response)濾波器的特點是它的衝擊響應是有限的,它跟過去的訊號無關,所以在使用時容易實現,速度快。

要理解FIR濾波器,需要知道訊號的頻域跟時域的關係,訊號的頻譜就是訊號在頻域上的表現形式,如果一個訊號由2個正弦波疊加(圖1)而成,我們在時域是看不出什麼規律的,將訊號進行傅立葉變換到頻域(圖2),我們就可以

                                                  

很清楚的看到該訊號是由2個正弦波疊加而成的。

如果我們要對圖1這個訊號進行濾波,從頻域上看,將訊號的頻譜乘以圖4所示的矩形波,結果的頻譜很顯然就只剩下了低頻段的這個正弦波,那麼我們知道在頻域和一個矩形波相乘就可以將高頻濾出,在時域怎麼做呢?我們學過訊號與系統,知道頻域卷積定理,這個定理的內容就是說:兩個訊號在頻域相乘,那麼在時域就相當於卷積,在時域相乘,在頻域就相當於卷積。知道了這個定理,我們將圖4的頻域訊號反變換到時域,變成圖3所示的訊號,我們將這個訊號和圖1的訊號進行卷積,得到的結果就是圖5所示的波形,這個波形的頻譜如圖6所示。這樣我們便完成了對訊號的濾波。

MATLAB中的FDAtool就是用來根據需要的濾波器生成圖3所示的濾波器係數。

       3、MATLAB生成訊號

  3.1濾波器係數設計

  在MATLAB中輸入fdatool即可開啟濾波器設計工具,如圖7所示。裡面可以設定濾波器的型別,取樣頻率,截止頻率等。本設計設定的引數如圖8所示。


圖7


圖8 

然後將此濾波器係數匯出,然後用以下命令將係數放大、取整:

>> Num

 

Num =

 

-0.0325  -0.0384    0.0784    0.2874   0.3984    0.2874    0.0784  -0.0384   -0.0325

 

>> Num=round(Num*256)//將係數放大並取整

Num =

  -8   -10    20  74   102   74   20   -10   -8

 

 

       最終生成的係數Num即可用於FPGA進行FIR濾波器實現。

 

3.2 待濾波訊號的設計

         本設計用於模擬的輸入波形是三個正弦波疊加而成,分別是1KHZ、3KHZ、4KHZ。下面是用於生成待濾波訊號的m檔案內容:

 

%*********產生.data檔案 用於FPGA模擬************%

 

Fs = 10000; %取樣頻率決定了兩個正弦波點之間的間隔
N = 4096; %取樣點數
N1 = 0 : 1/Fs : N/Fs-1/Fs;
s = sin(1000*2*pi*N1) + sin(3000*2*pi*N1) +sin(4000*2*pi*N1);%三種正弦波
fidc = fopen('E:\my_project\vivado_project\FIR\mem.txt','wb');  %將結果寫入mem.txt檔案,便於modesim使用

for x = 1 : N
    A = round(s(x)*20);
   if (A >= 0)
      bin_x = dec2bin(A, 8);        % 正數的反碼和補碼都和原碼一樣
      fprintf(fidc,'%s\n',bin_x);
   else
      bin_x = dec2bin(2^8 + A, 8);
      fprintf(fidc,'%s\n',bin_x);
   end
end 

fclose(fidc);  

4、FPGA實現FIR演算法

       實現FIR濾波器的過程其實就是實現卷積的過程,卷積的公式如下,從如下公式

module FIR#(
           parameter  WIDTH = 8

)

(
           input                              I_clk,
           input                              I_rst_p,
           input  signed     [WIDTH -1:0]     I_data,
           output signed     [2*WIDTH-1:0]    O_data

    );

wire signed [7:0] coeff1 = -8;//matlab fir生成係數 * 256
wire signed [7:0] coeff2 = -10;
wire signed [7:0] coeff3 = 20;
wire signed [7:0] coeff4 = 74;
wire signed [7:0] coeff5 = 102;


reg  signed [WIDTH -1:0]  sample_0;
reg  signed [WIDTH -1:0]  sample_1;
reg  signed [WIDTH -1:0]  sample_2;
reg  signed [WIDTH -1:0]  sample_3;
reg  signed [WIDTH -1:0]  sample_4;
reg  signed [WIDTH -1:0]  sample_5;
reg  signed [WIDTH -1:0]  sample_6;
reg  signed [WIDTH -1:0]  sample_7;
reg  signed [WIDTH -1:0]  sample_8;

//--------------------------------------加法
reg  signed [WIDTH:0]  add_data_0;//9 bit
reg  signed [WIDTH:0]  add_data_1;
reg  signed [WIDTH:0]  add_data_2;
reg  signed [WIDTH:0]  add_data_3;
reg  signed [WIDTH:0]  add_data_4;



reg  signed [2*WIDTH:0]  mult_1;//17 bit
reg  signed [2*WIDTH:0]  mult_2;
reg  signed [2*WIDTH:0]  mult_3;
reg  signed [2*WIDTH:0]  mult_4;
reg  signed [2*WIDTH:0]  mult_5;

reg signed  [2*WIDTH+1:0]   add_level_1;// 18 bit
reg signed  [2*WIDTH+1:0]   add_level_2;
reg signed  [2*WIDTH+1:0]   add_level_3;

wire signed [2*WIDTH+3:0] data_out;//20 bit
//====================================================輸入資料,移位暫存器
[email protected](posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            sample_1 <= 'h0;
            sample_2 <= 'h0;
            sample_3 <= 'h0;
            sample_4 <= 'h0;
            sample_5 <= 'h0;
            sample_6 <= 'h0;
            sample_7 <= 'h0;
            sample_8 <= 'h0;
         end
      else
         begin
            sample_0 <= I_data;
            sample_1 <= sample_0;
            sample_2 <= sample_1;
            sample_3 <= sample_2;
            sample_4 <= sample_3;
            sample_5 <= sample_4;
            sample_6 <= sample_5;
            sample_7 <= sample_6;
            sample_8 <= sample_7;
         end

   end
//=============================================fir係數對稱 add data  思想:共享資源
[email protected](posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            add_data_0 <= 'h0;
            add_data_1 <= 'h0;
            add_data_2 <= 'h0;
            add_data_3 <= 'h0;
            add_data_4 <= 'h0;
         end
      else
         begin
            add_data_0 <= sample_0 + sample_8;
            add_data_1 <= sample_1 + sample_7;
            add_data_2 <= sample_2 + sample_6;
            add_data_3 <= sample_3 + sample_5;
            add_data_4 <= {sample_4[WIDTH -1],sample_4} ;
         end
   end

//===========================================乘法  
[email protected](posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            mult_1 <= 'h0;
            mult_2 <= 'h0;
            mult_3 <= 'h0;
            mult_4 <= 'h0;
            mult_5 <= 'h0;

        end
     else
        begin
           mult_1 <= add_data_0 * coeff1;
           mult_2 <= add_data_1 * coeff2;
           mult_3 <= add_data_2 * coeff3;
           mult_4 <= add_data_3 * coeff4;
           mult_5 <= add_data_4 * coeff5;
//           mult_5 <= sample_4 * coeff5;
        end
   end

//==========================================================累加  
[email protected](posedge I_clk or posedge I_rst_p)
   begin
      if(I_rst_p)
         begin
            add_level_1 <= 'h0;
            add_level_2 <= 'h0;
            add_level_3 <= 'h0;
         end
      else
         begin
            add_level_1 <= mult_1 + mult_2;
            add_level_2 <= mult_3 + mult_4;
            add_level_3 <={mult_5[2*WIDTH],mult_5} ;
         end         
   end

assign data_out = add_level_1 + add_level_2 + add_level_3;
assign O_data = data_out[15:0];

endmodule

基本使用流水線結構,避免在關鍵路徑上演算法速度受影響。在學習使用Veriog來實現FIR的過程中,對於有符號數的乘法的掌握很重要。 add_data_4 <= {sample_4[WIDTH -1],sample_4} ,在需要擴位的時候,如果是負數,則最高位和中間位都為1;如果是整數,都為0;

5、測試和對比

使用MATLAB生成一個1khz+3kHz+4KHz的混合頻率訊號,寫入txt檔案。注意,輸入訊號定義為signed,如果MATLAB生成的資料為正數且資料較(最高位為1),在FPGA中會被認為是負數來出來,從而引發錯誤(因為這個問題博主用了一天來找bug).Matlab程式碼如下:

Fs = 10000; %採樣頻率決定了兩個正弦波點之間的間隔
N = 4096; %採樣點數
N1 = 0 : 1/Fs :N/Fs-1/Fs;
in =round((sin(1000*2*pi*N1) + sin(3000*2*pi*N1) + sin(4000*2*pi*N1))*20);
coeff =[-8,-10,20,74,102,74,20,-10,-8];
out =conv(in,coeff);%卷積濾波
%==========================================
fidc = fopen('E:\my_project\vivado_project\FIR\out.txt','wt');  %將結果寫入out.txt檔案,便於和modesim資料對比
for x = 1 :4104   
    %fprintf(fidc,'%s\n',num2str(out(x))/256);
    fprintf(fidc,'%d\n',(out(x)));
end
fclose(fidc);  

subplot(2,1,1);
plot(in);
xlabel('濾波前');
axis([0 200 -150 150]);

subplot(2,1,2);
plot(out);
xlabel('濾波後');
axis([100 200 -5000 5000]);

MATLAB模擬結果如下:

![這裡寫圖片描述](https://img-blog.csdn.net/20180811233136344?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FsYW5nYWl4aWFveGlhbw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 編寫Testbench讀取txt檔案對訊號濾波,使用$readmemb;
module tb_FIR(

    );
reg       I_clk;
reg [7:0] I_data;
reg       I_rst_p;
reg [7:0] mem[1:4096];

wire [15:0] O_data;
//wire [15:0] O_data;

reg  [12:0] addr;



FIR FIR_inst(
       .I_clk(I_clk),
       .I_data(I_data),
       .I_rst_p(I_rst_p),
       .O_data(O_data)
);

initial                                               
    begin 
      $readmemb("E:/my_project/vivado_project/FIR/mem.txt",mem);//將待濾波訊號讀入mem
      I_rst_p= 1;
      I_clk= 0;
      #50;
      I_rst_p= 0;
      #50000;
      $stop;
    end  
initial
       forever
          #10 I_clk = ~I_clk;
[email protected](posedge I_clk or posedge I_rst_p) 
      if(I_rst_p)                                
          I_data <= 8'b0 ;
       else
         I_data <= mem[addr];     

[email protected](posedge I_clk or posedge  I_rst_p) 
      if(I_rst_p)
         addr <= 12'd0;
       else
          addr <= addr + 1'd1;
endmodule

使用Vivado自帶的模擬工具模擬如下:


這裡寫圖片描述

對比MATLAB模擬的結果,兩者完全一樣,驗證了在FPGA中的FIR濾波器演算法。