【FPGA學習】MATLAB與FPGA實現FIR濾波器


本篇博客記錄一下在matlab設計和在FPGA平台實現FIR濾波器的方法,平台是Xilinx的ZYNQ

參考:
AMBA AXI-Stream Protocol Specification

使用matlab設計FIR濾波器

fdatool是matlab中專用的濾波器設計工具,在matlab中的命令行窗口直接輸入fdatool即可(也可以用filterDesigner):
fdatool
打開后的界面如下:
fdatool界面

  • 設置濾波器的參數,在界面的下半部
    • 響應類型:也就是濾波器類型
    • 設計方法:設計FIR濾波器和IIR濾波器的各種方法
    • 濾波器階數:定義濾波器的階數,可指定階數(對於n階濾波器,指定階數應填寫n-1)或直接根據選擇的濾波器類型使用最小階數
    • 頻率設定:定義頻帶的各參數
    • 幅值設定:定義幅值衰減的情況
    • 當采用窗函數設計時,還可以選擇可選的窗函數
      這個界面實際上是左邊的最下面的設計濾波器選項:
      設計濾波器
      由於我們要將FIR濾波器在FPGA的硬件上實現,因此還要在左邊的第三個選項選擇設置量化參數,並選擇定點數:
      設置量化參數
      定點數
      同時在分子字長還可以設定濾波器定點系數的長度

實例

本博客以一個示例來說明整個過程,濾波器要求如下:

  • FIR濾波器輸入采樣率為100kHz
  • 濾波器通帶的單邊帶帶寬為20kHz
  • 帶外衰減為90dB
  • 帶內波紋為1dB
  • 濾波器系數為定點數,ADC采樣信號(即輸入信號)的位數為24位,濾波器系數的位數可自己定義

輸入信號的頻譜如下所示:
信號頻譜

根據如上的要求,可在matlab中設計濾波器:
濾波器設計

設計完成后在上面的目標即可生成Xilinx系數文件(.coe文件):
生成.coe文件

調用FIR IP核

實現FIR濾波器可以直接調用提供的IP核,本篇博客中使用的是vivado中的FIR IP核
在工程左邊中點擊IP Catalog:
IP Catalog
搜索FIR即可找到需要調用的IP核:
FIR Compiler
首先需要配置濾波器的系數:
濾波器系數
接下來需要配置一下輸入的采樣頻率以及時鍾頻率
頻率
還需要修改一些輸入數據的位數等參數:
輸入數據位數
在Interface中還可以看到IP核的復位信號和使能信號,我這里將復位信號改為了低電平有效:
復位和使能

在vivado中生成IP核后,可以在IP Sources中的Instantiation Template中獲取例化IP核的模板,比如在.veo中就是可以獲取Verilog的例化模板:
.veo
可以直接根據這個進行例化

在編寫Verilog代碼之前需要對IP核有所了解,這個一方面要看Xilinx官方的IP手冊,FIR IP核對應的是PG149,另一方面可以在IP核配置界面的Implementation Details可以看到一些信息,尤其是確定接口信號的位數:
Implementation Details

接口控制模塊編寫如下:

// Xilinx FIR IPcore Interface Control
module fir_control(
    input sys_clk,      // 50M clock
    input sys_rst_n,

    input [23:0] s_axis_data_tdata,
    input s_axis_data_tvalid,
    output m_axis_data_tvalid,
    output s_axis_data_tready,
    output [41:0] fir_out,
    output reg clk_100k
    );

parameter count = 250;

wire [47:0] m_axis_data_tdata;
reg [15:0] cnt;
// reg clk_100k;
assign fir_out = m_axis_data_tdata[41:0];

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
    begin
        cnt <= 16'd0;
        clk_100k <= 1'b0;
    end
    else if(cnt < count - 1)
    begin
        cnt <= cnt + 1;
    end
    else
    begin
        cnt <= 16'd0;
        clk_100k <= ~clk_100k;
    end
end


fir_compiler_0 fir (
  .aresetn(sys_rst_n),                        // input wire aresetn
  .aclk(clk_100k),                              // input wire aclk
  .s_axis_data_tvalid(s_axis_data_tvalid),  // input wire s_axis_data_tvalid
  .s_axis_data_tready(s_axis_data_tready),  // output wire s_axis_data_tready
  .s_axis_data_tdata(s_axis_data_tdata),    // input wire [23 : 0] s_axis_data_tdata
  .m_axis_data_tvalid(m_axis_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_data_tdata)    // output wire [47 : 0] m_axis_data_tdata
);

endmodule

同時還編寫了testbench用於測試,testbench一方面要讀取輸入信號數據,另一方面要將輸出信號輸出到一個文件里,在matlab進行測試,這些都要用到Verilog語法中的系統任務

  • 使用數據文件對存儲器進行初始化:\(readmemb和\)readmemh,分別可以用於讀取二進制數和十六進制數
    • $readmemb("<file_name>",<memory_name>,<start_addr>,<finish_addr>);
    • <start_addr>和<finish_addr>是可選的,<start_addr>的默認值是存儲器數組的開始位置,<finish_addr>的默認值是數據文件或存儲器的結束位置
  • 可以將Verilog的輸出重定向到選擇的文件
    • 打開文件:$fopen
      • <file_handle>=$fopen("<file_name>");
      • \(fopen返回一個被稱為**多通道描述符**的**32位**值,多通道描述符中只有一位被設置成1,標准輸出有一個多通道描述符,其最低位被設置成1,標准輸出也稱通道0,標准輸出是一直開放的,以后對\)fopen的每一次調用都會打開一個新的通道,並返回一個32位的描述符,其中最多可設置到第30位,第31位是保留位
    • 寫文件:\(fdisplay、\)fmonitor和$fwrite
      • $fdisplay(<file_descripr or>,p1,p2,...,pn);
      • $fmonitor(<file_descripr or>,p1,p2,...,pn);
      • p1,p2,...,pn可以是變量,信號名或帶引號的字符串
    • 關閉文件:$fclose
      • $fclose(<file_descriptor>);
      • 文件一旦被關閉就不能再寫入,多通道描述符中的相應位被設置成0,下一次$fopen的調用可以重用這一位

testbench編寫如下:

module fir_tp;

// fir_control Parameters
parameter PERIOD = 20 ;
parameter count  = 250;

// fir_control Inputs
reg   sys_clk                              = 0 ;
reg   sys_rst_n                            = 0 ;
reg   [23:0]  s_axis_data_tdata            = 0 ;
reg   s_axis_data_tvalid                   = 0 ;

// fir_control Outputs
wire  m_axis_data_tvalid                   ;
wire  s_axis_data_tready                   ;
wire  [41:0]  fir_out                      ;

reg [23:0] data_test [1023:0];
reg [31:0] cnt;

wire clk_100k;
integer file;
reg [41:0] fir_out_buf = 0;

initial
begin
    #10 sys_rst_n = 1;
    s_axis_data_tdata = 24'd0;
    cnt <= 32'd0;
    $readmemh("C:/Users/zhaoyuheng/Desktop/ASIC_experiment/input.txt",data_test);
    s_axis_data_tvalid = 1;
    file = $fopen("C:/Users/zhaoyuheng/Desktop/ASIC_experiment/output.txt","w");
    forever #(PERIOD/2)  sys_clk=~sys_clk;
end

fir_control #(
    .count ( count ))
 u_fir_control (
    .sys_clk                 ( sys_clk                    ),
    .sys_rst_n               ( sys_rst_n                  ),
    .s_axis_data_tdata       ( s_axis_data_tdata   [23:0] ),
    .s_axis_data_tvalid      ( s_axis_data_tvalid         ),

    .m_axis_data_tvalid      ( m_axis_data_tvalid         ),
    .s_axis_data_tready      ( s_axis_data_tready         ),
    .fir_out                 ( fir_out             [41:0] ),
    .clk_100k               (clk_100k)
);


always @(posedge clk_100k) begin
    if(cnt == 1024 - 1)
    begin
        $stop;
    end
    else
    begin
        if(s_axis_data_tready == 1 && m_axis_data_tvalid == 1)
        begin
            cnt <= cnt + 1;
            s_axis_data_tdata <= data_test[cnt];
            fir_out_buf <= fir_out;
            $fwrite(file,"%b\n",fir_out_buf);
        end

    end
end


endmodule

在matlab中驗證

matlab中讀取txt文件:

cstr=textread('C:\Users\zhaoyuheng\Desktop\input.txt','%s');
n=length(cstr);
d = zeros(n,1);
for i=1:n
   d(i)=hex2dec(cstr{i});     % 16進制數轉換為10進制數,如果要將2進制數轉換為10進制數要用bin2dec函數
end
plot(abs(fft(d)));

load函數只能讀取10進制數

利用Verilog實現FIR濾波器

除了直接使用FIR的IP核外,還可以直接用Verilog語言來實現FIR濾波器,這里就用一個實例說明
我使用了正點原子的新起點FPGA開發板以及高速ADDA模塊,實現一個簡單的電壓采集系統,實現輸入16kHz與17kHz信號的分離,輸出信號直接使用ADI的AD9834,系統如下:

高速ADDA

正點原子的高速ADDA模塊的硬件結構圖如下:
高速ADDA硬件結構

  • AD9708芯片輸出的是一對差分電流信號,經過處理后輸出的模擬電壓范圍是-5V~5V
  • AD9280芯片的輸入模擬電壓轉換范圍是0V2V,因此電壓輸入端從-5V5V衰減到0V~2V之間

AD9708

AD9708是8位的電流輸出型DAC,速率可以達到125MSPS,供電電壓范圍為2.7V-5.5V,其時序圖如下:
AD9708時序圖
其中,ts是輸入建立時間,tH是保持時間

FIR的Verilog實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM