24 / 03 / 19
搬运翻译自The Golden Rules for proper FPGA design
作为一名FPGA设计师,需要具备多种技能,这些技能我无法在一页纸上全部概括。但有一些通用指南在这个职业中尤其相关。我尝试将它们总结为FPGA设计的五大黄金规则。
知道你在做什么
这是第一条规则,也是最难做到的。这意味着理解每一行代码、约束或配置命令的含义,以及它所带来的影响。这是理解逻辑设计的基础理论,以及设计工具如何响应我们向它们输入的内容。
这与试错法相反,所有高科技开发工具的供应商都暗中鼓励试错法,他们提供调试器、仿真器和许多被市场宣传为“易于使用”的工具。
通过草草记下一些东西,使用调试器进行测试运行,看看效果如何,修正一些问题再重复此过程来开发软件并不少见。间或,互联网上的示例代码片段也可能被复制粘贴到软件中。这种迭代工作习惯对于简单的软件开发来说可能相当高效,例如开发网站或智能手机应用:小错误并不那么严重,可以随着出现时修复,这是一种常见的态度。
但软件越复杂,这种方法就越快导致僵局。而对于FPGA设计来说,迭代开发实践很快就会反噬,且反噬得很厉害。FPGA设计工具远远不能帮助预防或警告严重错误。你完全可以搞砸一切,工具不会阻止你。
确保它能工作 或者说:“它能工作”还不够好。
FPGA是一种电子设备,而不是计算机。如果遵循某些设计原则(见规则#1),它同样可以重复且可靠。如果不这样做,它很可能因为某种或甚至没有明显原因而玩弄你。
当没有可见的bug时,并不意味着没有问题——这在任何领域都成立。例如,如果你的水管工完成工作后管道没有漏水,并不意味着工作做得正确。因为随着水压上升或有人意外触碰,管道可能后来开始漏水。
然而,我们都陷入了一个陷阱:进行快速测试或更全面测试,当一切看起来都没问题时,就认为工作完成了。对于漏水的管道、开发一个简单的网站,甚至是不属于关键应用程序的软件来说,这是相当可以接受的。但对于FPGA来说,这远远不够。
为FPGA开发设计意味着要保证设计能够工作。它是强迫设计工具产生一个无故障的比特流,使用它们提供的技术来实现这一目标。
这就像对自己做数学证明一样,确信逻辑设计是正确的,而不是尝试以“如果发生这个,就做那个”的方式思考。逻辑应该在最奇怪的边缘情况下也能工作,不是因为这些可能性被单独考虑进去,而是因为正确的数学表达式无论如何都保持真实。
最终在事实上它能正常工作了,但这并不是庆祝的理由。这是正确工作的自然结果。
明智地仿真(或根本不仿真)
FPGA新手最常抱怨的一点是他们的仿真工作得很好,因此他们的设计没问题,现在一定是其他地方出了问题。当然,这是无稽之谈。
首先,让我们明确一点,除非Verilog编码风格遵循一些严格的规则,否则仿真和硬件可能会完全不同的事情发生(VHDL也是如此)。参见规则#1。
但是,即使是正确完成的仿真也只覆盖了有限的时间和场景。即便是中等大小的设计,要仿真FPGA内部在比如说100毫秒内发生的情况,也需要很长时间。不仅如此,在硬件上实际运行设计往往会暴露出设计师未曾想象、因此也未曾模拟的场景。所以,一个仿真完美的设计可能在硬件上完全失败,仅仅因为FPGA运行的时间远超过仿真覆盖的范围,或者发生了意外的事情。
更糟糕的是,在硬件上发现bug很难,因为所有事情都是并行发生的。与软件调试不同,很少有一系列事件可以跟踪,更不用说单步执行的选项了。当然有一些工具可以在FPGA内部追踪信号,但要弄清楚追踪哪些信号,以及使用什么条件作为收集追踪数据的触发器并不总是容易的。
所以,尽可能少地使用仿真作为目标。如果你在使用仿真来修复你的设计,试图“让它工作”,那么当你尝试在硬件上运行时,很可能会遇到麻烦。
相反,尝试编写从第一次运行就完美工作的FPGA设计。这需要在编写第一行代码之前进行一些思考,以及了解你在做什么(见上面的规则#1)。仿真应该只是确认你做对了,或可能检测到一些愚蠢的打字错误。达到这个目标是一个学习和改进的过程,但它是值得的。最终,仿真成为浪费时间,因为它永远不会发现任何需要修复的东西。
这不仅仅是节省时间,还使得在硬件上需要修复的bug变得更少,更容易被发现。
我非常清楚,FPGA设计的常见第一课是“首先我们仿真,然后我们在硬件上运行”,但这只适用于第一课。
不要逞强 或者说:坚持使用成熟的编码风格。
当用于综合时,Verilog不是一种编程语言。主要区别在于,如果你用任何编程语言写下语法上正确的东西,编译器(或解释器)保证执行的正是语法所表达的含义。或者,在最坏的情况下,报告一个错误。
另一方面,综合器基于有限的逻辑元素集合生成逻辑。因此,很容易用Verilog编写出结果不可靠的逻辑代码,甚至是无法在FPGA上实现的代码。更糟糕的是,如果没有办法将Verilog代码作为逻辑在FPGA上实现,综合器往往会产生具有不同行为的逻辑。很多时候,综合器在这样做时不会发出警告。换句话说,FPGA上的逻辑与预期(即仿真)行为不同,而且没有任何警告。
更糟的是,综合器中的bug比编译器中的bug更常见。创造性的编码风格绝对可以暴露出这样的bug。
上述所有内容对于VHDL同样适用。
因此,避免此类问题的唯一方法是采用广泛使用的编码风格。要牢记的问题是:“如果综合器对这段代码片段感到困惑,除了我之外还有多少人会感到愤怒?”。如果答案是“很多人”,那你就安全了。
坚持成熟的编码风格还有另一个优点:可移植性。不管你喜不喜欢,今天你可能在使用一个综合器,明天你可能会发现自己使用一个完全不同的FPGA和不同的开发工具。
要了解成熟编码风格是什么样的,可以看看工具内置IP生成的Verilog代码。不同的代码作者之间有一些差异,但某些编码模式会比其他模式更常见。这些就是要模仿的。
几乎不用说,请按RTL(寄存器传输级)范式来工作。这不仅是一种成熟的编码风格,而且是FPGA工具所期望的。
关于Verilog的教科书和教程可能会误导人,因为它们通常试图全面覆盖。因此,它们经常覆盖很多在语法上正确的可能性,但很少使用,有时甚至不适合综合。
时序是一切
逻辑设计不是软件编程。仅仅在Verilog wire和寄存器中获得正确的值是不够的,它们出现在正确的地点和正确的时间同样重要。同时,还需要正确处理时钟。
这些是主要的话题,或多或少:
思考数据如何以及何时通过pipeline的不同阶段流动,特别是当管线可以被完全或部分阻塞时。实际上,任何数据流都是如此。
确保时序约束是正确的。特别是,阅读外部组件的数据手册并进行计算。此页面讨论了时序检查。
注意时钟:它们从第一次使用时就稳定了吗?它们的抖动是否足够低以满足它们的用途?
时钟域交叉:是否需要重新同步逻辑?如果是,它是否保证了信号从一个域到另一个域的无故障传输?更多相关内容在这里。 简而言之,与计算机程序不同,逻辑设计不仅仅关于将发生什么,还关于何时发生。
没有人说FPGA设计容易,这五条规则都不容易遵循。它们每一条都需要知识和一定程度的自律。然而,坚持这些规则绝对值得。这样做可以节省大量的挫折,特别是在项目的最后阶段,当一切都按期望正常工作时。