书接上回
三、并行结构的FPGA实现设计实例1、matlab参数与数据2、使用Verilog编写并行结构的FIR滤波器3、使用matlab将产生的程序进行仿真验证三、并行结构的FPGA实现
并行结构,并行实现滤波器的累加运算,即并行将具有对称系数的输入数据进行相加,而后采用多个乘法器并行实现系数与数据的乘法运算,最后将所有乘积结果相加输出。这种结构具有最高的运行速度,因不需要累加运算,因此系数时钟频率可以与数据输出时钟频率保持一致。
与串行结构相比,更高的速度付出的是成倍的硬件资源的代价。
设计实例
设计一个15阶的低通线性相位FIR滤波器,采用布莱克曼窗函数设计,截止频率为500Hz,采样频率为2000Hz;采用FPGA实现并行结构的滤波器,系数的量化位数为12bit,输入数据位宽为12bit,输出数据位宽为29bit,系统时钟2000Hz。
1、matlab参数与数据
FIR滤波器参数与串行结构实现的完全相同,请参照前一篇文章
链接: 基于FPGA的FIR滤波器的实现(4)— 串行结构FIR滤波器的FPGA代码实现
2、使用Verilog编写并行结构的FIR滤波器
RTL代码(需要先将matlab产生的noise_B和sin_B添加到工程目录下的simulation/modelsim文件夹中)module FirParallel(input wire clk,input wire 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)beginfor(i=0;i<15;i=i+1)Xin_reg[i] = 12'd0;endelsebegin//与串行结构不同,此处不用判断计数器状态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)beginfor(i=0;i<8;i=i+1)Add_reg[i] = 13'd0;endelsebeginfor(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//与串行结构不同,另外需要实例化8个乘法器IP核//实例化有符号数乘法器IP核multwire signed[11:0]coe[7:0];//滤波器为12bit量化数据wire signed[24:0]Mout[7:0];//乘法器输出为25bit数据assign coe[0] = 12'h000;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;mult mult_inst0(.clock(clk),.dataa(coe[0]),.datab(Add_reg[0]),.result(Mout[0]));mult mult_inst1(.clock(clk),.dataa(coe[1]),.datab(Add_reg[1]),.result(Mout[1]));mult mult_inst2(.clock(clk),.dataa(coe[2]),.datab(Add_reg[2]),.result(Mout[2]));mult mult_inst3(.clock(clk),.dataa(coe[3]),.datab(Add_reg[3]),.result(Mout[3]));mult mult_inst4(.clock(clk),.dataa(coe[4]),.datab(Add_reg[4]),.result(Mout[4]));mult mult_inst5(.clock(clk),.dataa(coe[5]),.datab(Add_reg[5]),.result(Mout[5]));mult mult_inst6(.clock(clk),.dataa(coe[6]),.datab(Add_reg[6]),.result(Mout[6]));mult mult_inst7(.clock(clk),.dataa(coe[7]),.datab(Add_reg[7]),.result(Mout[7]));//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据//与串行结构不同,此处在一个时钟周期内直接将所有乘法器结果相加reg signed[28:0]sum;reg signed[28:0]yout;reg [3:0]k;always @(posedge clk or negedge rst_n)if(!rst_n)beginsum = 29'd0;yout <= 29'd0;endelsebeginyout <= sum;sum = 29'd0;for(k=0;k<8;k=k+1)sum = sum + Mout[k];endassign Yout = yout;endmodule
仿真测试模块
`timescale 1ns/1nsmodule FirParallel_tb;reg clk;reg rst_n,write_en;reg [11:0]Xin;wire [28:0]Yout;wire clk_data; //数据时钟,速率为时钟的八分之一FirParallel FirParallel_inst(.clk(clk),.rst_n(rst_n),.Xin(Xin),//数据输入频率为2khz.Yout(Yout)//滤波后的输出数据);parameter clk_period = 500000;//设置时钟信号周期/频率:2KHzparameter data_num = 2000;//仿真数据长度parameter time_sim = data_num*clk_period;//仿真时间initial clk=1'b1;always #(clk_period/2) clk=~clk;initial beginrst_n=1'b0;write_en=1'b0;#20000 rst_n = 1'b1;write_en=1'b1;#time_sim $stop;Xin = 12'd10;end//从外部文件读入数据作为测试激励integer Pattern;reg [11:0]stimulus[1:data_num];initial begin//$readmemb("noise_B.txt",stimulus);$readmemb("sin_B.txt",stimulus);Pattern = 0;repeat(data_num)beginPattern = Pattern + 1;Xin = stimulus[Pattern];#clk_period;endend//将仿真数据dout写入外部文件中integer file_out;initial begin//file_out = $fopen("noise_out.txt");file_out = $fopen("sout.txt");if(!file_out)begin$display("could not open file!");$finish;endendwire rst_write;wire signed [28:0]dout_s;assign dout_s = Yout;assign rst_write = clk & (rst_n);always @(posedge rst_write)$fdisplay(file_out,"%d",dout_s);endmodule
仿真波形图
3、使用matlab将产生的程序进行仿真验证
M程序:%E4_7_NoiseAndCarrierOut.Mf1=200; %信号1频率为200Hzf2=800; %信号2频率为800HzFs=2000;%采样频率为2KHzN=12; %量化位数%从文本文件中读取数据%测试输入数据分别放在Noise_in和S_in变量中fid=fopen('C:\matlab work\fir1_1\filterCoe\noise.txt','r');[Noise_in,N_n]=fscanf(fid,'%lg',inf);fclose(fid);fid=fopen('C:\matlab work\fir1_1\filterCoe\sin.txt','r');[S_in,S_n]=fscanf(fid,'%lg',inf);fclose(fid);%滤波后的输出结果数据分别放在Noise_out和S_out变量中fid=fopen('C:\matlab work\fir1_1\filterCoe\noise_out.txt','r');[Noise_out,N_count]=fscanf(fid,'%lg',inf);fclose(fid);fid=fopen('C:\matlab work\fir1_1\filterCoe\sout.txt','r');%fid=fopen('C:\matlab work\fir1_1\filterCoe\E4_7_Sout.txt','r');[S_out,S_count]=fscanf(fid,'%lg',inf)fclose(fid);%归一化处理Noise_out=Noise_out/max(abs(Noise_out));S_out=S_out/max(abs(S_out));Noise_in=Noise_in/max(abs(Noise_in));S_in=S_in/max(abs(S_in));%求信号的幅频响应out_noise=20*log10(abs(fft(Noise_out,1024))); out_noise=out_noise-max(out_noise);out_s=20*log10(abs(fft(S_out(150:length(S_out)),1024))); out_s=out_s-max(out_s);in_noise=20*log10(abs(fft(Noise_in,1024))); in_noise=in_noise-max(in_noise);in_s=20*log10(abs(fft(S_in,1024))); in_s=in_s-max(in_s);%滤波器本身的幅频响应hn=black_fpga;m_hn=20*log10(abs(fft(hn,1024))); m_hn=m_hn-max(m_hn);%设置幅频响应的横坐标单位为Hzx_f=[0:(Fs/length(out_noise)):Fs/2];%只显示正频率部分的幅频响应mf_noise=out_noise(1:length(x_f));mf_s=out_s(1:length(x_f));mf_in_noise=in_noise(1:length(x_f));mf_in_s=in_s(1:length(x_f));mf_hn=m_hn(1:length(x_f));%绘制幅频响应曲线figure(1);subplot(211);plot(x_f,mf_in_noise,'--',x_f,mf_noise,'-',x_f,mf_hn,'--');xlabel('频率(Hz)');ylabel('幅度(dB)');title('FPGA仿真白噪声信号滤波前后的频谱');legend('输入信号频谱','输出信号频谱','滤波器响应');grid;subplot(212);plot(x_f,mf_in_s,'--',x_f,mf_s,'-',x_f,mf_hn,'--');xlabel('频率(Hz)');ylabel('幅度(dB)');title('FPGA仿真合成单频信号滤波前后的频谱');axis([0 1000 -100 0]);legend('输入信号频谱','输出信号频谱','滤波器响应');grid;%绘制时域波形%设置显示数据范围t=0:1/Fs:50/Fs;t=t*1000; t_in_noise=Noise_in(1:length(t));t_in_s=S_in(1:length(t));t_out_noise=Noise_out(1:length(t));t_out_s=S_out(1:length(t));figure(2);subplot(211);plot(t,t_in_noise,'--',t,t_out_noise,'-');xlabel('时间(ms)');ylabel('幅度');title('FPGA仿真白噪声信号滤波前后的时域波形');legend('输入信号波形','输出信号波形');grid;subplot(212);plot(t,t_in_s,'--',t,t_out_s,'-');xlabel('时间(ms)');ylabel('幅度');title('FPGA仿真合成单频信号滤波前后的时域波形');legend('输入信号波形','输出信号波形');grid;
仿真波形图
可以看到,并行结构的FIR滤波器设计成功,并且性能相比于串行结构更好,设计成功。
如果觉得《基于FPGA的FIR滤波器的实现(5)— 并行结构FIR滤波器的FPGA代码实现》对你有帮助,请点赞、收藏,并留下你的观点哦!