前言
本文介紹了設計濾波器的FPGA實現步驟,并結合杜勇老師的書籍中的串行FIR濾波器部分進行一步步實現硬件設計,對書中的架構做了簡單的優化,并進行了仿真驗證。
FIR濾波器的FPGA實現步驟
從工程角度分析FIR濾波器的FPGA實現步驟如下:
- 分析設計需求,根據設計需求確定FIR濾波器的仿真算法設計。
- 編寫仿真代碼或利用工具生成相關設計文件(包括但不局限與c++、MATLAB、python等語言或者相關濾波器設計工具)
- 量化濾波器系數,防止運算時數據溢出造成錯誤。
- 根據實際工程需求確定硬件實現架構并編寫代碼。
量化濾波器系數的影響
量化位數對濾波器的阻帶紋波有較大的影響,且量化位數越高,則影響越小。下面給出兩個之前設計的FIR IP工程中量化效果的截圖,從圖中可以很清楚看出,當量化位數不夠,也就是量化精度不夠時,對阻帶影響較大,使用量化效果不好的濾波器可能造成濾波效果不能達到預期效果。
量化精度不夠
正常量化
串行FIR濾波器FPGA實現
FIR濾波器的結構形式時,介紹了直接型、級聯型、頻率取樣型和快速卷積型4種。在FPGA實現時,最常用的是最簡單的直接型結構。FPGA實現直接型結構的FIR濾波器,可以采用串行結構、并行結構等不同中的結構設計,本節主要介紹在vivado環境下進行串行FIR濾波器設計的結構實現,同樣仿造杜勇老師的《數字濾波器的MATLAB與FPGA實現》的書中的設計需求去一步步搭建工程并實現。
實現串行FIR濾波器濾波器需求
設計一個15階(長度為16)的低通線性相位FIR濾波器,采用窗函數設計,截止頻率為500 Hz,采樣頻率為2 000 Hz;采用FPGA實現全串行結構的濾波器,系數的量化位數為12比特,輸入數據位寬為12比特,輸出數據位寬為29比特,系統時鐘為16 kHz。
濾波器系數確定與量化
確定濾波器的結構后,就根據濾波器進行設計代碼仿真,這里引用書中的仿真設計,并將濾波器參數系數量化。 確定濾波器系數的方法有很多,可以使用MATLAB中豐富的函數實現,或者使用相關濾波器設計的軟件工具,定制滿足當前需求的窗函數的濾波器系數。
N=16; %濾波器長度
fs=2000; %采樣頻率
fc=500; %低通濾波器的截止頻率
B=12; %量化位數
%生成各種窗函數
w_kais=blackman(N)';
%采用fir1函數設計FIR濾波器
b_kais=fir1(N-1,fc*2/fs,w_kais);
%量化濾波器系數
Q_kais=round(b_kais/max(abs(b_kais))*(2^(B-1)-1))
hn=Q_kais;
%轉化成16進制數補碼
Q_h=dec2hex(Q_kais+2^B*(Q_kais<0))
%求濾波器的幅頻響應
m_kais=20*log(abs(fft(b_kais,1024)))/log(10); m_kais=m_kais-max(m_kais);
Q_kais=20*log(abs(fft(Q_kais,1024)))/log(10); Q_kais=Q_kais-max(Q_kais);
%設置幅頻響應的橫坐標單位為Hz
x_f=[0:(fs/length(m_kais)):fs/2];
%只顯示正頻率部分的幅頻響應
m5=m_kais(1:length(x_f));
m6=Q_kais(1:length(x_f));
%繪制幅頻響應曲線
plot(x_f,m5,'-',x_f,m6,'--');
xlabel('頻率(Hz)');ylabel('幅度(dB)');
legend('未量化','12bit量化');
grid;
硬件架構
下圖為杜勇老師的《數字濾波器的MATLAB與FPGA實現》實現的串行FIR濾波器的結構圖。 因為FIR濾波器參數對稱,所以同時計算相應的對稱結構的值,所以針對長度為16的濾波器只需要計算8次即可出結果,圖中的8個時鐘周期可以替換成N/2;這樣就得到了一個通用化的串行FIR濾波器結構圖。
串行FIR濾波器結構
串行實現FIR濾波器,可以節約加法器資源,同時犧牲了整個濾波器實現的性能,缺點也就很明顯了,當濾波器的系數長度N增大時,該數據吞吐的速率也將對應變成1/N。
根據架構描述電路
杜勇老師書中提供的代碼相當繁瑣,而且不具有通用化的使用價值(串行FIR使用價值確實不大,可能只用于學習),我根據上述的硬件設計的架構對代碼進行了重寫配置,使得代碼更具有通用意義,可根據參數輸入來適配不同濾波器長度的設計。
實現模塊框圖
接口描述如下:
接口描述
參數描述如下:
參數描述
代碼如下:
`timescale 1ns / 1ps
module Fir_FullSerial(
input clk,//!系統時鐘
input rst,//!復位信號
input signed [SIGN_IN_WIDTH-1:0] signal_in,//!信號輸入
output signed [SIGN_OUT_WIDTH-1:0] signal_out//!信號輸出,信號輸出速度為CLK/FIR_COE_NUM
);
//
parameter integer SIGN_IN_WIDTH = 12 ;//!信號輸入位寬
parameter integer SIGN_OUT_WIDTH = 29 ;//!信號輸出位寬
parameter integer FIR_COE_WIDTH = 12 ;//!濾波器系數位寬
parameter integer FIR_COE_NUM = 16 ;//!濾波器長度
localparam integer FIR_WIDTH_DIV_2 = FIR_COE_NUM/2 ;
function [FIR_COE_WIDTH-1:0] coe_data;
input [FIR_WIDTH_DIV_2-1:0] index;
begin
case(index)
'd0:coe_data='h000;
'd1:coe_data='hffd;
'd2:coe_data='h00f;
'd3:coe_data='h02e;
'd4:coe_data='hf8b;
'd5:coe_data='hef9;
'd6:coe_data='h24e;
'd7:coe_data='h7ff;
endcase
end
endfunction
//!寄存輸入信號
reg [SIGN_IN_WIDTH-1:0] Sign_in_Reg[FIR_COE_NUM-1:0];
//!計數器邏輯
reg [FIR_WIDTH_DIV_2-1:0] cnt;
always @(posedge clk ) begin
if (rst=='b1) begin
cnt<='d0;
end
else begin
if (cnt==FIR_WIDTH_DIV_2-'b1) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end
//將數據存入移位寄存器sign_in_Reg中
integer i;
always @(posedge clk)begin
if (rst=='b1)begin
//初始化寄存器值為0
for (i=0; i
其中,代碼增加了一個信號輸出標志,該標志信號為傳輸8次數據后延時三拍的數據,為什么是三拍? 因為讀取信號后首先做了一級位寬拓展,第二級做了乘加運算,第三級為累加輸出。 所以輸出信號相比傳輸數據完成的位置延遲三拍。
針對乘累加運算,這里沒有使用IP,但是為了加速信號傳輸該信號的運算使用dsp48,所以在信號聲明時前面加了(*use_dsp48="yes"*)
。
仿真數據設計
為了驗證串行設計代碼的正確性。 這里使用MATLAB腳本產生了一個混頻信號,然后將混頻信號進行量化處理并導出txt文件以供仿真文件讀取。
clc;close all;clear all;
Fs = 2000; %采樣頻率
N = 2^10; %采樣點數
f1=300; %正弦波1頻率
f2=400; %正弦波1頻率
t=[0:N-1]/Fs; %時間序列
s1 = sin(2*pi*f1*t) ;
s2 = sin(2*pi*f2*t) ;
s = s1 .* s2;
figure(1);
subplot(1,2,1);
plot(t,s,'r','LineWidth',1.2);
title('時域波形');
axis([0,100/Fs,-3,3]);
set(gca,'LineWidth',1.2);
%轉化為位寬12bit數據
s_12bit=s./max(s).*(2.^11 - 1); % DA輸入波形,量化到16bit
s_12bit(find(s_12bit<0) ) = s_12bit(find(s_12bit<0) ) + 2^12 - 1;
s_12bit = fix(s_12bit);
s_12bit = dec2hex(s_12bit);
% %生成文件
fid= fopen('sin_data.txt','w+');
%生成十六進制
for i=1:N
fprintf(fid,'%s',s_12bit(i,:));
fprintf(fid,'\\r\\n');
end
fclose(fid);
%% 設計驗證
N=16; %濾波器長度
fs=2000; %采樣頻率
fc=500; %低通濾波器的截止頻率
B=12; %量化位數
%生成各種窗函數
w_kais=blackman(N)';
%采用fir1函數設計FIR濾波器
b_kais=fir1(N-1,fc*2/fs,w_kais);
ss=conv(b_kais,s);
subplot(1,2,2);
plot(t(20:1000),ss(20:1000));
title('濾波后信號');
axis([0,100/Fs,-1,1]);
set(gca,'LineWidth',1.2);
運行仿真后,根據設計的濾波器系數進行仿真,發現可以正常濾波除去高頻分量。
濾波仿真效果
仿真激勵文件編寫
`timescale 1ns / 1ps
module Fir_FullSerial_tb;
// Parameters
localparam integer SIGN_IN_WIDTH = 12;
localparam integer SIGN_OUT_WIDTH = 29;
localparam integer FIR_COE_WIDTH = 12;
localparam integer FIR_COE_NUM = 16;
// Ports
reg clk = 1;
reg rst = 1;
reg [SIGN_IN_WIDTH-1:0] signal_in;
wire [SIGN_OUT_WIDTH-1:0] signal_out;
Fir_FullSerial #(
.SIGN_IN_WIDTH(SIGN_IN_WIDTH ),
.SIGN_OUT_WIDTH(SIGN_OUT_WIDTH ),
.FIR_COE_WIDTH(FIR_COE_WIDTH ),
.FIR_COE_NUM (FIR_COE_NUM )
)Fir_FullSerial_dut (
.clk (clk ),
.rst (rst ),
.signal_in (signal_in ),
.signal_out ( signal_out)
);
reg [11:0] mem [0:99];
reg [9:0] addr ;
reg [11:0]data_out ;
always #(10*8)
begin
if(rst==0)
addr = addr + 10'd1;
signal_in = mem[addr][11:0];
end
always
#5 clk = ! clk ;
initial
begin
signal_in =0;
$readmemh("sin_data.txt",mem);
addr = 10'd0;
#10;
rst = 0;
end
endmodule
運行仿真,查看波形可見,濾波效果和仿真結果一致。
仿真波形
關于之前提到的延遲三拍的問題可以在波形輸出這里查看,7ff為濾波器系數上次運算的最后一個數據,此數據運算結果在下一拍,乘加運算的結果為0,下一拍進行累加輸出給sign_out輸出。
延遲分析
-
FPGA
+關注
關注
1629文章
21736瀏覽量
603434 -
matlab
+關注
關注
185文章
2976瀏覽量
230484 -
濾波器
+關注
關注
161文章
7817瀏覽量
178140 -
FIR
+關注
關注
4文章
146瀏覽量
33174 -
python
+關注
關注
56文章
4797瀏覽量
84693
發布評論請先 登錄
相關推薦
評論