FPGA之道(16)FPGA开发流程之项目方案与FPGA设计方案

前言

FPGA的开发遵循一定的流程,我们要学会站在巨人的肩膀上进行FPGA开发,听取前人的经验,这篇博文摘自《FPGA之道》,一起来学习下作者对于FPGA开发的丰富经验。

FPGA开发流程

FPGA项目的开发应该遵循一定的开发流程。
针对一般的FPGA项目来说,简要的开发流程框图如下:
FPGA之道(16)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上实现一些已经很成熟的理论算法,那么“不求甚解”是可以的;如果要以FPGA为平台来简化、优化或者创造一些理论算法,那么就必须对相关背景知识具有一个比较深层次的理解。
    通常来说,FPGA设计者们往往将精力更多的投放于FPGA项目具体的芯片实现过程,而忽略了对背景知识的充分研究,这是非常非常错误的。因为背景知识是FPGA项目的土壤,它直接决定了FPGA项目这棵小树今后的生长环境。单独追求FPGA项目的具体芯片实现而不讲求对背景知识进行充分研究的FPGA开发者,就好比一个生活在原始部落的捕猎者一样,尽管你使用弓箭的本领有多么的出神入化,也不可能比得上带瞄准镜的狙击猎枪。

项目方案的设计与制定

项目方案是FPGA基本开发流程中第一个以文档为输出的环节,项目方案做的好与坏,直接决定了整个FPGA项目开发后续流程的顺利与否。对于一些比较简单的项目,例如用FPGA实现一个异步串口等,可能项目方案的作用不是很明显,甚至还会让人觉得有些多余和麻烦,这也是很多FPGA开发者在平时进行FPGA项目开发的时候都没有写过项目方案的一个主要原因,但是一旦项目上了规模,或者具有了一定的理论深度,那么项目方案的存在就显得十分必要了,否则以后的返工可能会对开发者或开发团队的信心造成毁灭性的打击。
项目方案其实也是计划书的一种,虽说计划赶不上变化,但是没有计划又是万万不行的。在经历了对所接手项目背景知识的分析与研究之后,通过项目方案的设计与制定环节,可以让开发者们对项目有一个全局的、原理性的掌握,并且可以提早发现项目中潜在的一些漏洞,从而最大程度的避免因为后续开发环节中出现一些不可解决的问题而导致的返工。
项目方案的设计与制定是一个比较*的环节,对于最后输出的文档也没有非常严格的格式规定,但是作为整个项目的指导性文档,在设计与制定项目方案的过程中还是应该把握住一些基本原则的。

写清楚项目背景

项目方案第一步,就是要写明白为什么要做这个项目?想解决什么问题?做出来有什么好处?它有什么应用前景?等等一系列问题。因为通常来说,工程师都是服务于公司或者企业,而利润是所有公司、企业的终极目标,因此如果是一件对公司或企业没有任何好处的事情,那么别指望老总会为此投入人力、物力和财力。因此,为了能够让项目顺利开工,讲清楚这个项目的背景是很有必要的,当然自己就是老总的除外。
要写清楚项目背景,光解决上述的一系列问题还是不够的,因为这些问题都仅仅着眼于项目自身,兵家常说,“知己知彼,方能百战不殆”,所以还需要对行业中相关的公司和企业进行调研,分析一下行业目前的形式和竞争对手的威胁程度,看看别人在做什么,做到什么程度,它们有什么优势、有什么劣势,自已有什么优势、什么劣势等等。否则自己千辛万苦做出来一款产品,发现市场上别的公司的类似产品早都卖疯了,到时候就只有哭了。

写清楚项目需求

项目需求主要讲的是这个项目需要完成什么样的功能, 相对于项目背景来说,项目需求要写得稍微详细一些。

  • 首先,要讲清楚整个项目运行的环境状态。例如:这应该是一个车载的系统、机载的系统、舰载的系统还是手持电子设备等等。
  • 其次,要讲清楚项目的输入是什么。例如,声音处理相关项目的输入应该是声音,但具体是人声还是乐器声或是什么别的东西一定要说清楚;图像处理相关项目的输入应该是图像,但是是人脸识别还是红外成像什么的一定要说清楚。
  • 第三,要讲清楚项目的输出是什么。例如,可口可乐工厂的输出就是可口可乐,肯德基连锁店的输出就是汉堡、鸡腿。那么,我们项目最终的输出到底是数字、声音、图像或者是什么别的东西一定要说清楚。
    第四,要讲清楚项目的具体需求是什么。例如,该项目对处理的速度性能有什么要求,空间性能有什么要求,出现故障时有什么要求,成本控制有什么要求等等。
  • 最后,尽量详尽的列举一些细节需求,这也是项目需求中最重要的一环,越完备的列举细节需求,在后续方案制定的时候才能考虑的越全面,从而最大可能的在后续的具体项目开发中避免出现一些不匹配项。例如,当上位机请求数据时我们应该在多少时间内应答?应答数据包的格式是什么?每一位数据代表什么意义?等等。

写清楚方案框架

项目需求子环节解决了做什么的问题,而怎么做的问题将由方案框架和算法细节两个子环节来协作解决。 解决怎么做的问题,其实就是寻求项目解决方案的过程,这是整个项目方案的设计与制定环节的主要目的,也是整个项目开发过程中的指导与依据。如果说背景知识是FPGA项目这棵小树生长的土壤,那么解决方案就好比是一个园丁,为小树的生长搭好了支架,在保护它的同时还能够让其沿着正确的方向生长。
方案框架子环节主要负责从整体、宏观上解决怎么做的问题。它的好处就是能够让人一目了然,从而尽可能快的捕捉到解决方案的设计思路。为了达到这个效果,方案框架最好包括以下几个内容:

  • 一、方案框图。图形化的描述方式更容易让人看懂,并且更加形象,非常适合于从整体上对项目的解决方案进行描述。当然,为了更加清楚的对方案框架进行描述,最好对方案框图附上一些说明文字,简要说明一下框图中各个环节的功能以及功能环节之间的联系。
  • 二、软、硬件工作划分。电子类相关产品的开发一般都离不开软件和硬件两方面的开发工作,那么在方案框架子环节中,我们也应该分别阐述清楚整个项目软件、硬件部分的工作量与工作内容。

写清楚算法细节

算法细节是解决方案的又一重要组成部分,它是对方案框架的重要补充。 与方案框架正好相反,算法细节主要从细节和微观出发,解决怎么做的问题。这种解决不是概念上、原理上的解决,而是十分具体的解决。例如,如果我们下班到家后发现没有带钥匙,于是悲剧发生了,但是总不能坐以待毙吧,此时面对目前的囧境,方案框架就解决了到底是撬开门进入、翻窗户进入、打110求助还是返回公司取钥匙等等选项的选择。如果一旦确定了翻窗户作为方案框架,那么接下来,算法细节就解决了具体怎么翻窗户的问题,例如先敲开邻居家门,然后取来安全绳绑于腰间,另一端由邻居拿住,然后从邻居家的阳台踩着空调支架进入自家阳台等等。
从上面的分析可以看出,算法细节一定要清晰、详尽,最好能够细分为多个步骤,并且保证只要按照这些步骤一步步来,就能解决问题。

确保逻辑完备性

项目方案是否可行,首先在乎于其逻辑上的完备性。如果制定出来的方案,本身就不能自圆其说,弄得前后矛盾、疏漏百出,那么后续的工作也就没有进行下去的必要了。这也是为什么如果仅仅在脑子里面有个大概思路就开始项目的开发是很容易返工的。
要想确保项目方案的逻辑完备性,就要像做数学证明题一样,保证方案的每一步都有理有据,步与步之间的推理也要做到充分甚至必要。 这样,逻辑完备了,方案才有进一步往下付诸实施的意义。

确保实现无关性

项目方案一定要是实现无关的,这里的实现主要指的是使用某些具体的软、硬件或其开发工具来进行设计。 例如,可以在项目方案中出现类似这样的描述——“使用勾股定理计算直角三角形斜边的长度”,但是最好不要出现类似这样的描述——“在FPGA上分别利用两个DSP硬件乘法核资源实现乘方运算,然后将结果相加后,再调用开根号IP核即可得直角三角形斜边的长度”。那么为什么项目方案要是实现无关的呢?

  • 首先,项目方案仅仅是项目的指导,那么就要有个指导的样子。历来优秀的管理者都不是事必躬亲的,因为首先他没有那么多时间,其次这也是管理无能的表现。
  • 其次,真理是需要实践检验的。虽然方案已经通过了确保逻辑完备性的子环节,但是,这也仅仅是初步确保了方案没有明显的错误和漏洞,要想证明方案是完全正确的,是需要严密的数学推导的,而且,理论上成立的不一定技术上就能够实现。因此,在项目方案没有被进一步证明无误前,将实现具体化的任何工作都很可能会成为无用功。
  • 最后,条条大路通罗马。即使方案是正确的,但是实现可以是多样化的。同样一个功能,可以在FPGA上实现,也可以在单片机上实现,也可以在PC上实现。项目方案毕竟还是处于项目最开始的阶段,此时对整个项目的把握可能还不是很充分,而且以后可能还会不断修改,因此,也没有必要在这个时候将实现具体化。

确保书面易懂性

所谓书面易懂性,其实是为了区别原理易懂性而言的。前面说过,找出解决方案是项目方案设计与制定环节的主要目的。而解决方案是解决怎么做的问题,并不解决为什么这么做的问题。为什么这么做,其实是一个很难的问题,它蕴含在背景知识的分析与研究中,甚至即使项目成功了也都没有弄清楚。例如,你查到一组滤波器的系数具有很好的过渡带迅速衰减特性,如果仿真后确实是这样,那么你就可以在你的项目中使用了,至于为什么这组系数这么好,其实是没有太多的必要去花时间研究的。这也是做工程与做理论的根本区别之一。
那么,项目方案为什么一定要具有书面易懂性呢?这要从三个方面来说:

  • 一、一个项目往往对其他项目有借鉴作用,那么当以后有类似项目的时候,你总不希望复习自己做过的旧项目成为一件令人头痛的事情吧?
  • 二、对于一些规模较大的项目,往往都会由一个团队来分工协作,那么团队中的每一份子都应该对项目有个整体上的把握,而实现这一点只能通过阅读项目方案。如果你的项目方案写的晦涩难懂,或者容易产生歧义,那么大家能够合作愉快才怪呢!
  • 三、与第二点类似,制定项目方案的人可能是团队的管理者,也可能是对某一个科学领域比较擅长的专家,因此他们往往不会也不可能亲自去进行具体的设计实现。因此,这个时候,项目方案就成为了方案制定者与方案实现者这两者之间的一个桥梁。那么,桥修的好不好,直接决定了最终设计实现的好、坏程度。

算法可行性仿真与验证

随着FPGA项目所基于的理论知识越来越丰富、越来越深入,算法可行性仿真与验证环节的重要性就越来越明显。下面的部分,将分别从“为什么”、“何时做”和“怎么做”三个方面对该环节进行较为详细的阐述。

Why?

虽然进行了充分的背景知识调研,并且也给出了具有逻辑完备性的项目方案,但是理论与实践之间还是存在着不小的鸿沟,尤其是一旦涉及到了一些具体的算法,必须要进行认真的仿真与验证,否则就到了“尽信书不如无书”的境地。那么理论与实践之间到底需要注意哪些问题呢?

  • 一、精确度!
    现实中的信号往往都是以模拟的为主,而我们的FPGA或者PC机都只能处理数字信号,这也是为什么人类发明了AD和DA芯片。从模拟到数字的转换,一般来说需要经过采样和量化,这就涉及到了时间精度和幅度精度等等的问题;从数字到模拟,一般来说需要经过转换与内插,这也涉及到了时间精度和幅度精度等等的问题。
    举个例子来说明一下。假设我们要在FPGA上实现一个简单的函数功能,例如
    f(x) = x;
    如果需要求得 x = π 时的输出,那么就会碰到精确度问题。首先,π = 3.1415926……,是一个无线不循环小数,那么我们该怎么将π输入到FPGA芯片中去呢?其次,通过函数表达式,我们知道f(π) = π,那么此时输出也是一个无线不循环小数,那么我们该怎么将计算结果π输出给外界呢?由此可见,数字系统只能对模拟环境进行一个近似计算,至于需要近似到什么程度,完全取决于系统的精确度要求。

  • 二、准确度!
    其实学术界也远没有大家想象的那么崇高、圣洁,这其中也不乏很多鱼目混珠之徒,当你从网上或者一些文献、期刊中淘到一些理论算法后,千万不要太相信它所承诺的性能指标和应用效果。如果自己不首先进行一些仿真和验证的话,等到最后才发现上当受骗那可是非常悲惨的一件事情。

  • 三、细节度!
    “纸上得来终觉浅,绝知此事要躬行”!有些事情看起来是一回事,做起来又是另一回事。我们从事FPGA项目的开发,最终所有的算法都是需要在FPGA上实现的,因此对这些算法的了解不能流于表面,必须要通过前期的仿真与验证,才能把握住算法的一些具体细节。例如,解说球赛的人往往不怎么会踢球,就是这个道理。

  • 四、待定度!
    待定度是指需要结合具体问题才能确定的一些量度。例如,“增加FIR滤波器的阶数可以让滤波效果更好”,但是具体增加到多少阶才能达到项目的要求呢,这个只有结合我们项目的实际情况才能判断,所以需要通过前期的仿真和验证来确定。对于一些算法,软件的仿真不一定很全面,但是可以比较快给出基本结论,而如果直接在FPGA上进行尝试,虽然FPGA的并行性使得其处理速度很快,但是编译FPGA的工作还是得由PC机来完成,因此对于复杂一点的项目,即使比较微小的改动,编译一次恐怕就需要消耗好几个小时,所以时间方面真的是可以将人拖垮的。

  • 五、选择度!
    选择度是一种权衡的考虑。例如,要对一幅图像进行锐化处理,有的算子效果好但是计算复杂度高,有的算子效果略差但是却计算简单。那么,此时就需要对两种算子的效果分别进行仿真,根据系统要求来选择合适的算子来实现。

  • 六、仿真度!
    以上几点主要是从算法本身出发来考虑的,除此以外,算法可行性仿真与验证环节对今后的FPGA设计的功能仿真环节也是非常有帮助的。例如,需要用FPGA实现一个复杂的编码算法,那么我们怎么知道功能仿真的时候输出的码流到底对不对呢?很简单,如果前期我们做了足够充分的仿真与验证,那么,对于任意的原始码流,只要用我们编写的仿真、验证程序一运行,就可以知道正确的输出码流是什么了!

When?

那么,什么时候需要对FPGA项目进行算法可行性仿真与验证环节呢?这自然是在项目方案确认之后以及FPGA正式设计开始之前了。因为项目方案确定了,采用的算法才能基本确定,这时候才知道该去仿真什么算法。而算法可行性得到验证以后,FPGA设计才能正式开始;否则后续的一切工作都是没有意义的,此时必须重新返回到项目方案的设计与制定环节,甚至背景知识的分析与研究环节,重新开始工作。
当然了,并不是所有FPGA项目中都必须要进行算法可行性仿真与验证环节的,对于一些简单的项目,例如实现一个异步串口,这其实就没有什么算法上的难度。但是,随着FPGA芯片应用的领域越来越广,实现的功能越来越复杂,目前大部分FPGA项目中还是缺少不了这样一个环节的。如果你的FPGA项目碰到【Why?】小节提到的任意一点问题,那么建议你最好对算法进行一下基本的仿真和验证。

How?

知道了为什么要对算法进行仿真与验证,也了解了对算法进行仿真与验证的恰当时机,最后,我们来看一下该如何对算法进行仿真与验证。

  • 首先,软件工具比较推荐MATLAB、VC和LabVIEW,当然,远远不局限于此。其中,MATLAB和VC是纯文本语言编程环境,对于有软件程序设计基础的人来说比较容易上手,比较适合复杂的纯算法类仿真,人机交互方面略微吃力;而LabVIEW恰恰相反,图形化编程为主,易使用,即使是初学者也能很快上手,具有丰富的人机交互手段且调用非常简单,但编程容易受到空间的限制,不利于实现过于复杂的算法。
  • 其次,算法可行性仿真与验证建议遵循从理论证明到算法仿真再到环境模拟的过程。理论证明,强调的是证伪,如果有能力的话,就在最开始的时候完成,因为理论上证明有问题的算法,实现以后八成也是不靠谱的;算法仿真,强调的是将自然语言和公式描述的算法用程序来实现,从而可以比较清晰的看到算法的近似实际执行效果,从而做出判断;环境模拟,强调的是容错,通过模拟环境中混杂的噪声和干扰,来检验算法的健壮性。
  • 最后,由于算法可行性仿真与验证环节需要比较丰富的软件程序设计经验,那么如果是团队开发的话,可以找软件设计能力比较强的专人来负责。不过对于一个优秀的FPGA开发者来说,编程往往不是太大的问题,毕竟很多FPGA开发者都是参考、对比着C语言学会的HDL。

FPGA设计方案的制定

终于可以正式开始FPGA上的设计了,书本前的你是不是早已摩拳擦掌、迫不及待了呢?不过请先别着急,作家在进行文学创作的时候都要先列提纲,那么我们在进行FPGA项目的代码编写之前也要先将FPGA的设计方案写出来。当然,对于一些较简单的项目,这一环节的作用可能并不十分突出,但是随着FPGA项目的越来越复杂,这一环节的重要性也就越来越明显。
FPGA设计方案的制定环节是FPGA基本开发流程中第二个以文档作为输出的环节。这个文档,更多的时候是写给自己看的,因为FPGA方案的制定过程,其实就是一个FPGA设计构思的过程,所以如果你写不出这个文档,那么你就不可能做得好这个设计。如果是团队开发的话,那么这个文档更是其它FPGA开发者在进行具体代码编写时的指导,更加事关整个项目的成败。

编写FPGA设计方案的好处

在写HDL代码之前先进行FPGA设计方案的编写有以下几点好处:

  • 一、可以让我们对FPGA设计的难易程度有一个整体上的把握。FPGA方案其实就是一个从项目方案到HDL代码的桥梁,因此项目方案在FPGA上具体实现的难易程度可以在FPGA方案设计环节就先有一个大概的了解。
  • 二、可以让我们对FPGA设计的侧重点有一个整体上的把握。FPGA方案涉及到整个项目方案的FPGA实现思路,因此,可以比较直观的比较出项目方案中哪一部分的FPGA实现难度比较大,哪一部分的FPGA实现难度相对较小,从而在后续的代码开发中合理安排时间和精力。如果是团队开发,也比较利于合理的任务分配。
  • 三、可以让我们对FPGA设计的资源使用量有一个粗略的把握。FPGA方案其实就类似于FPGA设计的简略型自然语言描述,也许我们无法准确的估计出查找表和寄存器的使用量,但是大概的情况还是可以心中有数的,尤其是类似DSP核、BLOCK RAM这样的稀有资源,考虑周全的话,几乎是可以做到精确估计的。把握了FPGA设计可能使用的资源量,对于FPGA芯片的选型有着非常重要的指导意义;如果芯片型号已固定,那么也可以通过与FPGA芯片内部资源数量进行对比,来判断该FPGA芯片是否能够承载当前设计,如果不行的话,那么就需要重新调整和分配各个模块使用的资源。
  • 四、可以让我们提早发现项目方案中的一些问题,避免更多的返工。前面讲过,项目方案是实现无关的,它其实不管你最终是打算用什么东西来作为算法的载体,而编写FPGA设计代码是与实现息息相关的,它必须考虑到FPGA内部的结构特征,涉及到了算法的具体实现载体。既然FPGA设计方案充当了一个桥梁的作用,那么,从项目方案到HDL代码实现之间是否能够修通这条桥,其实也是一个未知数。到底桥能不能修通,需要试过后才知道,因此,在桥修通之前,对岸盖再多的高楼、别墅和商业配套都可能成为一座死城。例如,项目方案中想通过1Gb/s的串行传输速率来进行通信,可是实际采用的FPGA芯片只支持到200Mb/s,那么必须返回到项目方案制定环节进行修改。
  • 五、可以让我们对FPGA设计建立起一个模块化和层次化的结构,从而简化后续HDL代码编写时的复杂度。由于在FPGA方案设计阶段已经建立起了模块化和层次化的结构,那么在具体编写每段代码的时候,只要确保好接口正确,就可以集中精力着眼于局部设计。

如何编写FPGA设计方案

编写FPGA设计方案建议参考以下步骤:

  • 第一步,概括的将整个FPGA设计按照功能分为几个大的部分。最好辅以整体的系统框图来说明各个部分完成的功能以及彼此之间的联系,并且明确每个部分的主要输入输出,即FPGA与外界以及各部分之间的主要接口关系。
  • 第二步,针对每一个大的功能部分,如果觉得有必要,可以重复第一步的方法,直到每个子部分的功能都较为纯粹、单一(注:不是功能简单,因为功能简单的话,那么FPGA设计的层级就会太多,因此也会有过犹不及的不良影响)。
  • 第三步,针对于每一个纯粹的子部分,编写出算法思路说明。思路说明可以但不限于是文字叙述、原理框图、公式列举、逻辑推论、证明等。
  • 第四步,针对于每一个子部分的算法思路说明,给出详细的FPGA具体实现说明。实现说明可以但不限于是文字叙述、结构框图、流程图等。
    讲到这里,可能有人会分不清楚算法思路说明与FPGA具体实现说明之间的区别与联系。其实,如果当前项目中某一个功能单一的子部分就是另一个FPGA项目的全部内容,那么该子部分的算法思路说明就可以作为另一个项目的项目方案。当然了,项目方案是完全实现无关的,但是算法思路说明的最终目的多少还是要便于FPGA实现的,还是需要考虑一下FPGA的基本特性,因此考虑的要更加多一些。而FPGA具体实现说明,则完全偏重于算法思路的FPGA实现,类似使用FPGA内部什么资源,采用什么样的数字电路等。例如,子模块功能为对输入信号进行63倍放大;算法思路为直接对输入数据做乘法;具体实现就可能为使用DSP硬件乘法器并固定一个系数为63,或将乘以63改为乘以64后减去自身,然后用FPGA中很容易实现的移位操作来代替2的整数次幂乘法等等。

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设计的功能仿真,那么说明应该还存在着用不同定语修饰的其他仿真类型。
没错,笼统的说,FPGA设计中的仿真共分为两大类,即前仿真与后仿真。前仿真一般也叫功能仿真,目的是从逻辑上分析HDL代码所描述电路的正确性,它的仿真速度远比后仿真要快,我们可以根据需要来观察电路输入、输出端口或者HDL代码内部任一信号和寄存器的波形。后仿真一般也叫时序仿真,它是将电路中门延迟的参数和各种电路单元之间的连线情况考虑在内后进行的仿真,因此得到的仿真结果更接近真实的应用情况。不过由于后仿真时考虑了大量的时序信息,所以后仿真的速度相对于前仿真要慢得多。并且为了考虑时序信息,必须对HDL代码进行综合、布局布线等操作后才能得到电路的情况,所以HDL代码中的内部信号与电路中的对应关系不是那么明显,甚至会找不到对应关系,因此在观测内部节点波形时也会比较困难。
如果再细致一点,FPGA的仿真可以进一步划分为四类,即代码级仿真、门级仿真、映射后仿真以及布局布线后仿真。其中,代码级仿真,仿真的对象就是原始的、未经加工的HDL代码,这也就是我们通常所说的功能仿真;门级仿真,仿真对象是HDL代码经过综合后的门级网表;而映射后仿真,仿真对象是门级网表经过转换后对应到FPGA上的具体逻辑资源聚类,此时可以得到电路中的门延迟信息文件供仿真时使用;最后,布局布线后仿真,仿真对象是具有位置信息和连线信息的FPGA上逻辑资源聚类,因此可以兼并考虑门延迟与线延迟信息,这也就是我们通常所说的时序仿真。

二、功能仿真的作用。

功能仿真环节的作用,顾名思义,就是要确保HDL代码功能上的正确性。它主要体现在两方面:

  • 首先,在功能仿真环节的最开始,会先检查HDL代码语法的正确性,从而可以为我们指出编程上的一些语法错误以及编写代码时的一些笔误。
  • 其次,功能仿真环节可以仿真出HDL代码功能上的行为,我们可以通过仿真结果来判断HDL代码实现的功能是否与我们的预期一致,从而可以为我们指出一些对FPGA方案理解的偏差以及找出一些编程时的笔误。
    在以上讨论过的这些仿真类型中,功能仿真是FPGA基本开发流程中的必备环节,而其它的都是可选的。具不完全统计,整个功能仿真环节消耗的时间大概占整个项目开发周期的60%以上,由此可见其重要性。

三、正确的功能仿真时机。

功能仿真环节不是一个独立的环节,它必须与功能代码的编写环节紧密交织在一起。首先,功能仿真发现的问题必然要返回代码编写环节中去修改,因此不断发现问题就不断要修改然后再通过功能仿真确认修改。其次,如果你打算编写完整个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工具进行功能仿真。