DSP48E2使用以及FIR滤波器定点设计实现与优化
DSP48E2使用以及FIR滤波器定点设计实现与优化
DSP48E2是数字信号处理中最常用的FPGA硬核资源,对于高效率,低消耗实现数字信号处理算法有着极为重要的意义。因此熟练掌握DSP48E2的使用是算法工程化的必备技能。FIR滤波器是因果系统,只有零点没有极点,因此绝对稳定,此外具有线性相位,有限抽头长度,多用于实现低通滤波器、半带滤波器等,是信号处理中最典型的模块。本文由浅入深,通过单个DSP48E2资源实现实数乘法,再到3个DSP48E2实现复数乘法,最后到多个DSP48E2级联实现FIR滤波器,记录了本人在此过程中遇到的问题,与解决办法。含有DSP48E2硬核资源的Xilinx系列的FPGA芯片对实现FIR滤波器进行了专门优化(DSP的级联),因此本人通过FIR滤波器定点设计来掌握DSP48E2的使用,并对对称系数的FIR滤波器进行了结构优化与资源对比。
关键字:DSP48E2,饱和与截位,FIR滤波器,定点设计,优化设计
-
DSP48E2基础功能
图1 DSP48E2功能框图(ug579)
数字信号处理需使用大量的二进制乘法器和累加器,这些操作可以在专用的DSP内实现,这些DSP属于硬核资源。Xilinx的FPGA器件提供了大量专用的、低功耗DSP slice,并使用DSP48E2原语来定义。7系列FPGA器件中的DSP模块称为DSP48E1,UltraScale架构FPGA器件中的DSP模块则称为DSP48E2,其功能更加强大,对于DSP48E1, DSP48E2的不同,请参阅UG579。
DSP48E2主要包括27位预加器、27*18位二进制补码乘法器、48位ALU单元、SIMD算术单元、模式检测器等部分。能够实现27bit(D端口) +/- 27bit(A端口)的预加27bit(D+/-A),操作方式主要是通过配置INMODE[3:0],其中A端口的输入是30bit,在进入预加器前会截位成27bit,输出也是经过截位的27bit,我们知道27bit + 27 bit 的输出结果至少要28bit才能无误差输出,因此我的理解是预加器最多满足26bit + 26 bit的预加结果无损输出。高于26bit的加法不能在预加器中实现,可以通过DSP48E2的级联来完成。
图2 DSP48E2预加器(ug579)
27*18位二进制补码乘法器主要是实现(D +/- A)*B,B端口的位宽为18bit,配置较为简单INMODE[4],来配置成是打一拍后进行乘法(D + A)*B1,或者是打2拍后进行乘法
(D +/- A)*B2。也就是说INMODE参数主要是控制乘法器的动作,在不使用乘法器的情况下INMODE是无效的。当两个预加结果都输入乘法器时,可实现乘方的功能,需要配置其他参数PREADDINSEL,BMULTSEL。
可通过配置48位的ALU单元实现加法/减法/累加功能,需要OPMODE与ALUMODE。具体的配置过程需要自己查阅ug579,在这里不过多赘述了,OPMODE主要控制输入ALU的信号W,X,Y,Z的选择,ALU控制W,X,Y,Z相加或者做减法。
-
DSP48E实现乘法与复乘
举个例子,我们要实现两个数相加后乘于另一个数输出运算结果,(D + A2)*B2,那么DSP48E2原语相关的参数该如何配置呢?
A2,B2的意思如前文所述,为A,B 输入的数据,寄存2次后进行运算,为了满足时序要求,我们在使用的过程中尽量要求能用的寄存器都用上,流水线的方式可以优化时序。查阅ug579的table 2-1 、2-3、2-7,我们可以得到3个参数的配置如下。
INMODE = “00100”,OPMODE= “000000101”,ALUMODE=“0000”。具体意义与用法还是建议查阅Xilinx的DSP48E2手册ug579。
-
-
复乘的实现与优化
-
图3 复数乘法的数学推导
基于复乘的基本理论,我们要实现复数乘法,需要四个乘法器(4个DSP48E2),两个加法器(可通过级联功能实现),如图绿色方框所示;为了节省资源,我们对复乘进行了优化,让其只需要3个乘法即可实现,如红色方框所示,其中第一个与第三个是完全一致的。
前面我们基于DSP48E2,实现了两个实数的乘法。接下来我们要实现两个DSP48E2的级联,复乘结果的实部,虚部操作一样,因此我们以实部讨论为例。DSP48E2的级联,包括输入信号的级联ACIN,ACOUT,BCIN,BCOUT和输出信号的级联PCIN,PCOUT,Xilinx的DSP48E2资源,对于级联做了专门优化,通过这些端口级联的信号,不占用额外的逻辑资源。如图所示以u2_dsp为例:u0_p(C)-ad_im_2d*(b_re_2d+b_in_2d)
其中u0_dsp的输出结果通过PCIN端口,输入到u2_dsp(或者u3_dsp),并配置OPMODE为“000010101”,ad_im_2d的原因主要是时钟对齐,我们设置了u0_dsp为(A1+ D)*B1,u2_dsp则是设置了(A2+ D)*B2,计算预加与乘法的时间u2多需要一拍,而且 u0_dsp输出需要一拍寄存才能到达u2_dsp(u3_dsp)的PCIN端口,因此为了保证信号同步,u2_dsp,u3_dsp需要延迟2拍输入。
为了更好的理解,我们忽略预加与乘法的时间,保证3个DSP的预加与乘法时间相同(即打拍相同,比如同为(A2+D)*B2),则只要预留u0_dsp的输出P打一拍时间即可,此时,ad_im_1d,b_re_1d,b_im_1d,延迟一拍。
-
-
饱和与截断的处理
-
1、饱和运算尽量使用DSP模块中的PATTERNDETECT和PATTERNBDETECT端口来实现,而不是使用额外的逻辑资源。
2、使用DSP时可以灵活运用OPMODE来实现抽取、保持、清零、通道复用等功能,而不是仅仅把它作为一个静态配置来使用。
PATTERNDETECT和PATTERNBDETECT端口在算法类设计中这两个端口有一个很有用的功能,就是用来做饱和。如图4所示,PATTERNDETECT和PATTERNBDETECT是一个比较器的输出,这个比较器的作用是把DSP输出端的任意bit和一个数的对应bit做比较,如果相等则PATTERNDETECT为1,如果按位相反则PATTERNBDETECT为1。这个用于比较的数可以是参数设定的固定数,也可以是C端口的输入数据,而bit选择也可以通过参数或C端口进行。
图4 pattern端口
在用于饱和操作时,通常用参数设定固定的比较数和需要比较的bit位,我们知道DSP输出为48位,假如需要对高11bit做饱和操作,输出37bit,则对DSP做如下设置:
.MASK (48'h000fffffffff ),// 比较高12bit,低36bit不比较
.PATTERN (48'h000000000000 ),// 固定比较数为全0
.SEL_MASK ("MASK" ),// 比较bit选择使用“MASK”参数的设定
.SEL_PATTERN ("PATTERN" ),// 比较数选择使用“PATTERN” 参数的设定
饱和操作的原理是把DSP输出的符号位部分和12’h0做比较,当输出为正数时,符号位应该等于12’h0,则PATTERNDETECT输出1,当输出为负数时,符号位应该等于12’hfff,即12’h0的按位取反,则PATTERNBDETECT输出1,也就是说如果PATTERNDETECT和PATTERNBDETECT都为0,则表示输出数据溢出了。用如下代码实现最终的饱和输出:
always @ (posedge clk)
begin
if (PATTERNDETECT || PATTERNBDETECT) begin
out <= P[36:0];
end
else begin
out <= {P[47],{36{~P[47]}}};
end
end
当然这只是管中窥豹,DSP48E2的参数加in/out端口数目多达上百个,但是大部分参数都有详细的描述,用一遍就能理解其用法,需要详细领悟的参数/接口不到20个,这些端口需要反复使用才能完全灵活的配置达到自己所需要的功能。我的经验是实践出真知,多试试配置几遍,然后分析错误,就能很快掌握其基本用法。
-
FIR滤波器的设计与实现
-
FIR滤波器的定点设计
-
Matlab是一个非常好用的工具,本文的FIR滤波器设计与数据输出都是基于该工具。利用脚本生成正弦信号和FilterDesigner工具设计一个滤波器,得到浮点信号和浮点滤波器系数,我们需要进行定点化生成2进制或者16进制数据写入txt文档,方便后续Verilog代码读取($readmemb,$reammemh)。下面的代码有两种方式生成16进制或者2进制数据,写入数据的前提都是用round、floor等函数将浮点小数生成整数(round*(2^(N-1)))。
下面的代码是将整形数据写入txt文件,第一种hex file代码简单,且写入迅速,美中不足的是只能写入正数,需要将有符号数转化成无符号数;第二种方法(%注释代码)是以二进制补码写入txt文件,逐bit判断,速度很慢,可以满足写入有符号数的需求,后续将寻找其他函数,来快速写入16进制有符号补码数据。
%% signed to unsigned and output to txt.file in hex
fid=fopen('C:\... \Desktop\DataIn.txt','w');
unsigned_si = s2us(si,24);
fprintf(fid,"%06x\n",unsigned_si); % fprintf only unsigned hex(%x)number , signed number ?
%% output binary complement data to txt.file
% for k=1:length(si)
% B_s=dec2bin(si(k)+(si(k)<0)*2^N,N);
% for j=1:N
% if B_s(j)=='1'
% tb=1;
% else
% tb=0;
% end
% fprintf(fid,'%s',B_s(j));
% end
% fprintf(fid,'\r\n');
% end
% fprintf(fid,';');
fclose(fid);
非对称FIR滤波器与DSP48E2实现
我们知道FIR的原理是时域卷积,即
常见的直接型FIR滤波器系统框图如下所示,
图5 直接型FIR滤波器(systolic)
图6 倒置直接型FIR滤波器(transpose)
我们以图5的systolic直接型FIR滤波器的实现为例,实现其功能。
其中x(n)的信号传递可以使用ACIN,ACOUT级联传递,因为只需要延迟一拍,那么ACASCREG ,
AREG均设置为1。
图7 FIR滤波器第一级DSP48E2配置(PREG=0,设置AREG=1,只会有一拍延时)
我们可以从图5发现,当级联的DSP过多时,即FIR阶数太高,最后的加法组合逻辑将非常容易导致系统出现时序问题,时序无法收敛,因此我们在使用的P端口输出,一般会打一拍(PERG =1),充分利用寄存器资源满足我们需要的时序关系。
图8 FIR滤波器第一级DSP48E2配置(AREG=2,PREG=1)
当DSP的输出寄存一拍的时候,我们可以得到FIR滤波器的系统框图如下,在滤波器上下链路都加入一拍寄存器,保证我们得到卷积的结果,(不考虑预加与乘法的延时,设置完全相同,分析时可以忽略),完全利用了DSP48E2的资源,不需要其他逻辑资源完成滤波。需要注意的一点就是级联的打拍数必须 ≤ A 端口的打拍数,即在配置DSP48E2的时候ACASCREG ≤AREG,此时ACASCREG ,AREG均为2, PREG=1。
满足我们所需要的卷积结果。
图9 流水线直接型FIR滤波器
图10 流水线直接型FIR滤波器DSP48级联实现框图
对称系数FIR滤波器与DSP48E2实现
对于N阶FIR滤波器(N taps),我们需要N个DSP48E2级联来实现,但是我们设计的FIR滤波器大部分都是系数对称的,即h(N-1)=h(0),h(N-2)=h(1)…
FIR滤波器的卷积运算,我们可以得到如下结果,其中N为奇数,保证FIR滤波输出为整数群延时。
因此理论上我们可以利用延时打拍与DSP48E2的预加器,减少一半的乘法资源消耗。
下面是两种具体实现的框图以及前3个DSP的输出结果,同样不考虑加法与乘法的打拍延时(ADREG,MREG)。
图10 对称系数的FIR滤波器实现框图1
同样是满足卷积的结果。仿真也证明该方法的有效性,对于对称结构,节省了一半的乘法资源。
最后一种是完全基于FIR的结构特点,更改了系数与数据流方向,完全做到了不需要任何外部逻辑资源来达到FIR滤波器最优化设计的效果。该结构是目前本人了解到的对称系数的FIR滤波器的最优结构,也是最为推荐的对称系数FIR滤波器的实现结构。
图11 对称系数的FIR滤波器实现框图2(资源最少)
我们同样分析DSP的输出如下,N=15为例,对称系数分别为h(0),h(1),…h(7),总计需要8个DSP48E2,按照数据流分析
仿真实现该结构的过程中尤其要注意就是h的系数反向,与第一个DSP48E2 A端口只需要延时1拍(AREG =1 ,ACASCREG=1,INMODE= “10101”), 其他的DSP48E2的级联端口都是两拍(AREG =2,ACASCREG=2, INMODE= “10100”),设计的关键是保证时序与数据的对齐。出现错误后,可能需要自己一拍一拍的计算,才能定位错误。最后用beyond compare对比数据,切不可仅仅对比前几拍数据,需要大量数据进行比对,才能验证滤波器的功能。