原文网址:http://xilinx.eetrend.com/blog/810
自从开始学FPGA起,关于硬件乘法器与软件乘法器的概念就一直有点困惑。明明FPGA已经自带了18*18位的硬件乘法器(大概总结了一下,Spartan6系列的硬件乘法器数量如图1所示,Spartan3的如图2所示),好多书籍却要花费较多经历来讲如何用HDL语言来实现乘法;这是一个非常复杂的过程,感觉会让初学者不知所措(感觉使用Core Generator来产生乘法器要比用HDL语言要容易一些)。在硬件乘法器足够使用的情况下,为什么不直接使用硬件乘法器,来简化设计、提高性能呢?仔细阅读了Xilinx关于硬件乘法器的资料,没想到它的用法、用途还是这么丰富。于是在此和大家分享一下心得。
图1 Spartan6系列的硬件乘法器
图2 Spartan3系列的硬件乘法器
1. 简介
Xilinx FPGA一般都带有多个18*18的硬件乘法器(一般用MULT18*18原语来指代,在高端器件里面乘法器、加法器和累加器集成为DSP48、DSP48E等模块),一般我们都用它们来完成有符号或者无符号的乘法运算。其实,使用Xilinx FGPA自带的硬件乘法器,不光可以完成这些基本的操作,还可以被用来进行移位、产生一个数值的二进制补码等(如Xilinx Application Note 467)。另外,硬件乘法器还可以互相进行级联或者与CLB逻辑进行级联以进行更为复杂的功能求解。下面就具体问题具体分析,来展示一下Xilinx FPGA硬件乘法器的强大功能。
Xilinx FPGA硬件乘法器是独立于逻辑资源之外的,所以它不占用逻辑资源,可以以最小的硬件代价实现较为复杂的算术运算(当然布线资源还是要占用的)。总结了一下,包括有符号*有符号,有符号*无符号,逻辑运算与算术运算、二进制补码、幅值返回等都可以使用MULT18*18来实现。
一个基本的MULT18*18有两个独立的动态输入端口:18位的有符号数或者17位的无符号数。而把四个MULT18*18、一个36位的加法器和进行一个53位的加法器级联,就可以得到35*35位的乘法器,它的基本原理框图如图4所示。
图4 35*35位的有符号乘法器
二进制乘法与常规的乘法在本质上是一样的,它就像是只有0和1的十进制乘法(简直太简单了,可以参考《编程卓越之道第一卷:深入理解计算机》这本书,讲的很深刻)。以下是掌握二进制乘法需要知道的规则:
0 × 0 = 0
0 × 1 = 0
1 × 0 = 0
1 × 1 = 1
使用这四条规则,二进制乘法按照与十进制乘法相同的方式工作(事实上,如果按照十进制乘法的规则来对二进制数进行操作,结果完全正确,因为在十进制乘法中,与数字0和1相关的规则与二进制乘法是一样的,只是进位的数不一样而已,十进制的是到十才进位这里是到2就进了)。
3.时序考虑
在乘法器的计算中,LSB的产生要比MSB快,因为MSB需要更多层的加法,所欲时序考虑与36位输出的乘法器是不一样的。举个例子来说,如果两个无符号的的数其乘积永远不超过2的35次方,则输出结果的 P[35]将永远是0。对于其中任意有符号的n位相乘,如果不出现(-2^(n-1))* (-2^(n-1))的情况,则MSB永远与下一个低bit位相同(即p[2n-1]=p[2n-2])。同时考虑到,如果某些情况下要求输出没有位之间相对的布线延迟,则应增加LSB的延时以便和MSB的延时进行平衡。
因为器件制造等原因,在流水线式的MULT上,输入数据的建立时间中MSB的往往比LSB的要短,但是器件的管脚延时却没有区别。为了保证设计中的额外的安全裕量,MSB应该接受一个相对较慢的输入。复位与时钟使能必须比所有的输入数据的建立时间都快,并且等待时间必须为零。时序约束中的参数tmulidck(乘法器输入数据到时钟)用来同时约束数据与控制输入,但是对它们两种类型却具有不同的数值。
4.库中的原语
表1 MULT18*18和MULT18*18S的原语
经常用原语的朋友对此应该不陌生了,MULT18*18是普通的乘法器,MULT18*18S是带有控制端的(如时钟使能C、复位信号等)。使用库中的原语与使用Xilinx的实现工具,如FPGA Editor时,管脚的定义是一样的。乘法器原语的示意图如图5所示。
图5 乘法器原语的示意图
在ISE中打开Language-template,verilog— device primitive--- Spartan3--- Arithmetic functions, 就可以看到MULT18*18和MULT18*18S的原语调用代码了。例如MULT18*18的就是
// MULT18X18 : In order to incorporate this function into the design,
// Verilog : the following instance declaration needs to be placed
// instance : in the body of the design code. The instance name
// declaration : (MULT18X18_inst) and/or the port declarations within the
// code : parenthesis may be changed to properly reference and
// : connect this function to the design. All inputs
// : and outputs must be connected.
// <-----Cut code below this line---->
// MULT18X18: 18 x 18 signed asynchronous multiplier
// Spartan-3
// Xilinx HDL Language Template, version 12.1
MULT18X18 MULT18X18_inst (
.P(P), // 36-bit multiplier output
.A(A), // 18-bit multiplier input
.B(B) // 18-bit multiplier input
);
// End of MULT18X18_inst instantiation
前面已经介绍了Spartan3系列FPGA中硬件乘法器的用途、调用方法、结构特定等等,对Xilinx FPGA中基本的硬核乘法器有了初步的认识。接下来对它的一些具体使用方法做更深入的介绍。
前面已经介绍了Spartan3系列FPGA中硬件乘法器的用途、调用方法、结构特、使用一个专用硬件乘法器同时处理两个乘法的方法、使用Xilinx CORE Generator来生成带控制端的IP核00等等。接下来对它的一些具体使用方法做更深入的介绍。
1. 乘法器子模块
Spartan3系列FPGA中专用硬件乘法器是18*18位的,因为块RAM的宽度是18位的,二者匹配可以提高运算的性能。不过我们的程序中,并不总是这么巧需要18*18位的;以两个数相乘相乘为例,假如是有符号的整数9乘以有符号的整数9,那只是一个5*5的乘法,还剩余了很多的计算能力。为此,可以把一个大的18*18位的专用硬件乘法器划分为多个子模块。图1给出了这样的一个示例。
图1 乘法器自模块的划分方法
图2、图3分别给出了4*4有符号和无符号的乘法器子模块。
图2 4乘4有符号子模块
图3 4乘4无符号子模块
MAGNTD_18子模块可以返回一个数值的幅值(例如绝对值,以二进制补码表示)。一个负的输入返回的是正值,而正的输入保持不变。TWOS_CMP18和TWOS_CMP9可以求取输入的二进制补码。也可以使用一些额外的slice逻辑来与这些模块一起进行求取幅值、补码等操作。图4给出了使用MULT18*18专用硬件乘法器来生产两个TWOS_CMP9子模块的示意图。
图4 使用MULT18*18产生两个TWOS_CMP9子模块的示意图
2. 乘法器模块的位置约束
MULT18*18专用硬件乘法器可以有它们自己的LOC属性,即ucf用户约束文件中的位置约束。但是要注意到,MULT18*18的位置与平时我们所说的CLB的位置命名规则是不同的,例如
LOC= MULT18*18_X0Y0
代表的是位于器件最左下角的位置。为了清楚的说明,在PlanAhead的左下角找到了MULT18*18_X0Y0,如图5所示。
图5 PlanAhead中的MULT18*18位置
3. 乘法器的其它用途
3.1 移位操作
因为乘以2的n次方与移位n位的效果是一样的,所以乘法器也可以用作移位器,或者其它目的的通用资源。在一个系统中,假如硬件乘法器有富余,那用它们来做一些这样的操作是非常适宜的。
例如,把专用的硬件乘法器作为移位器使用时,把要被移位的数作为一个输入,而把要移动的位数n变为2的n次方作为另一个输入送入乘法器即可。因为MSB不能用来控制移位(因为MSB是符号位,如果把它移没了整个输出结果就是错误的),所以18*18位的乘法器处理0到16位的移位。在输出的36位数之中,多余的位被自动填零或者1(根据MSB的状态来决定)。这也符合了2进制移位对空余位的处理方式。
3.2 幅值返回
为了通过乘法操作来求一个输入的幅值,如果它是正数(MSB是0),则应该把它乘以1;如果它是负数(MSB是1),则应该把它乘以-1。在二进制补码表示中,1除了LSB是1外,其余的各位都是0;而-1的各位都是1,包括LSB上的值。所以,一个幅值或者绝对值返回的操作可以通过把输入乘以这样的一个数得到:它的LSB是1,而其余各位与输入的同次位上的数值相同。图6给出了这样操作的原理框图。
图6 幅值返回原理框图
3.3 二进制补码返回
一般情况下,求取一个数的二进制补码,需要使用带进位逻辑的LUT来实现。如果LUT资源比较紧张,而MULT资源富余的话,可以用MULT来求取二进制补码。把输入的数乘以同样多位数的全1的数就可以得到输入的二进制补码,是这样会产生同样多位数的冗余位,应该把它们剔除掉。图7给出了使用乘法器求取输入信号的二进制补码返回原理框图。
图7 使用乘法器求取输入信号的二进制补码返回原理框图
3.4 复数乘法
复数的乘法,例如(a+ib)*(c+id),它的结果也分为两部分,实部是ac-bd,虚部是(a+b)(c+d)-ac-bd。这样的复数乘法在数字信号处理中应用极多,例如快速傅里叶变换FFT、滑动傅里叶变换DFT等等。复数的乘法在前面讲system Generator和昨天的博文讲乘法器的IP核调用时都提到了,所以在这里不详细叙述了。
3.5 矩阵乘法中时间共享
矩阵的乘法在实际系统中应用非常广泛,例如语音、图形、数据处理中都要用到,而且其规模往往非常大,运算很复杂。一个3*3的矩阵操作就需要27次乘法和18次加法,而最后仅仅得到的是3*3的矩阵,共9个数。图像处理中的3*3的矩阵操作稍微简单一些,但是仍需要9个乘法和6个加法操作。
Spartan3系列FPGA使用“时间共享”的概念来处理矩阵乘法。不过我还是不是非常明白,有兴趣的朋友可以参考XAPP284(Matrix Math, Graphics, and Video)这个应用笔记,Xilinx网站上的应用笔记是非常好的资料,我正准备把相关的都仔细读一下;以后也会经常来分享一下自己的心得。
3.6 浮点运算
前面博文提到了,在实际开发中,一个主要的困难就在于浮点的运算。为了提高计算的精度,例如10000.51234*2000.69875454这样的操作,需要用非常大的位宽才能保证运算结果的精度,这样造成了芯片资源的极度紧张。而这样的一个浮点运算,在含有FPU的DSPs之中,例如TI的TMS320F28335(指令周期6.67ns)中,仅仅需要十几个周期,即几十纳秒的时间就可以处理完了。希望Xilinx有一天可以推出带有FPU的FPGA,这样再做浮点乘法就很轻松了。
关于Spartan3中专用硬件乘法器的使用,目前我只理解到这样的程度,所以暂时不能和大家分享更多了,欢迎来讨论。
最后,附上二进制补码的乘法运算规则:
0 x 0 = 0
0 x 1 = 0
1 x 0 = 0
1 x 1 = 1, 并且没有进位和借位
例如