FIR滤波器的FPGA实现

来源:互联网 发布:ubuntu原版和麒麟 编辑:程序博客网 时间:2024/05/29 12:17

1.FIR滤波器简介
FIR(Finite Impulse Response)滤波器:有限长单位冲激响应滤波器,又称为非递归型滤波器,是数字信号处理系统中最基本的元件,它可以在保证任意幅频特性的同时具有严格的线性相频特性,同时其单位抽样响应是有限长的,因而滤波器是稳定的系统。因此,FIR滤波器在通信、图像处理、模式识别等领域都有着广泛的应用。
2.并行FIR滤波器
根据传递函数H(Z)和FIR滤波器系数的对称性,可得FIR滤波器的一般实现结构,串型结构和并型结构。本文介绍并行结构的FIR,其结构框图如图1,并行滤波器的滤波速度快,一个时钟周期内完成一次滤波,但消耗大量的FPGA资源,如乘累加器,且器件的延迟较大,工作频率不宜太高。
这里写图片描述
图 1
3.FIR滤波器Matlab的实现
设计一个15阶(长度为16)的低通线性相位FIR滤波器,采用布莱克曼窗函数设计,截止频率为500Hz,采样频率为2000Hz,输入数据12比特量化,系数12比特量化,输出数据位宽为29比特。
文件:fir.m(产生滤波器的系数并量化)
function hn = fir
fs = 2000;%采样频率
fd = 500;%截止频率
Qb = 12;
N = 16;
% [n,wn,beta,ftype]=kaiserord(fd,mag,dev,Fs);
% b=fir1(n,wn,ftype,kaiser(n+1,beta));
w_kais=blackman(N)’;
b=fir1(N-1,fd*2/fs,w_kais);
Q_b = round(b/max(abs(b))*(2^(Qb-1)-1));
hn = Q_b;
文件:NoiseAndCarrier.m(产生200Hz和800Hz的正弦合成信号以及噪声,将数据量化后写入txt文件,作为信号源)
f1 = 200;%信号1200Hz
f2 = 800;%信号2800Hz
fs = 2000;%采样频率
Qb = 12;
t = 0:1/fs:1;
s1 = sin(2*pi*f1*t);%信号1
s2 = sin(2*pi*f2*t);%信号2
s = s1 + s2;%信号合成
Noise = randn(1,length(t));%噪声

%归一化
s = s/max(abs(s));
Noise = Noise/max(abs(Noise));

%量化
Q_s = round(s*(2^(Qb-1)-1));
Q_Noise = round(Noise*(2^(Qb-1)-1));

%通过滤波器
hn = fir
filter_s = filter(hn,1,s);
filter_noise = filter(hn,1,Noise);
%信号的幅频特性
m_s = 20*log(abs(fft(s,1024)))/log(10);m_s = m_s - max(m_s)
m_noise = 20*log(abs(fft(Noise,1024)))/log(10);m_noise = m_noise - max(m_noise);
%滤波后的幅频特性
Fm_s = 20*log(abs(fft(filter_s,1024)))/log(10);Fm_s = Fm_s - max(Fm_s);
Fm_noise = 20*log(abs(fft(filter_noise,1024)))/log(10);Fm_noise = Fm_noise - max(Fm_noise);
%滤波器的幅频特性
hn = 20*log(abs(fft(hn,1024)))/log(10);hn = hn - max(hn);

%画图
x_f =1:fs/length(m_s):fs/2;%横坐标,Hz
mf_s =m_s(1:length(x_f));
mf_noise = m_noise(1:length(x_f));
Fmf_s = Fm_s(1:length(x_f));
Fmf_noise =Fm_noise(1:length(x_f));
hn_f = hn(1:length(x_f));
subplot(211)
plot(x_f,mf_s,’-‘,x_f,Fmf_s,’-.’,x_f,hn_f,’–’);
xlabel(‘频率(Hz)’);ylabel(‘幅度(dB)’);
legend(‘输入信号’,’输出信号’,’滤波器’);grid on;
subplot(212)
plot(x_f,mf_noise,’-‘,x_f,Fmf_noise,’-.’,x_f,hn_f,’–’);
xlabel(‘频率(Hz)’);ylabel(‘幅度(dB)’);
legend(‘输入信号’,’输出信号’,’滤波器’);grid on;

%将数据以十进制的格式写入txt文件
fid =fopen(‘E:\Matlab\FPGA\fir_filter\int_s.txt’,’w’);
fprintf(fid,’%8d\r\n’,Q_s);
fprintf(fid,’;’);
fclose(fid);

fid =fopen(‘E:\Matlab\FPGA\fir_filter\int_noise.txt’,’w’);
fprintf(fid,’%8d\r\n’,Q_Noise);
fprintf(fid,’;’);
fclose(fid);

%将数据以二进制的格式写入txt文件
fid =fopen(‘E:\Matlab\FPGA\fir_filter\bin_s.txt’,’w’);
for i=1:length(Q_s)
s = dec2bin(Q_s(i)+(Q_s(i)<0)*2^Qb,Qb);
for j = 1:Qb
if(s(j) == ‘1’ )
tb = 1;
else
tb = 0;
end
fprintf(fid,’%d’,tb);
end
fprintf(fid,’\r\n’);
end
fprintf(fid,’;’);
fclose(fid);

fid =fopen(‘E:\Matlab\FPGA\fir_filter\bin_noise.txt’,’w’);
for i=1:length(Q_Noise)
s = dec2bin(Q_Noise(i)+(Q_Noise(i)<0)*2^Qb,Qb);
for j = 1:Qb
if(s(j) == ‘1’ )
tb = 1;
else
tb = 0;
end
fprintf(fid,’%d’,tb);
end
fprintf(fid,’\r\n’);
end
fprintf(fid,’;’);
fclose(fid);

执行结果如图2:

图 2
这里写图片描述

从图2(上)可以看出在200Hz和800Hz 有个尖峰,为正弦波信号,经过滤波器后800Hz的信号幅度被衰减了50dB,达到了滤波效果。

4.FPGA的实现

按照图1直接进行设计,设计后的RTL视图如图3,和图1差不多。
这里写图片描述
图 3
文件:FirParallel.v
/***************************************************************************************
模块名:FirParallel
功能:15阶低通线性相位FIR滤波器,采用布莱克曼窗函数设计,截止频率为500Hz,采样频率为2000Hz,系数量化为12bit
作者:冬瓜
时间:2015.9.6
Email:lidong10280528@163.com
****************************************************************************************/
module FirParallel
(
clk,rst_n,
xin,yout
);
input clk,rst_n;//时钟和复位信号,低电平复位
input signed [ 11:0 ]xin;//输入数据
output signed [ 28:0 ]yout;//滤波后的数据

//将数据存入移位寄存器中
reg signed [11:0]xin_reg[15:0];
reg [ 3:0 ]i,j;
always @( posedge clk or negedge rst_n )
if( !rst_n )
begin
for( i=0;i<15;i=i+1 )
xin_reg[ i+1 ] <= ‘d0;
end
else
begin
for( j=0;j<15;j=j+1 )
xin_reg[ j+1 ] <= xin_reg[ j ];

xin_reg[ 0 ] <= xin;
end

//将对称系数的输入数据相加
reg signed [ 12:0 ]add_reg[7:0];
always @( posedge clk or negedge rst_n )
if( !rst_n )
begin
for(i=0;i<8;i=i+1)
add_reg[ i ] <= ‘d0;
end
else
begin
for( i=0;i<8;i=i+1 )
add_reg[ i ] = { xin_reg[ i ][ 11 ],xin_reg[ i ] } + { xin_reg[ 15-i ][ 11 ],xin_reg[ 15-i ] };
end

wire signed [ 11:0 ]coe[ 7:0 ];//滤波器系数
wire signed [ 24:0 ]mout[ 7:0 ];//乘法器输出
assign coe[ 0 ] = 12’h0;
assign coe[ 1 ] = 12’hffd;
assign coe[ 2 ] = 12’h00f;
assign coe[ 3 ] = 12’h02e;
assign coe[ 4 ] = 12’hf8b;
assign coe[ 5 ] = 12’hef9;
assign coe[ 6 ] = 12’h24e;
assign coe[ 7 ] = 12’h7ff;

//实例化乘法器
//乘法器核设置如下:
//dataa:12bit
//datab:13bit
//有符号,流水线1级
mult u1 (
.clock ( clk ),
.dataa ( coe[ 0 ] ),
.datab ( add_reg[ 0 ] ),
.result ( mout[ 0 ] )
);
mult u2 (
.clock ( clk ),
.dataa ( coe[ 1 ] ),
.datab ( add_reg[ 1 ] ),
.result ( mout[ 1 ] )
);
mult u3 (
.clock ( clk ),
.dataa ( coe[ 2 ] ),
.datab ( add_reg[ 2 ] ),
.result ( mout[ 2 ] )
);
mult u4 (
.clock ( clk ),
.dataa ( coe[ 3 ] ),
.datab ( add_reg[ 3 ] ),
.result ( mout[ 3 ] )
);
mult u5 (
.clock ( clk ),
.dataa ( coe[ 4 ] ),
.datab ( add_reg[ 4 ] ),
.result ( mout[ 4 ] )
);
mult u6 (
.clock ( clk ),
.dataa ( coe[ 5 ] ),
.datab ( add_reg[ 5 ] ),
.result ( mout[ 5 ] )
);
mult u7 (
.clock ( clk ),
.dataa ( coe[ 6 ] ),
.datab ( add_reg[ 6 ] ),
.result ( mout[ 6 ] )
);
mult u8 (
.clock ( clk ),
.dataa ( coe[ 7 ] ),
.datab ( add_reg[ 7 ] ),
.result ( mout[ 7 ] )
);

//对乘法器输出的结果进行累加,并输出滤波后的数据
reg signed[ 28:0 ]sum;
reg signed[ 28:0 ]dout;
reg [ 3:0 ]k;
always @( posedge clk or negedge rst_n )
if( !rst_n )
begin
sum <= ‘d0;
dout <= ‘d0;
end
else
begin
dout <= sum;
sum = ‘d0;
for( k=0;k<8;k=k+1 )
sum = sum + mout[ k ];
end
assign yout = dout;
endmodule

激励文件:FirParalle.vt
`timescale 1 ps/ 1 ps
module FirParallel_vlg_tst();
// constants
// general purpose registers

reg clk;
reg rst_n;
reg [11:0] xin;
// wires
wire [28:0] yout;

// assign statements (if any)
FirParallel i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.rst_n(rst_n),
.xin(xin),
.yout(yout)
);
parameter clk_period =20;
parameter half_clk_period = clk_period/2;
parameter data_period = clk_period;
parameter half_data_period = data_period/2;
parameter data_num = 1000;
parameter sim_time = data_num*data_period;

//产生时钟和复位信号
initial
begin
clk = 1;
rst_n = 0;

400;

rst_n = 1;

sim_time $finish;

end
always #half_clk_period clk = ~clk;

//读取txt文件
reg [ 11:0 ]stimulus[ 1:data_num ];
integer i;
initial
begin
$readmemb(“bin_s.txt”,stimulus);
i = 0;
repeat(data_num)
begin
i = i + 1;
xin = stimulus[ i ];

data_period;

end
end

//将滤波后的数据写入txt文件
wire write_clk = clk & rst_n;//写时钟,复位不写数据
integer fid;
initial
begin
fid = fopen(filters,w);if(!fid)begindisplay(“cannot open the file!”);
finish;endendwiresigned[28:0]syout=yout;//always@(posedgewriteclk)fdisplay(fid,”%d”,s_yout);
endmodule

仿真结果如图4
这里写图片描述

                图 4

5.Matlab对结果分析
文件:FIRAnalysis.m
fs = 2000;

fid = fopen(‘E:\FPGA_Project\Modelsim\FirParallel\simulation\modelsim\filter_s.txt’,’r’);%读取通过Verilog编写的FIR滤波器后的数据
[FPGA_s_out N_FPGA_s]=fscanf(fid,’%lg’,inf);
fclose(fid);
fid = fopen(‘E:\FPGA_Project\Modelsim\FirParallel\simulation\modelsim\int_filter_s.txt’,’r’);%读取通过Matlba编写的FIR滤波器后的数据
[Matlab_s_out N_Matlab_s]=fscanf(fid,’%lg’,inf);
fclose(fid);

%归一化处理
FPGA_s_out = FPGA_s_out/max(abs(FPGA_s_out));
Matlab_s_out = Matlab_s_out/max(abs(Matlab_s_out));

FPGA_s = 20*log(abs(fft(FPGA_s_out,1024)))/log(10);
FPGA_s = FPGA_s - max( FPGA_s );

Matlab_s = 20*log(abs(fft(Matlab_s_out,1024)))/log(10);
Matlab_s = Matlab_s - max(Matlab_s);

x_f = 0:fs/length(FPGA_s):fs/2;
FPGA_s =FPGA_s(1:length(x_f));
Matlab_s =Matlab_s(1:length(x_f));

plot(x_f,FPGA_s,’-‘,x_f,Matlab_s,’–’);
xlabel(‘频率(Hz)’);ylabel(‘幅度(dB)’);
legend(‘通过FPGA滤波后’,’通过Matlab滤波后’);
grid on;
这里写图片描述
图 5
从图5看出,对比s(t)信号(200Hz和800Hz的正弦合成信号)通过FPGA的滤波以及通过Matlab的滤波,两者在200Hz和800Hz的幅度一样,虽然在其他频率点有误差,但在误差范围内。

0 0