这篇博客,我们来回归FPGA设计的最中心问题即对底层资源使用的细节性问题,在FPGA设计中经常会采用存储、计算,再存储、再计算等类似的设计理念,而这也会很好地映射在FPGA内部架构上,即映射在对应的存储单元Block RAM、可配置逻辑单元LUT、基本逻辑单元LATCH和FF、计算单元DSP48E1等,博客的最后将重点分析Xilinx 7系列的FPGA底层重要单元,从而印证底层资源对FPGA项目工程的系统性能起到决定性作用,帮助大家建立一个整体性的概念,这也将对未来的成长提高有着深远的意义。
下面笔者用最通俗易懂的语言为大家逐一详细地介绍这四大类FPGA底层资源,在系统学习实践了前面小节的知识,这时再回归底层资源才会有更准确地理解,相反地如果刚开始就一股脑地翻阅各类FPGA书籍陷入各种底层资源分配的困扰,那么在学习路线上就本末倒置了,这就好比学习STM32的时候先去对着手册把各个寄存器说明从头看到尾,但到最后仍然离熟练应用相差很远,甚至都不知道STM32要干什么,因为在学习路线上一开始就走错了,所以导致很难入门和深入提高。
但是如果沉淀了一定的代码量和项目经验,这时候再回过头带着问题去看底层资源的问题,那么不管是FPGA也好,STM32也好,相信朋友们一定会收获到很多,例如FPGA方面,通过这篇博客的介绍,大家明白了底层资源是这么一回事,我们写的Verilog硬件描述语言映射到了哪些底层逻辑资源,这样就建立起了一个整体性的概念,对于以后的研发工作大有裨益。类似的对于STM32,大家在积累了一定的工作经验,再带着问题去看底层寄存器,堆栈等相关知识,对于深入理解STM32就会有很大的帮助,同时在排查程序问题的时候会非常便利。
随着科技的发展,现在的芯片厂商为了方便用户快速二次开发,上层的生态系统做得都非常用心,比如Xilinx推出的Vivado开发环境真的很人性化,开发工具就帮助用户把Verilog硬件描述语言层层编译,最后映射到底层逻辑资源上,还给出了各种时序报告、器件资源使用报告等,类似的ST公司推出了hal库也为用户的二次开发提供了很大的便利,这就是说一方面芯片厂家本身提供的开发环境、固件库等帮助用户做了很多底层要做的事情,但另一方面如果用户有了一定的项目积累再去了解这些细节,那么这时候选择回归了底层,将会很好地建立一个整体开发的概念。
一、可配置逻辑单元LUT
说到LUT(Look-Up-Table),可能大多数FPGAer的第一反应即它只是一个RAM查找表,这可能就是很多FPGA教科书给大家带来的刻板印象,LUT可以说是FPGA器件内最为丰富也最为重要的底层资源,以至于各大FPGA芯片厂家也经常将它作为衡量芯片容量的关键性指标,大部分情况下LUT作为逻辑函数发生器以实现逻辑运算,通俗地说LUT这时候可以被看成真值表,即每当输入一个信号就等于输入一个地址,然后需要对该地址进行查表后,再输出该地址所对应的内容。
举个例子,例如Xilinx 7系列FPGA器件都使用了6输入的LUT,所以可以把它看成一个有6位地址线的RAM,每当用户通过Verilog硬件描述语言描述一个逻辑电路后,FPGA的开发环境都会自动计算逻辑电路的所有可能结果,并把结果作为真值表先写入RAM,这样每输入一个信号进行逻辑运算对于FPGA器件来说就等于对输入的地址进行查表,再把查表后对应的内容输出即可,这也就对应了FPGA里组合逻辑电路的实现。
Xilinx 7系列FPGA里的可配置逻辑块CLB(Configurable Logic Block)中包含了两类CLB,即CLBLL和CLBLM,两者的区别在于CLBLL包含了两个SLICEL(L指的是Logic),而CLBLM包含了一个SLICEL和一个SLICEM(M指的是Memory),SLICEL和SLICEM中均有4个LUT6、数据选择器、进位逻辑和8个触发器构成,这也对应了一个可配置逻辑块CLB中的资源量,如图14-9所示是SLICEL和SLICEM的宏观架构,而SLICEL和SLICEM的区别主要体现在LUT的功能上,如表14-2所示,是两者内部LUT的功能对比,可以看到SLICEM内部的LUT不仅仅具备LUT最基本的功能即用作逻辑函数发生器,也可以配置成分布式ROM、RAM以及移位寄存器等。
LUT功能 | SLICEL | SLICEM |
逻辑函数发生器 | 支持 | 支持 |
分布式ROM | 支持 | 支持 |
分布式RAM | 不支持 | 支持 |
移位寄存器 | 不支持 | 支持 |
表1 SLICEL和SLICEM中LUT的功能
图1 SLICEL和SLICEM的宏观架构
Xilinx 7系列FPGA器件中,SLICEM中的1个LUT6可以配置成1个64bit的分布式RAM,那么1个CLBLM中的4个LUT6就可以配置成1个256bit的分布式RAM,并且这个分布式RAM可以是单口RAM,也可以是双口RAM。同样的道理,SLICEM或SLICEL中的LUT6也可以配置成1个256bit的ROM表,对比前面小节介绍的用Block RAM配置生成RAM、ROM等IP核,当数据规模较小的时候,用LUT配置Distributed分布式RAM相比Block块RAM具有更大的优势,更节约FPGA器件的内部资源,因为Block RAM即BRAM资源必须是整块整块使用的。
最后在Xilinx 7系列FPGA器件中,1个LUT6也可以配置成1个32bit的移位寄存器,如图14-10所示,在这里可以实现1-32个时钟周期的延迟,即实现了SHIFT RAM移位寄存器的效果,这里A[4:0]作为输入端口,当其值为0时,对应了1个时钟周期的延迟,当其值为31时,对应了32个时钟周期的延迟,这里也有点类似于FIFO的功能,所以Xilinx 7系列FPGA器件中,可以使用Block RAM的方式也可以使用LUT的方式配置。
移位寄存器本质上来说就是一种数据延迟的实现,Xilinx提供了专门的IP核实现,即前面小节所介绍的SHIFT RAM,专门用于生成移位寄存器,相比于纯LUT实现,SHIFT RAM做了更多的内部资源优化。
图2 SLICEM中的LUT6配置成移位寄存器
二、基本逻辑单元LATCH和FF
介绍完FPGA器件最关键的可配置逻辑单元LUT后,就不得不说LATCH和FF了,LATCH即锁存器,它是电平触发的存储单元,数据存储的动作主要取决于锁存信号的电平值,同时如果if 语言或者cace语言不完整的话,也都会被开发环境最后综合推断出latch,从而导致系统具有不稳定性。
FF( Flip Flop)即触发器,其中DFF也就是众所周知的D类触发器,如图3所示是D触发器的示意图,因为它是边沿触发的,所以被归为时序逻辑,实际上说到底,锁存器也好,D触发器也好,它们的逻辑功能都是非常相似的,都有着暂存数据的功能,只是锁存器是电平敏感,而D触发器是边沿敏感,FPGA器件中D触发器的资源远远多于锁存器的资源,同时因为锁存器无法滤除毛刺,这就使得后期在做复杂项目工程时,时序分析更加困难,所以在FPGA设计当中应该尽量避免锁存器。
图3 D触发器的示意图
三、存储单元Block RAM
Block RAM即BRAM在FPGA设计当中扮演了一个重要角色即数据存储或者数据缓存,同时作为存储器,其类型可以是单口RAM,真双口RAM、单口ROM、双口ROM等,另外Block RAM也可以配置成FIFO使用。
在Xilinx 7系列FPGA器件中每个BRAM块都达到了36Kb的大小,它是由两个18Kb的BRAM拼接到一起得到的,这里每个36Kb的BRAM均可以当成一个独立的36Kb BRAM或者FIFO使用,也可以当成两个独立的18Kb的BRAM使用,或者当成一个18Kb的BRAM和一个18Kb的FIFO使用,如图4所示是36Kb的BRAM可以配置类型。
图4 36Kb BRAM的可配置类型
目前,Xilinx的主流中高端FPGA芯片里,在储存器方面,36Kb的BRAM已经成为了主流,其中BRAM在FPGA程序设计中也有很多经典的应用方式,例如:1.把BRAM配置成一个双口RAM或者两个独立的单口RAM;2.使用BRAM来实现数据的延迟,虽然在Xilinx 7系列的FPGA芯片上使用CLB中的LUT都可以实现数据延迟效果,但对于大位宽,深延迟等应用领域,使用整块的BRAM资源无疑是最好的选择;3.把BRAM配置成双口ROM优化数据存储;4.使用BRAM资源实现复杂的状态机或者计数器设计;5.将BRAM配置成ROM,再把其当成一个大型LUT资源,从而完成逻辑运算的查表法实现。
四、计算单元DSP48E1
乘法运算是数字信号处理、人工智能实现等应用领域最广泛的基本运算,所以乘法器在FPGA设计中就有了非常重要的意义,Xilinx公司也随着芯片的迭代更新,从最初植入芯片的18*18嵌入式乘法器,到具有更多的DSP48模块,再到后续的DSP48E模块,最后Xilinx 7系列FPGA器件中植入了功能更加趋于完善的DSP48E1模块。
当然如果把整个芯片跟新迭代的进程,以及DSP48E1所有应用场景都逐一列举出来,那可能写好几节内容都无法描述清楚,也完全没有那个必要,所以在这里只做简要介绍,使大家对其有个整体性的了解即可,如图5所示是DSP48E1的基本结构,其中带“*”的信号代表该信号是级联信号,有专门的布线资源,观察下图可以看出DSP48E1可以划分为5个部分即:对外端口、预加器、乘法器、逻辑运算单元和模式检测电路。
DSP48E1的详细说明可以参考Xilinx 的官方手册ug479_7Series_DSP48E1,但是需要重点说明的是DSP48E1不仅仅是乘法器,使用Verilog硬件描述语言时,涉及到乘法、乘加、乘减和乘累加运算会被Vivado环境层层编译,最后自动映射到DSP48E1资源上,而通常的加法、减法和累加运算默认情况下会用常规资源实现。
图5 DSP48E1的基本结构
DSP48E1在Xilinx 7系列FPGA器件上通常有三大类应用,典型的有:1.DSP48E1可以用在逻辑运算上,其中包括了基本逻辑运算、计数器实现、数据选择器MUX实现等;2.DSP48E1可以用来做基本的数学运算,其中包括了有符号数乘法运算、有符号数加法运算、有符号数加减法运算动态切换、乘加运算和乘减运算(P=C+A*B和P=C-A*B的运算形式)、取绝对值运算等;3. DSP48E1也可以用在高级数学运算方面,例如累加运算、乘累加运算、复数乘法运算((a+bj)x(c+dj)=(ac-bd)+ (ad+bc)j等的运算形式)。
如图6所示是DSP48E1的结构简图,通过这个简图,大家可以大概了解到为什么DSP48E1实现这么多种类的数学以及逻辑运算。
1处是一个预加器,它主要实现的是最大位宽是30的A端口与最大位宽是25的D端口的相加运算,做预加运算的时候,此处会输出最大位宽是25的结果,这个预加器不用的时候可以旁路省略。
2处是一个25x18乘法器,它的两个乘数分别是最大位宽是18位的B端口,以及A端口与D端口相加后结果的低25位,输出的结果为48,同样的这个乘法器不用的时候可以旁路省略。
3处是一个的功能比较多。它能做加减法器,累加减法器,逻辑运算等。
4处是一个模式探测器,这里主要是实现了上下溢出的检测,具有计数到一定值的时候对结果进行重置功能。
5处是一个数据选择器,它的两个数据输入端口为C端口和P端口,所以DSP48E1可以实现普通加法还是做累加运算。当然DSP48E1里面有很多数据选择器,它们与DSP48E1所实现的功能密切相关,这里笔者不展开过度说明了,有兴趣的朋友们可以参考官方手册做进一步的研究。
图6 DSP48E1的结构简图