前几章费了好大的功夫才说完了加法器/减法器,后面会稍微加快点速度,今天主要说一下比较器。
三、比较器在FPGA中的实现
比较器可以分为等于/不等于、大于/大于等于、小于/小于等于,从资源和逻辑层级来说,大于、大于等于、小于以及小于等于所使用资源完全一致,所以我们就选取大于比较器进行讲解,同时等于和不等于使用资源一致,我只用等于比较器进行分析。
3.1 等于/不等于比较器
为了分析等于比较器,我们按照4bit位宽为例,用门级电路表示出4bit等于/不等于比较器的结构,其实很简单,就是每位都进行“同或”操作,最终把对比后的结果“相与”,下图所画电路只为等于比较电路,如果是不等于比较器,只需要输出加反向即可。
我们从前面章节了解到,FPGA中的LUT可以实现任何函数运算,那么对于K7芯片,LUT为6输入,所以每个LUT最大可以完成3bit数据的相等/不等比较。
综合电路示意图如下,在vivado中可使用一台LUT6和一台LUT3完成。
由此可以推算出,这种综合方式只使用LUT,并且基本和输入位宽成正比,位宽越大使用的LUT数越多。通过vivado实验,当输入信号位宽大于8后,综合后的电路会改变,和累加器类似,电路会带有CARRY4进位链。这个进位链完成最后一级LUT的功能,由于CARRY4的延迟小于LUT,目的是为了尽量减少延迟,所以是划算的。
下图展示了LUT综合方式和进位链CARRY综合方式不同点。输入信号A和B,位宽均9bit,左边为只用LUT的综合方式,右边为进位链综合方式。对于下图左边,每个LUT6都进行了3位数据的对比,所以9位比较器使用三个LUT6,最后还需要一台LUT3作为与门,将这些中间结果相与,输出最终比较结果。
如果使用进位链,注意:此时LUT6的输出表示不等于。而进位链的选择器逻辑为CO[i+1]=S? DI: CYINT,当S[0]=0时表示低三位都相等,所以输出CO[0]=CYINT=1;当S[0]=1时表示低三位至少有一位不相等,此时CO[0]=DI[0]=0。同理,S[1]=0时表示中间三位都相等,CO[1]=CO[0],意味着低三位相等则CO[1]=1,反之为0;S[1]=1时表示中间三位至少有一位不相等,CO[1]=DI[1]=0。依次类推,最高位也如此。
对于更高位宽比较器而言,前面的LUT逻辑基本都一致,而后级的CARRY4进行了级联,下图表示了13bit相等比较器电路图,原理不多赘述。
总结一下,对于一般相等/不相等比较器,12bit以下都是两级逻辑,位宽极小的使用纯LUT的综合方式,12bit以上,每增加12bit多一级CARRY,参考表格如下。
3.2 大于/大于等于/小于/小于等于比较器
本文只选取’大于’比较器介绍,其余都是类似,占用资源也完全一样。对于单bit的大于比较器,如果用门级电路搭建,则为下图所示,这个电路同时可以输出单比特大于和等于比较,将这个电路作为黑盒,只预留输入输出,当然这个黑盒也可以通过改变输出A>B和A!=B,原理都差不多,后面我们要频繁使用。
如果是2比特的大于比较器,需要将每一比特都进行对比,下图分别为2比特和3比特的大于比较器,分别使用一台LUT4和LUT6。对于2比特输入,先对A[1]和B[1]进行比较,如果A[1]>B[1],或门的其中一台输入为1,那么最终输出也将为1;反之如果A[1]<B[1],或门两个输入都是0,则输出也为0;最后如果A[1]=B[1],表示最高位相等,与门的输出决定了最终的输出,如果A[0]>B[0],与门输出为1,最终输出为1,反之最终输出为0。对于3比特比较器,只是多级联了一级逻辑,原理都是一样的,但是对于FPGA的LUT,是查找表类型,只要输入在规定范围,再复杂的门级电路都只需要一台LUT,所以对于K7芯片来说,4比特以下的大于比较器都只需要一台LUT,大家在评估其他FPGA芯片时,也是需要按照LUT的输入端数量进行计算的,原理都是通用的。
突然觉得用这种LUT综合方式很容易啊,只需要并联LUT就可以解决问题,比如一台6bit的大于比较器,我可以按照下图方式综合,大家可以思考一下是不是正确?
<hr/>给一定空间进行考虑,倒计时——10、9、8、7、6、5、4、3、2、1——”错误!“,开始很容易走进这个误区,上图的电路可以表示相邻3bit是否大于,但是如果出现高3bit相等的状态,是没办法算出正确值的,大家想想是不是这样?
那么正确的答案应该是指什么呢?对了,老老实实级联吧。计算低3bit是否大于,然后对比[3]和[4]位,如果A[4:3]>B[4:3],则LUT5直接输出1。由于K7中LUT最大输入为6,不够了所以要级联LUT,对比A[5]和B[5]……好吧这只是为了省事画综合图的理解,正确的流程应该是反着来的,先对比高位,如果A[5]>B[5],接下来都不用再对比了,直接输出答案就完事了。
这样的综合方式如何?乍一看或是逻辑层级挺高的,如何优化呢?好的方法就是用资源换速度,增加LUT使用,降低逻辑层级,来一起看vivado咋搞的——CARRY综合方式。
vivado好像很喜欢使用CARRY进位链,稍微不用就不舒服,那也没办法,Xilinx的综合方案就是这样搞的,到底哪种综合方式更好想必Xilinx肯定做过评估了毋庸置疑,所以这里我们有必要深入了解一下这种CARRY综合方式,才能在设计代码时知道比较器真正占用资源和逻辑层级。
更大位宽的大于比较器将会使用更多逻辑资源,路径也会更长,和上述的累加器、等于比较器类似,在位宽更大时,vivado选择了延迟更小的CARRY4来替代LUT逻辑,要明白带进位链的比较电路,需要从最简单的1bit说起。如下图所示,这次的”黑盒子“可以输出A>B或者A!=B,CARRY4进位链则作为选择器使用。当S[0]=1,即A[0]!=B[0]时,CO[0]=DI[0],如果A[0]>B[0],则CO[0]=1,反之CO[0]=0;当S[0]=0时,即A=B,此时直接输出CI的值为0,CO[0]=0。如果你只看1bit的大于比较器综合方式,一定会认为Xilinx是不是用1+10-10+1=2去证明了1+1=2?是有点莫名其妙,那我们接着往下看。
为了更省事了解,我再列出2bit位宽比较器综合图,其实和1bit原理一样,也没有觉得有任何省事的地方,因为使用更多的资源实现了相同的功能,会想到刚才展示的LUT综合方式,只用1个LUT就能搞定了,不急不急,再看看。
由上面可以看到对于小位宽的大于比较器,使用延迟链根本没有必要,反而增加了逻辑层级,但是当输入位宽增加后,差距就出来了。比如下图中位宽为8bit的比较器,如果只使用LUT,则需要不断的级联,我们估计一下,如果使用LUT综合方式,是不是需要1个LUT6、2个LUT5以及1个LUT3的级联?那逻辑层级就是4级了,大家可以自行思考一下,这里就不贴图了。
如果按照vivado提供的CARRY综合方式,它会如何搞呢?其实可以将每个LUT作为4输入,先计算出每两位的大小比较,将结果输出,送入CARRY4中,而进位链的好处是有级联的比较器,先考虑最低位[1:0],当S[0]=0,即A[1:0]=B[1:0]时,CO[0]=0;当S[0]=1,即A[1:0]!=B[1:0]时,CO[0]的值取决于A[1:0]是否大于B[1:0]。对于次低位[3:2],当S[1]=1时表示此时A[3:2]!=B[3:2],完全取决的于DI[1],即A[3:2]是否大于B[3:2],不赘述;反而当S[1]=0时表示此时A[3:2]=B[3:2],则需要看低位对比如何,这时需要借鉴低位结果,这个结果就是传过来的地位进位值CO[0],当CO[0]=1表示A[1:0]>B[1:0],从而A[3:0]>B[3:0],反之A[3:0]<=B[3:0]。对于高位比较也如此,不赘述。
通过上述描述可以看出,CARRY4进位链中的多级选择器可以很好的实现逐级对比功能,这在大位宽中非常有用,极大的优化了逻辑层级的猛增,所以位宽大的时候vivado基本都是使用这种综合方式没有任何毛病。但是有一点值得我们去讨论:
vivado为啥不用LUT6进行3bit大小对比,然后再进行CARRY级联呢?这样完全可以节省大量LUT资源。但是遗憾的是,无论我如何努力,把综合策略如何更改,也无法修改它的综合方式,我认为这也许是Xilinx的失误。
既然CARRY综合方式比LUT方式层级少了那么多,那为啥我们还要花篇幅去说LUT综合方式呢?我觉得不管工具用什么样的综合方式,最关键的是我们自个可以去独立分析,当我们考虑多种可能性以后,就不会总停留在一台层次,觉得工具那么高不可攀,它也是人意志的体现,我们也有可能想出比工具更好的方式。
PS:附带一张vivado的综合图,是12bit大于比较器的结果,依然使用了LUT4,让强迫症患者看着几乎奔溃,为啥不用LUT6???。
最后,老规矩,为了省事大家设计,无脑贴出大于比较器的逻辑层级表,同时适用于其他几种类似的比较器,仅作参考,再说一遍不同架构FPGA综合结果不同。 |