购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第二讲

四则运算正负整数
可否综合加减乘除算术运算

记住该记住的,忘记该忘记的。改变能改变的,接受不能改变的。

——《麦田里的守望者》

有句歌词唱得好:“Oh,Oh,Oh,Oh,Oh,爱是一加一。”

贫道这里稽首了。列位请想,连“爱情”这么玄妙的东西,都是可以用加法运算表示的,足以见得算术运算的重要了。鄙人知道,老和尚最喜欢唠叨的就是如何实现无符号数加法的流水线了。本着“走自己的路,叫别人无路可走”的态度,趁着该比丘今天吃斋饭,在下给他下了点巴豆。后来呢?后来,老罗汉就不得不请假了,今天这回书由老道代劳。他还不得不央求某家,我自然也得装出很不愿意的样子:“下不为例!明天你要请我吃火锅,麻辣的!”

根据鄙人的统计,目前FPGA的活计主要是两类:快速、定制接口系统和信号处理系统。接口的事情咱们不谈,信号处理对于数字逻辑工程师而言主要就是数学运算了。为什么这样算是算法工程师的事情?如何在逻辑里高性价比地实现?FPGA 工程师那是当仁不让。“预作信号,必先算术。学会操作,才能成功”。来来来,贫道带诸位进入今天的主题:算术运算。

一、加减乘除排排队

数字的编码问题,前文书已经介绍过了,不再重叙。尚不熟悉的听众需要回过头去复习一下,这个过程说书人略过不表。

算术运算全部打包也没有几个,如表3.5所示。

表3.5 Verilog语言中的算术运算

例3.6给出了一些除法与取模操作的例子,供大家参考。

【例3.6】除法与取模运算例子

本着“不见棺材不落泪”的理念,贫道偏偏要挑战一下资料里所谓的“可综合性”,于是做了例3.7的代码,用ISE内部综合软件验证。结果是:取模和求幂运算的确是不可以综合的,可是除法运算竟然综合出来了。

【例3.7】算术运算实验(取模和求幂运算已注释)

module arithmetic

input[7:0]a0,a1,

output[7:0]sum,

output[7:0]min,

output[7:0]mul,

output[7:0]div

//,output[7:0]mod

//,output[7:0] pow

);

//Load other module(s)

//Definition for Variables in the module

//Logical

assign sum=a0+a1;

assign min=a0-a1;

assign mul=a0 * a1;

assign div=a0 /a1;

//assign mod=a0 % a1;

//assign pow=a0 ** a1;

endmodule

欣喜之余,多亏在下看了看工具里的显示RTL功能。其他运算还算正常,这个除法的实现如图3.3所示,实在是很复杂啊。图看不清?这就对了,器件太多了。这张图大家看个热闹即可,具体的结构实在没有深入研究的必要。

再来比较一下占用资源数(Number of Occupied Slices)的情况,不要除法是4个,有了除法则为28个。看来这个除法的实现是会占用很多资源的。还要告诉大家的是,通常这样做出来的除法器,时钟频率是做不上去的。

总而言之,“听人劝吃饱饭”,最好还是不要不信邪。我们的结论是:虽然除法运算“/”在FPGA设计里可以实现,但是基于性价比的考量,还是尽量不要用它为好。

图3.3 直接用算术运算符实现除法器的RTL图

二、常数移位简化多

接下来,和大家唠唠Verilog语言中有关移位的操作。从逻辑层面上说,移位操作和算术运算是两类完全不同的东西,似乎分别来讲更为合适。一则,移位涉及的内容不多,作为一回来讲过于短了,听众值不回票价;二来,数字设计中移位用得最多的场合还是乘以或除以2的幂的运算。因此,老比丘把移位的内容也放到算术运算里了。如果哪位施主有意见,请找和尚辩理,老道在这里只是照本宣科。

移位操作分为以下几种。

逻辑右移(>>):1个操作数向右移位,产生的空位用0填充。

逻辑左移(<<):1个操作数向左移位,产生的空位用0填充。

算术右移(>>>):1个操作数向右移位。如果是无符号数,则产生的空位用0填充;有符号数则用其符号位填充。

算术左移(<<<):1个操作数向左移位,产生的空位用0填充。

就这么点内容,看着不难吧?但是,这些是给“语法党”看的。如果您老只想知道这些,以后别说认识贫道。这么说吧,如果只按照前面的几个规则写代码,则肯定会遇到综合软件报错的,而且一定有“按照语法没错啊”的疑惑。

对于组合逻辑,或者说Verilog语法中的移位操作而言,不管是什么移位,移位的位数必须是常数值,否则无法综合。对于移位位数为变量的移位,需要一个叫作“筒形移位器”的结构来实现,由于它涉及时序逻辑,所以在本书的后面进行介绍。

例3.8中给出了一些移位操作的例子。

【例3.8】移位操作

少安勿燥,还没有到“打完收工”的时候。前面说过移位操作和算术运算是有联系的,现在就和诸位摆摆。

按照“从最简单开始”的原则,我们先唠唠一个乘数是常数的情况。没什么特别的考虑,这里姑且假设 b 是常数而无符号,a 是可以变化的。我们专门说这个话题,略微有些头脑的人,都会知道学习元芳的回答:“此中必有蹊跷。”如果您老还是用一个一般的乘法器把输入b连接成一个常数,那就是所谓的“败家孩子”了。只举两个例子供大家参详:

m=17a=(16+1)×a=(a<<4)+a;

m=6a=(8-2)×a=(a<<3)–(a<<1)

其中,“<<”是左移位操作。看懂了吗?看不懂的,那就是祖师爷没赏这碗饭了,别怪我不点拨你。就这样,话不多说,多说无益,点到为止。

前面方法的抽象与简化就是著名的布斯(Booth)算法。这个方法不详细说了,有心人可以看一看《IP核芯志》。

例3.9里给出了一般的“*”和乘以常数17的一个比较。

【例3.9】常数乘法的移位表示

① 一些FPGA具有DSP(数字信号处理)核,可能将乘法器优化到这些硬核里,此时该值为0。

三、符号与否详细说

想当年Verilog还是95版本时,计算有符号数是一个比较复杂的事情。所有的数字编码均由设计者负责,遇到有符号数运算则需要特别地、很小心地处理符号位。稍有不慎,后果不堪设想。

到了Verilog 2001,似乎一切都简单了。语言标准里提供了带符号数的关键词signed,所有的对有符号数的运算代码,符合一般数学上的书写习惯。如果变量声明时,标记该变量为有符号数 signed,还有别忘了位宽,综合软件就会按照有符号数的处理原则完成有关计算。指定比特宽度的有符号数是可综合的,大家可以放心使用。

【例3.10】两个版本代码的区别

对于Verilog 1995里的大括号“{ }”的作用后文书会讲,这个例子大家看看热闹,一起“涨姿势”即可。

无符号数与有符号数之间的转换也是需要计算的,不小心必会出错。Verilog 2001的设计者还是很体贴的,给了大家两个系统函数$signed()和$unsigned()。但是,括号里是常数还好,综合软件就帮你算了;如果括号里是变量,那就是不可综合了。因此,进行电路设计时,还是不用为妙。

四、加法电路内里瞧

前面在下多次强调:“只看代码,不管电路,那是棒槌 ”。算术运算里最简单的要算加法/减法了,对应的加法器的结构也是最简单的。这里就不提减法了,减法一般可转化为加负的被加数处理。至于乘法、除法和取模,要用到时序电路,暂且不表;幂运算的结构更加麻烦,本书都不会说;可变移位位数的移位操作,前文书说了也需要时序电路,因此后文书才会说到。

前面一回书讲逻辑操作时,不晓得你发现没有?老罗汉就给自己留了私货了:全加器和半加器,这两个就是多比特加法器的基础啊。奈何“出师未捷身先死,长使英雄泪满襟”,倒是便宜了小道捡个便宜。罪过,罪过!

接下来,贫僧就来摆一摆这多比特位数加法器的龙门阵。

这里以4比特、无符号数加法器作为例子。最基本的、组合逻辑的加法器如图3.4所示。这个叫逐次进位的加法器,建立时间为各级半/全加器的建立时间之和,简称全加链。当位宽很大时,这种单元可以允许的最高工作时钟频率是很低的。例3.10是对应的代码,用到了前面已经实现的全加器和半加器。

图3.4 4比特、无符号加法器的全加器链结构

【例3.11】4比特加法器代码

module adder_4bits

input[3:0]a0,a1,

output[3:0]sum,

output c

);

//Definition for Variables in the module

wire c0,c1,c2; //Carry bits within the chain

//Load other module(s)

half_adder H0(.a0(a0[0]),.a1(a1[0]),.s(sum[0]),.c1(c0));

full_adder F1(.a0(a0[1]),.a1(a1[1]),.c0(c0),.s(sum[1]),.c1(c1));

full_adder F2(.a0(a0[2]),.a1(a1[2]),.c0(c1),.s(sum[2]),.c1(c2));

full_adder F3(.a0(a0[3]),.a1(a1[3]),.c0(c2),.s(sum[3]),.c1(c));

//Logical

endmodule

在设计里最怕的,这里有“之一”,就是这种串糖葫芦似的组合逻辑了。糖葫芦在数字逻辑设计里,绝对不是好吃的。这个在比较器里已经介绍了,恕不重叙。可以肯定地说,这种结构在位宽变大时,允许的最高工作时钟频率一定会急剧降低。原因嘛,在于需要不停地等待。

山人闭目养神,忽然心血来潮,怦然一动,感受到了强烈质疑的气场。唉,人微言轻啊,看来建立一定的技术权威是必要的。好吧,这里好好解释一下,好让很多人心服口服外加佩服。来,来,来,大家看看,在加法器由4'h0+4'h0={c=0,s=4'h0 }变成4'h3+4'h14={c=1,s=4'h1 }的电光火石时间,到底发生了什么?

假设全加器的处理速度是1 ns,全加器之间的传输线上的时延也是1 ns。我们1 ns、1 ns的看过去,如图3.5所示。图中的斜体数值表示原来的结果,有下画线的数值表示中间结果。另外,为了简单化,不考虑skew的影响。其实,这也是仿真软件的工作方式。捎带说一句,和主题无关不细表可否?

对应输出信号的时序如图3.6所示,不难看出,需要7 ns的时间才能真正得到可靠的结果。7 ns对应时钟140多MHz,也不算太慢了。但是,这才是4比特位宽的情况。如果位宽增加,这个时钟几乎呈反比下降,10个比特位宽的加法器就不到100 MHz了,这可是无论如何都不能说是高速了。当然,脸皮厚的情况除外。

对于这种结构,要想提高工作的最高时钟频率,就要采用时序电路里的流水线方法了。提一句醒,后文书会详细给大家介绍。

有符号数的加法运算要涉及条件判断,也超出了目前内容的范围,且容沙门后面补充。

这正是:

“数字系统半边天,加减乘除舞蹁跹。移位也把亲戚连,放大缩小优化先。符号问题天地变,最新版本放眼前。基础电路加法链,待要加速看下篇。”

图3.5 比特宽度无符号加法器的时序变化

图3.6 全加链结构的时序图 lTjycLqo20Jz7Qdk2iA9FIijCdn9HY9ak+PQtS1ejed1PxbhLc0E4+OoK/hfBMFu

点击中间区域
呼出菜单
上一章
目录
下一章
×