FPGA的开发遵循一定的流程,我们要学会站在巨人的肩膀上进行FPGA开发,听取前人的经验,这篇博文摘自《FPGA之道》,一起来学习下作者对于FPGA开发的丰富经验。
FPGA项目的开发应该遵循一定的开发流程。
针对一般的FPGA项目来说,简要的开发流程框图如下:
结合上图我们可以看出,当开始开发一个FPGA项目的时候,首先需要进行前期的一些调研和验证工作,然后制定FPGA设计的方案,这之后才能进行FPGA代码编写并以几乎并行的时间开始功能仿真工作。跟据功能仿真的情况,可能会不断的去修改之前编写的FPGA代码,直到所有的功能仿真通过之后,才能进入到时序分析环节。若在时序分析环节中出现了问题,则需要重新返回到FPGA代码编写环节进行修改,如此往复直到时序分析环节通过,然后便可以进行上板调试工作。若在调试的环节出现了问题并确认需要修改FPGA设计代码,则重新返回到FPGA代码编写环节,如此往复直到上板调试通过,方可确认项目结束。
对于非一般的FPGA项目开发,例如SOPC相关的FPGA设计等,则需要结合其特殊性,在一般的FPGA项目开发流程的基础上做出修改、添加、删除或调整,从而灵活应对。
接下来的部分,我们将结合一般FPGA项目的简要开发流程框图,详细的介绍各个流程环节的内容、方法和注意事项,让大家对FPGA项目的开发先有一个整体上的把握。
每一个FPGA项目的诞生都是为了解决一些现实中碰到的问题,因此每一个FPGA项目都有着自己对应的背景知识。这些背景知识可能包括数学、物理、化学、生物等一级学科领域,也可能是更加具体一点的通信、控制、图像等二级甚至交叉学科领域。由于我们不太可能在做FPGA项目之前对这些领域都有所涉猎,因此要想做出满足要求的FPGA设计,就少不了对相关背景知识的学习、掌握和分析。
这一环节所消耗的时间根据FPGA项目相关理论深度的不同而不同,一般来说,数据缓存、简单控制类的FPGA项目在这方面消耗的时间较少,而类似调制解调、视音频压缩等有着较强理论背景的项目,在这方面消耗的时间就会比较长。
在进行背景知识学习的时候,可以“不求甚解”。
项目方案是FPGA基本开发流程中第一个以文档为输出的环节,项目方案做的好与坏,直接决定了整个FPGA项目开发后续流程的顺利与否。对于一些比较简单的项目,例如用FPGA实现一个异步串口等,可能项目方案的作用不是很明显,甚至还会让人觉得有些多余和麻烦,这也是很多FPGA开发者在平时进行FPGA项目开发的时候都没有写过项目方案的一个主要原因,但是一旦项目上了规模,或者具有了一定的理论深度,那么项目方案的存在就显得十分必要了,否则以后的返工可能会对开发者或开发团队的信心造成毁灭性的打击。
项目方案其实也是计划书的一种,虽说计划赶不上变化,但是没有计划又是万万不行的。在经历了对所接手项目背景知识的分析与研究之后,通过项目方案的设计与制定环节,可以让开发者们对项目有一个全局的、原理性的掌握,并且可以提早发现项目中潜在的一些漏洞,从而最大程度的避免因为后续开发环节中出现一些不可解决的问题而导致的返工。
项目方案的设计与制定是一个比较自由的环节,对于最后输出的文档也没有非常严格的格式规定,但是作为整个项目的指导性文档,在设计与制定项目方案的过程中还是应该把握住一些基本原则的。
项目方案第一步,就是要写明白为什么要做这个项目?想解决什么问题?做出来有什么好处?它有什么应用前景?等等一系列问题。因为通常来说,工程师都是服务于公司或者企业,而利润是所有公司、企业的终极目标,因此如果是一件对公司或企业没有任何好处的事情,那么别指望老总会为此投入人力、物力和财力。因此,为了能够让项目顺利开工,讲清楚这个项目的背景是很有必要的,当然自己就是老总的除外。
要写清楚项目背景,光解决上述的一系列问题还是不够的,因为这些问题都仅仅着眼于项目自身,兵家常说,“知己知彼,方能百战不殆”,所以还需要对行业中相关的公司和企业进行调研,分析一下行业目前的形式和竞争对手的威胁程度,看看别人在做什么,做到什么程度,它们有什么优势、有什么劣势,自已有什么优势、什么劣势等等。否则自己千辛万苦做出来一款产品,发现市场上别的公司的类似产品早都卖疯了,到时候就只有哭了。
项目需求主要讲的是这个项目需要完成什么样的功能, 相对于项目背景来说,项目需求要写得稍微详细一些。
项目需求子环节解决了做什么的问题,而怎么做的问题将由方案框架和算法细节两个子环节来协作解决。 解决怎么做的问题,其实就是寻求项目解决方案的过程,这是整个项目方案的设计与制定环节的主要目的,也是整个项目开发过程中的指导与依据。如果说背景知识是FPGA项目这棵小树生长的土壤,那么解决方案就好比是一个园丁,为小树的生长搭好了支架,在保护它的同时还能够让其沿着正确的方向生长。
方案框架子环节主要负责从整体、宏观上解决怎么做的问题。它的好处就是能够让人一目了然,从而尽可能快的捕捉到解决方案的设计思路。为了达到这个效果,方案框架最好包括以下几个内容:
算法细节是解决方案的又一重要组成部分,它是对方案框架的重要补充。 与方案框架正好相反,算法细节主要从细节和微观出发,解决怎么做的问题。这种解决不是概念上、原理上的解决,而是十分具体的解决。例如,如果我们下班到家后发现没有带钥匙,于是悲剧发生了,但是总不能坐以待毙吧,此时面对目前的囧境,方案框架就解决了到底是撬开门进入、翻窗户进入、打110求助还是返回公司取钥匙等等选项的选择。如果一旦确定了翻窗户作为方案框架,那么接下来,算法细节就解决了具体怎么翻窗户的问题,例如先敲开邻居家门,然后取来安全绳绑于腰间,另一端由邻居拿住,然后从邻居家的阳台踩着空调支架进入自家阳台等等。
从上面的分析可以看出,算法细节一定要清晰、详尽,最好能够细分为多个步骤,并且保证只要按照这些步骤一步步来,就能解决问题。
项目方案是否可行,首先在乎于其逻辑上的完备性。如果制定出来的方案,本身就不能自圆其说,弄得前后矛盾、疏漏百出,那么后续的工作也就没有进行下去的必要了。这也是为什么如果仅仅在脑子里面有个大概思路就开始项目的开发是很容易返工的。
要想确保项目方案的逻辑完备性,就要像做数学证明题一样,保证方案的每一步都有理有据,步与步之间的推理也要做到充分甚至必要。 这样,逻辑完备了,方案才有进一步往下付诸实施的意义。
项目方案一定要是实现无关的,这里的实现主要指的是使用某些具体的软、硬件或其开发工具来进行设计。 例如,可以在项目方案中出现类似这样的描述——“使用勾股定理计算直角三角形斜边的长度”,但是最好不要出现类似这样的描述——“在FPGA上分别利用两个DSP硬件乘法核资源实现乘方运算,然后将结果相加后,再调用开根号IP核即可得直角三角形斜边的长度”。那么为什么项目方案要是实现无关的呢?
所谓书面易懂性,其实是为了区别原理易懂性而言的。前面说过,找出解决方案是项目方案设计与制定环节的主要目的。而解决方案是解决怎么做的问题,并不解决为什么这么做的问题。为什么这么做,其实是一个很难的问题,它蕴含在背景知识的分析与研究中,甚至即使项目成功了也都没有弄清楚。例如,你查到一组滤波器的系数具有很好的过渡带迅速衰减特性,如果仿真后确实是这样,那么你就可以在你的项目中使用了,至于为什么这组系数这么好,其实是没有太多的必要去花时间研究的。这也是做工程与做理论的根本区别之一。
那么,项目方案为什么一定要具有书面易懂性呢?这要从三个方面来说:
随着FPGA项目所基于的理论知识越来越丰富、越来越深入,算法可行性仿真与验证环节的重要性就越来越明显。下面的部分,将分别从“为什么”、“何时做”和“怎么做”三个方面对该环节进行较为详细的阐述。
虽然进行了充分的背景知识调研,并且也给出了具有逻辑完备性的项目方案,但是理论与实践之间还是存在着不小的鸿沟,尤其是一旦涉及到了一些具体的算法,必须要进行认真的仿真与验证,否则就到了“尽信书不如无书”的境地。那么理论与实践之间到底需要注意哪些问题呢?
一、精确度!
现实中的信号往往都是以模拟的为主,而我们的FPGA或者PC机都只能处理数字信号,这也是为什么人类发明了AD和DA芯片。从模拟到数字的转换,一般来说需要经过采样和量化,这就涉及到了时间精度和幅度精度等等的问题;从数字到模拟,一般来说需要经过转换与内插,这也涉及到了时间精度和幅度精度等等的问题。
举个例子来说明一下。假设我们要在FPGA上实现一个简单的函数功能,例如
f(x) = x;
如果需要求得 x = π 时的输出,那么就会碰到精确度问题。首先,π = 3.1415926……,是一个无线不循环小数,那么我们该怎么将π输入到FPGA芯片中去呢?其次,通过函数表达式,我们知道f(π) = π,那么此时输出也是一个无线不循环小数,那么我们该怎么将计算结果π输出给外界呢?由此可见,数字系统只能对模拟环境进行一个近似计算,至于需要近似到什么程度,完全取决于系统的精确度要求。
二、准确度!
其实学术界也远没有大家想象的那么崇高、圣洁,这其中也不乏很多鱼目混珠之徒,当你从网上或者一些文献、期刊中淘到一些理论算法后,千万不要太相信它所承诺的性能指标和应用效果。如果自己不首先进行一些仿真和验证的话,等到最后才发现上当受骗那可是非常悲惨的一件事情。
三、细节度!
“纸上得来终觉浅,绝知此事要躬行”!有些事情看起来是一回事,做起来又是另一回事。我们从事FPGA项目的开发,最终所有的算法都是需要在FPGA上实现的,因此对这些算法的了解不能流于表面,必须要通过前期的仿真与验证,才能把握住算法的一些具体细节。例如,解说球赛的人往往不怎么会踢球,就是这个道理。
四、待定度!
待定度是指需要结合具体问题才能确定的一些量度。例如,“增加FIR滤波器的阶数可以让滤波效果更好”,但是具体增加到多少阶才能达到项目的要求呢,这个只有结合我们项目的实际情况才能判断,所以需要通过前期的仿真和验证来确定。对于一些算法,软件的仿真不一定很全面,但是可以比较快给出基本结论,而如果直接在FPGA上进行尝试,虽然FPGA的并行性使得其处理速度很快,但是编译FPGA的工作还是得由PC机来完成,因此对于复杂一点的项目,即使比较微小的改动,编译一次恐怕就需要消耗好几个小时,所以时间方面真的是可以将人拖垮的。
五、选择度!
选择度是一种权衡的考虑。例如,要对一幅图像进行锐化处理,有的算子效果好但是计算复杂度高,有的算子效果略差但是却计算简单。那么,此时就需要对两种算子的效果分别进行仿真,根据系统要求来选择合适的算子来实现。
六、仿真度!
以上几点主要是从算法本身出发来考虑的,除此以外,算法可行性仿真与验证环节对今后的FPGA设计的功能仿真环节也是非常有帮助的。例如,需要用FPGA实现一个复杂的编码算法,那么我们怎么知道功能仿真的时候输出的码流到底对不对呢?很简单,如果前期我们做了足够充分的仿真与验证,那么,对于任意的原始码流,只要用我们编写的仿真、验证程序一运行,就可以知道正确的输出码流是什么了!
那么,什么时候需要对FPGA项目进行算法可行性仿真与验证环节呢?这自然是在项目方案确认之后以及FPGA正式设计开始之前了。因为项目方案确定了,采用的算法才能基本确定,这时候才知道该去仿真什么算法。而算法可行性得到验证以后,FPGA设计才能正式开始;否则后续的一切工作都是没有意义的,此时必须重新返回到项目方案的设计与制定环节,甚至背景知识的分析与研究环节,重新开始工作。
当然了,并不是所有FPGA项目中都必须要进行算法可行性仿真与验证环节的,对于一些简单的项目,例如实现一个异步串口,这其实就没有什么算法上的难度。但是,随着FPGA芯片应用的领域越来越广,实现的功能越来越复杂,目前大部分FPGA项目中还是缺少不了这样一个环节的。如果你的FPGA项目碰到【Why?】小节提到的任意一点问题,那么建议你最好对算法进行一下基本的仿真和验证。
知道了为什么要对算法进行仿真与验证,也了解了对算法进行仿真与验证的恰当时机,最后,我们来看一下该如何对算法进行仿真与验证。
终于可以正式开始FPGA上的设计了,书本前的你是不是早已摩拳擦掌、迫不及待了呢?不过请先别着急,作家在进行文学创作的时候都要先列提纲,那么我们在进行FPGA项目的代码编写之前也要先将FPGA的设计方案写出来。当然,对于一些较简单的项目,这一环节的作用可能并不十分突出,但是随着FPGA项目的越来越复杂,这一环节的重要性也就越来越明显。
FPGA设计方案的制定环节是FPGA基本开发流程中第二个以文档作为输出的环节。这个文档,更多的时候是写给自己看的,因为FPGA方案的制定过程,其实就是一个FPGA设计构思的过程,所以如果你写不出这个文档,那么你就不可能做得好这个设计。如果是团队开发的话,那么这个文档更是其它FPGA开发者在进行具体代码编写时的指导,更加事关整个项目的成败。
在写HDL代码之前先进行FPGA设计方案的编写有以下几点好处:
编写FPGA设计方案建议参考以下步骤:
终于到了编写HDL代码的环节了,该环节的输出是整个FPGA设计的核心代码,相信大家心情一定非常激动,不过还请先稍安勿躁,因为写代码可不是一件简单的事情,它是很有讲究的。这里简要的介绍一些概念与注意事项。
一、层次化、分模块的编程思路。
写好一个包含一万行代码的模块很难,但是写好一百个包含一百行代码的模块却相对来说简单很多。这就是为什么我们在开始功能代码的编写前先要经历FPGA设计方案制定环节。在制定方案时就将FPGA设计层次化、模块化,从而细分为功能纯粹、单一的各个子部分,这样在代码编写的时候,就可以集中经历去逐个攻克每个子部分,从而最终完成整个设计的开发。
二、编写代码都可以使用什么工具?
HDL代码其实就是普通的文本文件而已,所不同的就是文件名的后缀一般为“.vhd”或者“.v”,因此,我们可以使用任何文本编辑软件来进行HDL代码的设计。不过为了增强HDL代码的可阅读性和可理解性,推荐大家使用能够识别HDL语言关键字的文本编辑工具,例如添加了HDL语法库的Ultraedit。不过,通常情况下,如果我们已经决定了使用哪一个公司的FPGA芯片,那么最好还是使用该公司提供的FPGA集成开发环境来进行代码的编写,例如Xilinx公司的ISE、Altera公司的Quartus等。除此以外,一些专业的FPGA仿真软件也都能很好的支持HDL代码的语法高亮显示,例如Modelsim、IUS等。
三、编写代码要考虑周全。
HDL代码作为FPGA设计的核心载体,一定要认真对待,不可一蹴而就。在进行代码编写的时候,仅仅从设计的功能上出发是远远不够的,还需要考虑到代码对于FPGA片上资源的调配以及对时序指标的影响等。例如,如果我们已经知道系统主时钟的速度比较高,那么在代码设计的时候就要尽量避免编写延迟比较大的复杂组合逻辑,否则等到时序分析的环节发现成千上万条时序问题时,会让人产生重写代码的冲动。 因此,编写代码的时候要具有全局观,要重视代码,因为代码是设计的根本,如果代码写不好、写的欠考虑,那么后续环节的问题就会铺天盖地袭来。
四、编写代码要果敢。
第三点说了,编写代码要考虑周全,但也不能因噎废食。虽说代码编写不能马虎,但也不能畏首畏尾,迟迟不敢落笔。要对自己的代码有信心,当然这是需要经历一定程度的磨练的。请记住,好的代码不是完美的代码,而是高效的代码,因为完美仅仅指的是代码质量,而高效却体现在两方面——时间和质量。在现今这样一个竞争越来越激烈的环境下,能够最快的实现项目所要求功能的代码才是最好的代码,而完美的代码,具有一定经验的FPGA开发者来都能够写得出来,只不过需要一定的时间罢了。因此我们在进行HDL代码的编写时,也不必太过拘泥于一些小节,在保证代码质量没有明显下降的前提下,追求时间上的效率才是关键。
五、注意备份与版本控制。
当FPGA设计上了一定规模后,代码的编写往往需要消耗比较长的时间,除此以外,HDL代码还会在后续一些环节的影响下进行一些必要的修改。那么,此时对于代码的备份和版本控制就显得非常之重要。目前来说,比较推荐的版本控制软件为SVN,它的功能非常强大,操作也比较简单。除此以外,打压缩包也是一种比较原始的备份办法,不过此时我们需要配合Beyond compare来判断两个版本间的代码变动情况,并且最好配合有相关的文档来对每个版本的代码改进情况进行说明。
如果说FPGA功能代码的编写环节输出的代码量并不一定是FPGA基本开发流程中输出代码量最多的环节,你可能会不相信,那么下面我们就从几个方面来简单了解一下FPGA设计的功能仿真环节——一个同样以代码作为其有效输出的环节,更为详细的介绍请参阅本书的【功能仿真篇】。
既然本小节的题目叫FPGA设计的功能仿真,那么说明应该还存在着用不同定语修饰的其他仿真类型。
没错,笼统的说,FPGA设计中的仿真共分为两大类,即前仿真与后仿真。前仿真一般也叫功能仿真,目的是从逻辑上分析HDL代码所描述电路的正确性,它的仿真速度远比后仿真要快,我们可以根据需要来观察电路输入、输出端口或者HDL代码内部任一信号和寄存器的波形。后仿真一般也叫时序仿真,它是将电路中门延迟的参数和各种电路单元之间的连线情况考虑在内后进行的仿真,因此得到的仿真结果更接近真实的应用情况。不过由于后仿真时考虑了大量的时序信息,所以后仿真的速度相对于前仿真要慢得多。并且为了考虑时序信息,必须对HDL代码进行综合、布局布线等操作后才能得到电路的情况,所以HDL代码中的内部信号与电路中的对应关系不是那么明显,甚至会找不到对应关系,因此在观测内部节点波形时也会比较困难。
如果再细致一点,FPGA的仿真可以进一步划分为四类,即代码级仿真、门级仿真、映射后仿真以及布局布线后仿真。其中,代码级仿真,仿真的对象就是原始的、未经加工的HDL代码,这也就是我们通常所说的功能仿真;门级仿真,仿真对象是HDL代码经过综合后的门级网表;而映射后仿真,仿真对象是门级网表经过转换后对应到FPGA上的具体逻辑资源聚类,此时可以得到电路中的门延迟信息文件供仿真时使用;最后,布局布线后仿真,仿真对象是具有位置信息和连线信息的FPGA上逻辑资源聚类,因此可以兼并考虑门延迟与线延迟信息,这也就是我们通常所说的时序仿真。
功能仿真环节的作用,顾名思义,就是要确保HDL代码功能上的正确性。它主要体现在两方面:
功能仿真环节不是一个独立的环节,它必须与功能代码的编写环节紧密交织在一起。首先,功能仿真发现的问题必然要返回代码编写环节中去修改,因此不断发现问题就不断要修改然后再通过功能仿真确认修改。其次,如果你打算编写完整个FPGA项目的所有HDL代码后再进行功能仿真——引用一位友人的话——到时候不是你搞定问题,而是问题搞定你。因此,推荐的功能仿真介入时机是每完成一个模块的编写就着手一个模块的功能仿真,完成了若干个功能相关模块的编写和单独功能仿真后,就着手该功能聚类的功能仿真,以此类推,直到完成顶层仿真。
一般来说,FPGA厂商自带软件集成开发环境中就包含了用于功能仿真的工具,通常来说,对于一般的需求,这些仿真工具就能应对自如了。
不过样样行,就难样样精,所以比起那些专业做仿真工具公司的产品来说,这些集成开发环境自带的仿真工具就难免显得相形见绌。专业的仿真工具中也有三六九等之分,一般来说,比较优秀的仿真工具多是运行于Linux环境下的,这主要是由于Linux系统的效率较高,因此仿真速度较快,不过这也要求使用者必须具备一定的Linux基础,例如Cadence公司的IUS仿真工具。稍逊一点的仿真工具多是运行于Windows环境下的,由于Windows的一些先天不足,导致仿真的性能受到一定限制,不过由于Windows庞大的用户群,这类仿真工具更易上手一些,例如Mentor公司的Modelsim仿真工具。
不过仿真工具终究还是软件,因此其执行必是串行的,而FPGA中的电路是并发执行的,以串行模仿并行,时间上面肯定会有巨大的膨胀效应,因此对于稍微复杂一点的设计,要想通过功能仿真对HDL代码进行相当于实际1秒钟的仿真,可能需要消耗1天以上的时间。所以,随着软件工具的这种制约越来越明显,各大公司便相继推出了硬件原型仿真加速器产品,这种产品基本上可以将仿真时间与实际时间缩小到同一个量级上,但整套系统配置与使用都是非常的麻烦,并且价格也往往昂贵的离谱,例如Cadence公司的Cadence Incisive Palladium III硬件仿真、加速器。对于并非专业做大型项目仿真的企业、机构来说,此类产品的问世没有什么意义。
功能仿真代码与功能描述代码相比起来,更加强调备份,而不是版本控制。
为什么版本控制不重要呢,因为对于功能描述代码来说,任何一个子模块的微小改动,都会影响到整个FPGA设计,从而形成一个新的版本。而对于功能仿真代码来说,首先,任何一个子模块的改动,都不会影响其他子模块的功能仿真代码,甚至一般连针对其自身的功能仿真代码都无须修改。其次,功能仿真代码是相互独立的,它不像功能描述代码,通过层级化、模块化关系最终形成一个整体。例如,如果一个FPGA设计的功能描述代码主要包括两个模块——A和B,那么针对模块A我们需要编写一套功能仿真代码,姑且称之为simA,针对模块B我们需要编写一套功能仿真代码,姑且称之为simB。如果说FPGA设计的顶层模块为C,那么C的实现肯定是要调用A、B两个模块对应的实例,但是如果我们针对C模块编写simC功能仿真代码,那么simC想实现的功能可不是通过简单调用simA、simB两套功能代码就可以的,必须重新编写,因此simA、simB、simC是三套独立的代码。随着FPGA项目的复杂度越来越高,功能描述代码始终只有一套,而功能仿真代码可能会有几十、几百套,但是由于功能仿真代码的针对性,导致其被修改的频率也不会很高,所以对于功能仿真代码不需要花费太多精力进行版本控制方面的维护。
但是功能代码的备份很重要,因为一旦编写了FPGA设计的功能描述代码,那么在任何FPGA基本开发流程的环节中,只要碰到问题,可能都需要对这个核心载体进行修改。凡是被修改过得代码,都必须被怀疑为存在bug的,为了验证修改的正确性,我们需要对其功能进行重新的仿真,因此保留好所有的功能仿真环境可以方便我们快速完成对设计修改正确性的确认。除此以外,针对顶层模块的功能仿真代码,只需要稍许修改就可以应用到其他类型的仿真中。
回到本章节开头的问题,结合以上第三、第五两条的介绍,我们可以知道对于一个共有N个模块及子模块的FPGA功能代码,可能需要编写超过N套数量的功能仿真代码来对其进行验证,因此,通常来说,功能仿真环节才是整个FPGA基本开发流程中代码量输出最大的环节。虽然这些功能仿真代码根本就不会参与到最终的FPGA芯片上实现,但它们对于确保最终项目的成功起到了不可磨灭的作用。
最后,从一个FPGA开发者的角度来看,推荐大家使用操作方便且功能也足够强大的modelsim工具进行功能仿真。