900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 数字信号处理——并行FIR滤波器MATLAB与FPGA实现

数字信号处理——并行FIR滤波器MATLAB与FPGA实现

时间:2022-09-10 08:13:26

相关推荐

数字信号处理——并行FIR滤波器MATLAB与FPGA实现

前言

本文介绍了设计滤波器的FPGA实现步骤,并结合杜勇老师的书籍中的并行FIR滤波器部分进行一步步实现硬件设计,对书中的架构做了复现以及解读,并进行了仿真验证。

并行FIR滤波器FPGA实现

FIR滤波器的结构形式时,介绍了直接型、级联型、频率取样型和快速卷积型4种。在FPGA实现时,最常用的是最简单的直接型结构。FPGA实现直接型结构的FIR滤波器,可以采用串行结构、并行结构等不同中的结构设计,上文根据书中提供的架构完成了串行 FIR滤波器的实现,本文沿用上文的基本代码结构,按照并行FIR滤波器的架构完成电路描述。

FIR滤波器需求

设计一个15阶(长度为16)的低通线性相位FIR滤波器,采用窗函数设计,截止频率为500 Hz,采样频率为2 000 Hz;采用FPGA实现并行结构的滤波器,系数的量化位数为12比特,输入数据位宽为12比特,输出数据位宽为29比特,系统时钟为16 kHz。

滤波器系数确定与量化

确定滤波器的结构后,就根据滤波器进行设计代码仿真,这里引用书中的仿真设计,并将滤波器参数系数量化。确定滤波器系数的方法有很多,可以使用MATLAB中丰富的函数实现,或者使用相关滤波器设计的软件工具,定制满足当前需求的窗函数的滤波器系数。具体量化系数确定可参考上文《数字信号处理-09-串行FIR滤波器MATLAB与FPGA实现》中的相关内容,或者参考杜勇老师的书中的内容。

硬件架构

下图为杜勇老师的《数字滤波器的MATLAB与FPGA实现》实现的并行FIR滤波器的结构图。因为FIR滤波器参数对称,所以同时计算相应的对称结构的值,将对称系数的X(n)相加后,可调用8个乘法器,完成对滤波器的乘法运算,所以针对并行滤波器的架构数据的输入速率和时钟可以相同,每一个时钟周期流水输出一个滤波后的信号值。图中的8输入的加法器,可以替换成N/2;这样就得到了一个通用化的并行FIR滤波器结构图。

并行FIR滤波器

并行实现FIR滤波器,虽然浪费了加法器和乘法器的资源,但是提升了整个滤波器实现的性能,当滤波器的系数长度N增大时,数据的吞吐速率不变(暂且不考虑面积增大对性能的影响),但带来的坏处就是会用掉相应倍数的逻辑资源和运算资源,速度和面积本来就是鱼和熊掌的关系,在实际应用中应当做相应的权衡和割舍。

根据架构描述电路

根据杜勇老师书中提供的架构,对电路进行描述,同样沿用了前文的通用化的模板,后期可根据参数输入来适配不同滤波器长度的设计。

实现模块框图

接口描述如下:

接口描述

参数描述如下:

参数描述

代码如下:

`timescale1ns/1psmoduleFir_Parallel(inputclk,//!系统时钟inputrst,//!复位信号inputsigned[SIGN_IN_WIDTH-1:0]signal_in,//!信号输入outputsigned[SIGN_OUT_WIDTH-1:0]signal_out//!信号输出,信号输出速度和输入速度相同);//parameterintegerSIGN_IN_WIDTH=12;//!信号输入位宽parameterintegerSIGN_OUT_WIDTH=29;//!信号输出位宽parameterintegerFIR_COE_WIDTH=12;//!滤波器系数位宽parameterintegerFIR_COE_NUM=16;//!滤波器长度localparamintegerFIR_WIDTH_DIV_2=FIR_COE_NUM/2;function[FIR_COE_WIDTH-1:0]coe_data;input[FIR_WIDTH_DIV_2-1:0]index;begincase(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;endcaseendendfunctionintegeri;genvarj;//!滤波器系数加载wiresigned[FIR_COE_WIDTH-1:0]coe[FIR_WIDTH_DIV_2-1:0];generatefor(j=0;j<FIR_WIDTH_DIV_2;j=j+1)assigncoe[j]=coe_data(j);endgenerate//!寄存输入信号reg[SIGN_IN_WIDTH-1:0]Sign_in_Reg[FIR_COE_NUM-1:0];//将数据存入移位寄存器sign_in_Reg中always@(posedgeclk)beginif(rst=='b1)begin//初始化寄存器值为0for(i=0;i<FIR_COE_NUM;i=i+1)Sign_in_Reg[i]=12'd0;endelsebeginfor(i=0;i<FIR_COE_NUM-1;i=i+1)Sign_in_Reg[i+1]<=Sign_in_Reg[i];Sign_in_Reg[0]<=signal_in;endendregsigned[SIGN_IN_WIDTH:0]add_sum[FIR_WIDTH_DIV_2-1:0];//为了保证加法运算不溢出,输入输出数据均扩展为SIGN_IN_WIDTH+1比特。//对称结构只需要计算FIR_WIDTH_DIV_2次//一级流水always@(posedgeclk)beginif(rst=='b1)beginfor(i=0;i<FIR_WIDTH_DIV_2;i=i+1)add_sum[i]<='d0;endelsebeginfor(i=0;i<FIR_WIDTH_DIV_2;i=i+1)add_sum[i]<={Sign_in_Reg[i][SIGN_IN_WIDTH-1],Sign_in_Reg[i]}+{Sign_in_Reg[FIR_COE_NUM-1-i][SIGN_IN_WIDTH-1],Sign_in_Reg[FIR_COE_NUM-1-i]};endend(*use_dsp48="yes"*)regsigned[SIGN_IN_WIDTH+FIR_COE_WIDTH:0]mult_out[FIR_WIDTH_DIV_2-1:0];always@(posedgeclk)beginif(rst=='b1)beginfor(i=0;i<FIR_WIDTH_DIV_2;i=i+1)mult_out[i]<='d0;endelsebeginfor(i=0;i<FIR_WIDTH_DIV_2;i=i+1)mult_out[i]<=add_sum[i]*coe[i];endendassignsignal_out=sign_out;regsigned[SIGN_OUT_WIDTH-1:0]sum;regsigned[SIGN_OUT_WIDTH-1:0]sign_out;always@(posedgeclk)beginif(rst)beginsum<='d0;sign_out<='d0;endelsebeginsign_out<=sum;//sum='d0;//for(i=0;i<FIR_WIDTH_DIV_2;i=i+1)//sum=sum+mult_out[i];sum<=mult_out[0]+mult_out[1]+mult_out[2]+mult_out[3]+mult_out[4]+mult_out[5]+mult_out[6]+mult_out[7];endendendmodule

代码解读

关于加载滤波器系数的部分,我这里使用了function做了包装,以便于后续修改滤波器长度时,可以通过脚本生成function去增加滤波器系数的长度。

function[FIR_COE_WIDTH-1:0]coe_data;input[FIR_WIDTH_DIV_2-1:0]index;begincase(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;endcaseendendfunction

针对乘法运算,这里没有使用IP,但是为了使得该部分运算使用DSP资源,更好地提升性能,因此该信号的运算使用dsp48资源,所以在信号声明时前面加了(*use_dsp48="yes"*)

关于杜勇老师书中写的信号与系数相乘后的结果针对sum信号使用了阻塞赋值的部分,个人觉得这个在时序逻辑中是不太好的设计,使用的代码如下,虽然会简化乘累加的过程,但是针对实际使用的工程来说,这个是不好的代码风格。

always @(posedge clk)beginif (rst=='b1)begin sum = 'd0; sign_out <= 'd0;endelse beginsign_out <= sum;sum = 'd0;for (i=0; i<FIR_WIDTH_DIV_2; i=i+1)sum = sum + mult_out[i];endend

所以这里我直接做了展开处理,将8个结果做了加法。

电路架构优化

我认为在随着滤波器规模变大运算的数据位宽增加时,信号与系数相乘后的结果进行累加操作的部分,组合逻辑的延时相对会增加很多,为了进一步提升电路架构的性能,可对该部分进行加法树的平衡,打拍优化加法树结构,应该有可能进一步提升电路架构的性能。

仿真设计

仿真数据设计

为了验证并行设计代码的正确性。这里使用MATLAB脚本产生了一个混频信号,混频的频率为100hz和700hz的叠加,然后将混频信号进行量化处理并导出txt文件以供仿真文件读取。

clc;closeall;clearall;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输入波形,量化到16bits_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+');%生成十六进制fori=1:Nfprintf(fid,'%s',s_12bit(i,:));fprintf(fid,'\r\n');endfclose(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);

运行仿真后,根据设计的滤波器系数进行仿真,发现可以正常滤波除去高频分量。

滤波仿真效果

仿真激励文件编写

`timescale1ns/1psmoduleFir_Parallel_tb;//ParameterslocalparamintegerSIGN_IN_WIDTH=12;localparamintegerSIGN_OUT_WIDTH=29;localparamintegerFIR_COE_WIDTH=12;localparamintegerFIR_COE_NUM=16;//Portsregclk=1;regrst=1;reg[SIGN_IN_WIDTH-1:0]signal_in;wire[SIGN_OUT_WIDTH-1:0]signal_out;Fir_Parallel#(.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_Parallel_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*1)beginif(rst==0)addr=addr+10'd1;signal_in=mem[addr][11:0];endalways#5clk=!clk;initialbeginsignal_in=0;$readmemh("sin_data.txt",mem);addr=10'd0;#10;rst=0;endendmodule

运行仿真,查看波形可见,滤波效果和仿真结果一致。

仿真波形

延迟分析

该架构的数据输入后,每四个时钟周期后输出一个数据,其中,一个时钟周期用于X(n)的加和,一个时钟周期用于计算信号和滤波器系数相乘的结果,一个时钟周期用于乘法输出后的数据做累加处理,一个时钟用于读取累加后的结果。

延时分析

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。