当前位置:   article > 正文

FPGA高速设计(二)

FPGA高速设计(二)

  之前我在学习的过程中,在找资料的过程中花费了大量时间,但也找到一些大佬的优质文章,为了减少各位粉丝查找优质资源的时间,在这里开创一个转载个人认为内容比较优质的文章,学习一下大佬阅读、分析手册的思路,提升对FPGA底层、应用的知识。

  如果觉得对你有帮助,可以订阅该专栏,后续会一直转载优质内容。

  以下为大佬文章正文,如有侵权,请通知删除:


  针对FPGA工程师,要设计出好的代码,必须了解FPGA内部的结构,所以开始也就稍微啰嗦一些书本上的基本知识。要说明所有涉及到的实例都是基于xilinx公司的K7芯片,总结出的逻辑层级在其他结构的FPGA芯片不能通用,需要自行计算得出,本文中也会给出计算方法。

一、FPGA内部结构

  xilinx的FPGA基本逻辑单元叫CLB或者CLE(反正含义都差不多),对于K7芯片,每个CLB中有两个SLICE,SLICE分为SLICEM和SLICEL,通常SLICEM中的LUT更多作为分布式RAM进行使用,逻辑主要靠SLICEL来搭建,所以可以认为SLICE才是真正可重用的基本单元。展开SLICE后,发现内部包含4个6输入的LUT、3个MUX、1个4输入进位链和8个FF。SLICE内部结构随着芯片架构不同而不同,下图列出K7和VU9P的LUT结构,可以看到vu9p的SLICE更为强大,拥有8个6输入的LUT,而K7只有4个6输入LUT,别的不说,单凭这一点就是质的改变。(上图为K7,下图为vu9p)

在这里插入图片描述
在这里插入图片描述

  我们深挖一下LUT,LUT翻译过来叫查找表,它的作用是实现所有逻辑函数的功能模块。更通俗一点,我们知道对于ASIC是用门电路搭建而成的,而对于FPGA来说,无论什么逻辑都可以用LUT来实现。数字电路中学过与非门、异或门、选择器等等基本单元,但是LUT可以把这些功能全部实现,只要输入够多,它可以实现任何复杂的bit运算。乍一看确实感觉它很神奇,那为什么ASIC也不这么做呢?我们再简单剖析下LUT的内部结构。

  对于6输入的LUT而言,你可以认为它的本质就是6bit位宽的存储器(不够严谨但是可以这么理解),能够存储2^6=64位数据,如下图所示,6个输入表示6位地址线,一个LUT只有一个输出,这1bit的输出值就存储在存储器中,当输入特定地址后将对应的值读出,所以LUT的延迟比一般的专用逻辑电路要大,而且对于FPGA来说,即使综合出一个反相器,也需要使用一个LUT实现,根本上同样的逻辑, FPGA使用的资源要远大于ASIC。

在这里插入图片描述

  在设计中,大量的资源都是用逻辑来实现功能,而逻辑都是使用最简单的加减乘除、比较、条件判断和循环等运算搭建的,这也是我们使用FPGA设计电路最重要、重复性最高的部分。提到加法器,就会想到进位链,如果学过数字电路,就会知道常用的有串行进位链以及更快速的超前进位链,很不巧FPGA厂商选择了前者,因为可重用行更高。下图显示出K7中的4输入CARRY的结构图,乍一看有点小复杂,我先说一下pin的含义,一会把加法器说完你就会突然觉得这图很简单。这个进位链有4位输入输出,所以每一位运算都是重复的,我们以最低位说起:CIN为上一级传过来的进位值,CYINIT为初始状态时的进位值;S0为加法器中的最低位的和,同时DI0是一个中间信号,如果要计算O=A[3:0]+B[3:0],那么DI0就是A[0]或者B[0]任意一个,后面会详细介绍原因,O和CO分别是加法器的和与进位值输出。

在这里插入图片描述

二、基本运算在FPGA中的实现

2.1 加法器原理

  加法器在数字电路中都学过,没必要花费篇幅去介绍,所以我就直接把公式贴出来。

在这里插入图片描述

  ^表示异或逻辑,+表示或逻辑,AB表示A和B的与逻辑。

2.2 加法器在FPGA中的实现

  先上一张综合图,4位加法器在vivado真正综合后的原理图如下图,可以看到4位加法器使用4个LUT2和一个CARRY4,LUT2表示2输入的LUT。

在这里插入图片描述

  结合加法器的公式,我们知道LUT应该是用于异或计算,所以CARRY4中S[3:0]端的输入应该是{A[3]^B[3], A[2]^B[2], A[1]^B[1], A[0]^B[0]},CI是上一级进位制,CYINIT是进位初始值都是0,但是DI端连接到了A上,上一节解释了其实连接到B上也是可以的,下面我们看看这是为什么?我们将上面的综合图美化一下。

在这里插入图片描述

  其实应该有4个异或门,为了方便只画了一个。上图中的推导比较详细了,比如你要设计进位链的电路,你一开始肯定会想到有S和CI端口,S=0时你推导出输出进位值CO=A或者B;当S=1时你得到CO=Ci,其中Ci表示上一级的进位值。那么一切都变得通顺了,你增加了一个端口DI用于连接输入的任意一个信号,用S端口作为选择器的sel,经过选择就可以直接得到输出进位值CO了。我再把进位链的电路放大版贴出来一遍,现在看上去是不是异常简单,CO0则看作[0]位的输出进位,也是[1]位的输入进位值,最终数据输出O1需要结合S和Ci进行异或运算。到此针对CARRY6或者CARRY8一样的道理,只是多复制了几份级联而已。

在这里插入图片描述

  我们把SLICE内部彻底搞透以后,很容易发现4比特加法器的电路其实只用一个SLICE就可以实现(4个LUT2+1个CARRY4),下面引出逻辑层级的概念,广义来说,两个寄存器之间的组合逻辑所包含的LUT、CARRY、MUX的最大个数可以认为是逻辑层级,我们通常只考虑关键路径(延迟最大的路径),查看综合网表时由于不涉及线延迟,所以逻辑层级最大的那条路径也认为时关键路径。那么4比特加法器逻辑层级是多少呢?很简单就是两级,LUT+CARRY,那对于8比特、12比特甚至更大位宽的加法器应该怎么综合呢?逻辑层级又是多少呢?

  因为每个SLICE只有一个CARRY4,对于8位加法器,进位链就需要级联了,使用一个SLICE实现低4比特加法器,另外一个SLICE实现高4比特加法器,中间需要进位连接,只用把两个CARRY4级联就完成了。所以8比特位宽加法器需要2个SLICE(8个LUT2和2个CARRY4)可以实现,最大级数为3级,最长路径为LUT2–CARRY4–CARRY4;同理对于12位加法器,需要12个LUT2和3个CARRY4,最大级数为4级,最长路径为LUT2–CARRY4–CARRY4–CARRY4。8bit加法器的vivado综合结果如下图所示,两个进位链进行了级联:

在这里插入图片描述

  这里临时插一段,为什么说同样的代码,但是不同FPGA综合出的电路不同,逻辑层级也不一样呢?我们以vu9p举个栗子,大家可以翻到上面看看其SLICE的内部结构,是由8个6输入LUT构成的,进位链也是8位的,提问:如果对于vu9p芯片,设计8比特加法器逻辑层级为多少?答案是两级,因为只使用了一个SLICE,最长路径为LUT2–CARRY8。很明显使用高端芯片不仅工艺上可以减少线延迟,在SLICE架构上也可以减少设计代码的逻辑层级,而且加法器位宽越大差距越明显,比如设计24比特加法器,K7上综合出逻辑层级为24/4+1=7级,但是对于vu9p来说逻辑层级为24/8+1=4级,有时候芯片和芯片的差距就是这么大。

  下表为总结的针对K7芯片的全加器逻辑层级,其实都可以自己口算出来:

在这里插入图片描述

  上面的加法器类型是标准的全加器,但是我们通常写代码的时候用到的加法器并不局限于S=A[3:0]+B[3:0],比如下图中常常用到的两种类型,S=A+1我暂且称为被加数为1的加法器,对于S=S+1则叫它累加器。这个代码其实也是很有迷惑性的,因为只是进行加1的操作,不少同学甚至工作的同行也会认为它没有危害性,殊不知它的逻辑层级也是不容小视。

在这里插入图片描述

  我们先说说累加器,累加器可以当做加法器进行综合,即可以使用LUT2和CARRY4的进位链方式进行组合,也可以只用LUT的方式进行组合。对于小位宽的累加器,如果使用LUT2和CARRY4组合,最高层级为2级,如果只使用LUT则只需要1级。所以对于小位宽累加器,vivado更倾向于用LUT方式综合,4bit的累加器代码和电路图如下所示,共使用4个LUT和4个寄存器。

在这里插入图片描述
在这里插入图片描述

  由于是累加器,其中一个加数不管位宽为多少,另一个加数永远是1,每个周期数据最低位都需要翻转,所以第0位(最低位)的输出OUT[0]经过反相器后再进入寄存器;对于第1位,如果最低位为0,最终输出OUT[1]仍然是上一个周期的值,反之则取反;对于第2位,需要将前两位的结果相与,如果结果为1,表示最低两位为2’b11,则累加后必须有进位,输出OUT[2]与上一周期值取反;同理最高位也一样,一共只需要4个LUT就可以搞定了,看来工具也是会偷懒的。下面贴出vivado综合出的结果:
在这里插入图片描述

  其实这种综合方式在小位宽时比较好用,但是当位宽稍微大一点时,一个SLICE的资源就不够用了,需要将多个SLICE进行级联,而且位宽越大级联越多,导致整个路径的延迟成比例增加。比如当7bit累加器综合时,最高位的计算需要一个6输入LUT和一个2输入LUT级联,更高位宽的依次类推;可推算出对于这种综合方式,N位累加器最大层级为ceil(N/6),使用最多LUT为N+N/6。

  当位宽稍微大一点时,vivado就放弃了这种只使用LUT的综合方式,从而使用CARRY4中的逻辑配合LUT进行计算,暂时我们称为进位链综合方式,将上面的电路称为LUT综合方式。下图举例说明了vivado的进位链综合方式是什么样的,最直观的电路就是下图左边所示,可以看成一个4bit输入和一个4’b0001固定值的加法器,但这样浪费资源,因为对于或门只要一个输入是0,输出都等于另一个输入,所以去掉无用的逻辑,就变为下方右图的电路。
在这里插入图片描述

  所以累加器的这种进位链综合方式突然简单多了,不管输入为多大位宽,永远只需要一个LUT,剩下的就是CARRY的级联,因为CARRY是专用电路,延迟比LUT小,所以这样明显划算很多,下图展示了12bit位宽累加器所占用资源,只需要一个LUT1和三个CARRY级联即可。更一般的情况,针对K7芯片来说,对于N位累加器,使用进位链综合方式,最大层级为1+ceil(N/4),使用的LUT数为N,使用CARRY4个数为ceil(N/4)。
在这里插入图片描述

  为了方便记忆,将累加器的资源占用和逻辑层级也做成表格,综合方式为LUT表示第一种方式,即只使用LUT搭建;LUT+CARRY4表示进位链综合方式,Vivado一般在12bit位宽以上才会使用进位链的综合方式。同理如果你用的是vu9p,根据SLICE结构你可以自己进行运算。

在这里插入图片描述

  其实有些东西越刨越深,越挖越基础,但是当把这些基础的东西融会贯通,写高质量的代码只是手到擒来,我见过很多团队,在FPGA代码review时基本就审核一下格式、命名以及拆分同一个always中看上去很冗余的逻辑,他们真的就认为这是好代码的标准,但是我们是设计电路,不是搞软件开发,代码的可读性很重要,但是有时候不是多拆几个always这种表面的东西可以让设计变得更好的,因为还是需要从最底层思考。今天写不完了,就光加法器还有很多东西要总结,后面还会有比较器、选择逻辑等等基本逻辑的深挖,同时会专门写一下对于加法器这类逻辑怎么进行流水拆分和真正的示例,估计还得写个4-5次吧,逻辑写完后看时间再分享一些设计架构和约束方面的东西。

  最后连接上一篇谈一谈if-else,因为FPGA综合if-else逻辑并不会占用太多逻辑层级,那是不是可以随便用了呢?其实是有限制的,因为if或者else if中的判断逻辑会决定整体的逻辑层级,比如下图中的代码,很多同学经常这么写,虽然只有两级if-else,但是这中逻辑对高速设计是要命的存在,首先A[23:0]+B[23:0]是一个加法器,对K7芯片来说需要7级逻辑,然后又要判断A+B是否与C相等,又是一个24比特相等的比较,后面会介绍这也会占用不少逻辑层级,而且这两个逻辑的层级会叠加,如果一个项目中大量使用这种设计,这一定是灾难性的。其实知乎中很多同行说写代码时心中有电路,但起码看到代码大脑能很快浮现出综合图是不夸张的。今天太晚了先不写了,下回贴上这个代码的综合图和分析。
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/739275
推荐阅读
相关标签
  

闽ICP备14008679号