利用FPGA实现全串行低通FIR滤波器
设计一个15阶(长度为16)的具有线性相位低通FIR滤波器,采用布拉克曼窗函数设计,截止频率为500HZ,抽样频率为2000HZ;采用FPGA实现全串行FIR滤波器,系数的量化位数为12比特,输入数据位数为12比特,输出数据位数为29比特,系统时钟为16KHZ
设计思路:首先采用MATLAB根据要求设计出滤波器系数,并仿真出系数量化前后的幅频响应曲线;根据图4-17所示的结构采用Verilog HDL语言再FPGA中实现该滤波器;采用MATLAB仿真出具有白噪声特性的输入信号,以及由200HZ及800HZ单频信号合成的输入信号;将仿真的输入信号作为Verilog HDL测试输入信号,用MOdelSim仿真滤波器输出信号。采用MATLAB分析滤波器输出信号,比较输入输出信号的时域及频域图。
1.采用MATLAB设计出滤波器系数
function Q12 = My_E4_7_fir8
%设置参数
N = 15; %N阶 长度为N+1 滤波器
fc = 500; %截止频率为500HZ
fs = 2000; %抽样频率为2000HZ
wn = fc/(fs/2); %归一化截止频率
ftype = 'low'; %滤波器类型
window = blackman(N+1)'; %布拉克曼窗函数 与滤波器长度一样
B = 12; %量化位数
%用firl函数设计低通滤波器
b = fir1(N,wn,ftype,window);
Q12 = round(b/max(abs(b))*(2^(B-1)-1)); %12比特量化
%求其幅度响应
m_blcak = 20*log(abs(fft(b,1024)))/log(10);
m12_black = 20*log(abs(fft(Q12,1024)))/log(10);
%保证最大值相同(都为零),好进行比较
m_blcak = m_blcak - max(m_blcak);
m12_black = m12_black - max(m12_black);
%figure(2)
%x_f = [0:fs/length(m_blcak):fs-fs/length(m_blcak)];
%plot(x_f,m_blcak,'-',x_f,m12_black,'--');
%xlabel('频率(Hz)');ylabel('幅度(dB)');
%legend('未量化','12bit量化');
%grid;
%figure(3)
%x_f_2 = [0:fs/length(m_blcak):fs/2];
%plot(x_f_2,m_blcak(1:length(x_f_2)),'-',x_f_2,m12_black(1:length(x_f_2)),'--');
%xlabel('频率(HZ)');ylabel('幅度(dB)');
%legend('未量化','12bit量化');
%grid;
2.采用MATLAB仿真滤波器测试数据及滤波后的输出数据
采用MATLAB仿真滤波器测试数据、仿真测试数据经滤波器滤波后的输出数据,以便与FPGA实现后的结果进行比较,并判断FPGA实现的正确性。首先需要仿真生成12比特量化的、抽样频率为2000HZ的高斯噪声,以及两个频率分别为200HZ与800HZ信号的合成信号,并将仿真的数据转化成二进制数据写入文本文件中,供FPGA仿真程序读取;然后,仿真出量化后的数据经滤波器滤波后的输出结果。
%定义参数
fs = 2000; %抽样频率
f1 = 200; %信号1频率
f2 = 800; %信号2频率
N = 12; %量化位数为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)); %生成均值为0,方差为1,大小为[1,length(t)]的高斯噪声
%归一化处理
s = s/max(abs(s));
noise = noise/max(abs(noise));
%12比特量化
s_12 = round(s*(2^(N-1)-1)); %-2^11——2^11
noise_12 = round(noise*(2^(N-1)-1));
%调用自己设计的滤波器对信号进行滤波
Q12=My_E4_7_fir8;
Filter_noise=filter(Q12,1,noise_12); %由于是filter滤波器,所以b=Q12,a=1
Filter_s=filter(Q12,1,s_12);
%求信号的幅频响应
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);
%求滤波后信号的幅频响应
m_filter_s = 20*log(abs(fft(Filter_s,1024)))/log(10); m_filter_s = m_filter_s - max(m_filter_s);
m_filte_noise = 20*log(abs(fft(Filter_noise,1024)))/log(10); m_filte_noise = m_filte_noise - max(m_filte_noise);
%求滤波器的幅频响应
m_Q12 = 20*log(abs(fft(Q12,1024)))/log(10); m_Q12 = m_Q12 - max(m_Q12);
%设置幅频响应的横坐标单位为Hz
x_f = 0:fs/length(m_s):fs/2;
%只显示正频率部分的幅频响应
m_s = m_s(1:length(x_f));
m_Q12 = m_Q12(1:length(x_f));
m_noise = m_noise(1:length(x_f));
m_filter_s = m_filter_s(1:length(x_f));
m_filte_noise = m_filte_noise(1:length(x_f));
%画幅频响应曲线
figure(2)
subplot(211)
plot(x_f,m_s,'-.',x_f,m_filter_s,'-',x_f,m_Q12,'--');
xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真合成单频信号滤波前后的频谱');
legend('输入信号频谱','滤波后的频谱','滤波器频谱');
grid;
subplot(212)
plot(x_f,m_noise,'-.',x_f,m_filte_noise,'-',x_f,m_Q12,'--');
xlabel('频率(HZ)');ylabel('幅度(dB)');title('Matlab仿真白噪声信号滤波前后的频谱');
legend('输入信号频谱','滤波后的频谱','滤波器频谱');
grid;
%将生成的数据以十进制数据格式写入txt文件中
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_noise_12.txt','w');
fprintf(fid,'%8d\r\n',noise_12);
fprintf(fid,';');
fclose(fid);
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Int_s_12.txt','w');
fprintf(fid,'%8d\r\n',s_12);
fprintf(fid,';');
fclose(fid);
%将生成的数据以二进制数据格式写入txt文件中
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_noise_12.txt','w');
for i=1:length(noise_12)
B_noise=dec2bin(noise_12(i)+(noise_12(i)<0)*2^N,N); %补码形式
for j=1:N
if B_noise(j)=='1'
tb=1;
else
tb=0;
end
fprintf(fid,'%d',tb);
end
fprintf(fid,'\r\n');
end
fprintf(fid,';');
fclose(fid);
fid=fopen('D:\Work_directory\MATLAB\Digital_filter_MATLAB\Chapter_4\E4_7\E4_7_Bin_s_12.txt','w');
for i=1:length(s_12)
B_s=dec2bin(s_12(i)+(s_12(i)<0)*2^N,N)
for j=1:N
if B_s(j)=='1'
tb=1;
else
tb=0;
end
fprintf(fid,'%d',tb);
end
fprintf(fid,'\r\n');
end
fprintf(fid,';');
fclose(fid);
不难发现,800HZ频率的信号几乎被滤掉了。
3.采用Verilog HDL语言设计全串行低通FIR滤波器
`文章来源:https://www.toymoban.com/news/detail-723777.html
module My_FirlFullSerial(
//滤波器系数为12位量化 输入数据也是12位 且都是有符号数 都是以补码形式输入(计算机中数都是以补码形式存在并进行加减)
//使用到的是15阶FIR滤波器 长度为N=16
//由FIR滤波器计算公式 最终输出的数据位宽 > (16*2^12)*2^12 = 2^(4+12+12) = 2^28
//所以滤波后的数据保险起见可以为29位
input clk , //时钟信号 时钟频率为16KHZ
input rst , //复位信号
input signed[11:0] Xin, //输入数据 数据输入频率为2KHZ
output signed[28:0] Yout //滤波后的输出数据
);
//实例化有符号数乘法器IP核 mult
reg signed [11:0] coe; //滤波器系数
wire signed [12:0] add_s; //输入数据中 对称数据的和 第n个和最后N-n个
wire signed [24:0] Mout; //两项乘积数的和
mult Umult( //乘法器IP核
.clock(clk), //时钟信号
.dataa(coe),
.datab(add_s),
.result(Mout)
);
//实例化有符号数加法器IP核,对输入数据进行1比特符号位扩展,输出结果为13比特数据
//由于结果为13比特 ,所以把输入数据都扩展为了13位
reg signed [12:0] add_a;
reg signed [12:0] add_b;
adder Uadder( //加法器IP核
.dataa(add_a),
.datab(add_b),
.result(add_s)
);
//时钟计数器 每计数八次为一次循环
reg [2:0] count;
always @(posedge clk or negedge rst)
if(!rst)
count <= 3'd0;
else count <= count + 1'd1;
//给每一个数据寄存器赋值 共16个
reg [11:0] Xin_reg[15:0]; //输入数据寄存器
reg [3:0] i,j;
always @(posedge clk or negedge rst)
if(!rst) begin //复位时每个寄存器都清零
for(i=0;i<15;i=i+1)
Xin_reg[i] <= 12'd0;
end
else if(count == 3'd7) begin //当一次循环结束时 开始加载下一个数据 并所有数据右移
for(j=0;j<15;j=j+1)
Xin_reg[j+1] <= Xin_reg[j];
Xin_reg[0] <= Xin;
end
else ;
//将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
//需要注意的是,下面程序只用了了一个加法器及一个乘法器
//以8倍率的速率调用乘法器IP核,由于滤波器长度为16比特,系数具有一定的对称性
//故可在一个数据周期内完成所有8个滤波器系数与数据的乘法运算
//为了保证加法运算不溢出,输入、输出数据均扩展为13比特
always @(posedge clk or negedge rst)
if(!rst) begin
coe <= 12'd0;
add_a <= 13'd0;
add_b <= 13'd0;
end
else if(count == 3'd0) begin
coe <= 12'd0; //第一个滤波器及最后一个滤波器系数的值
add_a <= {Xin_reg[0][11],Xin_reg[0]}; //第一个滤波器系数对应的输入值 并将12位的输入数据扩展为13位
add_b <= {Xin_reg[15][11],Xin_reg[15]}; //若最高位为1 则补1 最高位为零 则补零 总之就是补高位的值
end
else if(count == 3'd1) begin
coe <= 12'hffd; //c1
add_a <= {Xin_reg[1][11],Xin_reg[1]};
add_b <= {Xin_reg[14][11],Xin_reg[14]};
end
else if(count == 3'd2) begin
coe <= 12'h00f;
add_a <= {Xin_reg[2][11],Xin_reg[2]};
add_b <= {Xin_reg[13][11],Xin_reg[13]};
end
else if(count == 3'd3) begin
coe <= 12'h02e;
add_a <= {Xin_reg[3][11],Xin_reg[3]};
add_b <= {Xin_reg[12][11],Xin_reg[12]};
end
else if(count == 3'd4) begin
coe <= 12'hf8b;
add_a <= {Xin_reg[4][11],Xin_reg[4]};
add_b <= {Xin_reg[11][11],Xin_reg[11]};
end
else if(count == 3'd5) begin
coe <= 12'hef9;
add_a <= {Xin_reg[5][11],Xin_reg[5]};
add_b <= {Xin_reg[10][11],Xin_reg[10]};
end
else if(count == 3'd6) begin
coe <= 12'h24e;
add_a <= {Xin_reg[6][11],Xin_reg[6]};
add_b <= {Xin_reg[9][11],Xin_reg[9]};
end
else begin
coe <= 12'h7ff;
add_a <= {Xin_reg[7][11],Xin_reg[7]};
add_b <= {Xin_reg[8][11],Xin_reg[8]};
end
//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
//考虑到乘法器及累加器的延时,需要在计数器count=2时对累计器清零,同时输出滤波器结果
//类似的延时长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看
reg signed [28:0] sum;
reg signed [28:0] yout;
always @(posedge clk or negedge rst)
if(!rst) begin
sum <= 29'd0;
yout <= 29'd0;
end
else if(count == 2 ) begin //计数到2的时候 说明一次循环结束
yout <= sum; //将累加值保存到yout变量中
sum = 29'd0; //累加值重新清零
sum = sum + Mout;
end
else sum = sum + Mout;
assign Yout = yout;
endmodule
4.编写Verilog HDL测试激励文件
`timescale 1 ns/ 1 ns //设置仿真时间单位:ns
module FirFullSerial_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg [11:0] Xin;
reg clk;
reg rst;
wire clk_data; //数据时钟,速率为系统时钟clk的1/8;
// wires
wire [28:0] Yout;
// assign statements (if any)
FirFullSerial i1 (
// port map - connection between master ports and signals/registers
.Xin(Xin),
.Yout(Yout),
.clk(clk),
.rst(rst)
);
parameter clk_period=62500; //设置时钟信号周期(频率):16kHz
parameter clk_period_data=clk_period*8;
parameter clk_half_period=clk_period/2;
parameter clk_half_period_data=clk_half_period*8;
parameter data_num=2000; //仿真数据长度
parameter time_sim=data_num*clk_period; //仿真时间
initial
begin
//设置时钟信号初值
clk=1;
//clk_data=1;
//设置复位信号
rst=1;
#200000 rst=0;
//设置仿真时间
#time_sim $finish;
//设置输入信号初值
Xin=12'd10;
end
//产生时钟信号
always
#clk_half_period clk=~clk;
reg [2:0] cn_clk=3'd0;
always @(posedge clk) cn_clk <= cn_clk + 3'd1;
assign clk_data = cn_clk[2];
//从外部TX文件(SinIn.txt)读入数据作为测试激励
integer Pattern;
reg [11:0] stimulus[1:data_num];
initial
begin
//文件必须放置在"工程目录\simulation\modelsim"路径下
//readmemb("E4_7_Bin_noise.txt",stimulus);
$readmemb("E4_7_Bin_s.txt",stimulus);
Pattern=0;
repeat(data_num)
begin
@(posedge clk_data);
Pattern=Pattern+1;
Xin=stimulus[Pattern];
end
end
//将仿真数据dout写入外部TXT文件中(out.txt)
integer file_out;
initial
begin
//文件放置在"工程目录\simulation\modelsim"路径下
//file_out = fopen("E4_7_Noiseout.txt");
file_out = fopen("E4_7_Sout.txt");
if(!file_out)
begin
display("could not open file!");
finish;
end
end
wire rst_write;
wire signed [28:0] dout_s;
assign dout_s = Yout; //将dout转换成有符号数据
assign rst_write = clk_data & (!rst);//产生写入时钟信号,复位状态时不写入数据
always @(posedge rst_write )
$fdisplay(file_out,"%d",dout_s);
endmodule
5.利用Modelsim对FPGA程序进行功能和时序仿真
6. 编写MATLAB程序,分析FPGA中输出数据是否与MATLAB仿真中相同
7.总结
看完书本这部分内容,几乎都自己重新写了一遍,算是大概了解了这个流程。虽然由于自身技术原因,利用FPGA仿真那部分操作出了点问题,现在还不怎么明白,所以5、6节我没加上去内容。总体来说还可以文章来源地址https://www.toymoban.com/news/detail-723777.html
到了这里,关于利用FPGA实现全串行低通FIR滤波器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!