实施HDL代码时应遵循哪些最佳实践?
与更常见的软件开发领域相比,有什么共同点和区别?
Answers:
有点旧的线程,但想放入我的$ 0.02。这并不是真正针对Verilog / VHDL的。更多关于一般硬件设计的信息……特别是针对定制ASIC的可综合设计。
这是我根据多年行业(相对于学术)在设计上的经验得出的意见。它们没有特定的顺序
我的总括声明是为执行验证而设计。在硬件设计中,验证至关重要。在实际的硅片中发现错误时,错误会贵很多。您不能只是重新编译。因此,前硅被给予了更多的关注。
了解控制路径和数据路径之间的区别。这使您可以创建更加优雅和可维护的代码。还允许您保存门控并最小化X传播。例如,数据路径永远不需要可重置的触发器,控制路径应该始终需要它。
在验证之前证明功能。通过正式方法或通过波形。我将解释2这样有很多优点。首先,它将节省您因洋葱剥皮而浪费的时间。与许多应用程序级设计(尤其是在学习中)和大多数课程工作不同,代码更改的周转时间非常长(从10分钟到几天不等,具体取决于复杂性)。每次更改代码时,都需要进行详细的说明,皮棉检查,编译,波形生成,最后是实际的仿真……这本身可能要花费数小时。其次,您很难遇到困难的情况。注意这是针对硅前验证的。这些肯定会在后硅片上遭受打击,使您损失很多资金。相信我,验证功能的前期成本极大地降低了风险,值得付出努力。有时很难说服最近的大学毕业生。
有“鸡丁”。小鸡位是通过驱动程序设置为禁用硅功能的MMIO中的位。旨在还原置信度不高(置信度与验证工作成正比)的更改。在预硅中几乎不可能达到所有可能的状态。除非经过后硅验证,否则无法真正满足您对设计的信心。即使只有1种状态在发生错误的时间中被击中0.000005%的时间,它也会在后硅阶段(但不一定在前硅阶段)命中。
不惜一切代价避免在控制路径中出现异常。您拥有的每个新异常都会使验证工作加倍。这个很难解释。可以说有一个DMA块,它将数据保存到另一个块将要使用的内存中。可以说保存下来的数据结构取决于完成的某些功能。如果您决定设计成不同功能之间保存的数据结构不同,那么只需将验证工作乘以DMA功能的数量即可。如果遵循此规则,则保存的数据结构将是可对内容位置进行硬编码的每个功能可用的所有数据的超集。一旦DMA保存逻辑针对1个功能进行了验证,就对所有功能进行了验证。
最小化接口(读取最小化控制路径)。这与最小化异常有关。首先,每个新接口都需要验证。这包括测试台中的新检查器/跟踪器,断言,覆盖点和总线功能模型。其次,它可以成倍地增加您的验证工作!假设您有1个用于读取缓存中数据的接口。现在说(由于某种奇怪的原因),您决定要另一个接口来读取主存储器。您只需四倍的验证工作。现在,您需要在任何给定的时间n验证这些组合:
了解并传达假设。缺少这是阻止通信问题的主要原因。您可能已经对一个完美的模块进行了完全验证。.但是,如果不理解所有假设,则您的模块在连接时会失败。
最小化潜在状态。设计的状态(意想不到的或意想不到的)越少,验证所需的工作就越少。将类似的功能分组为1个顶级函数(如定序器和仲裁器)是一个好习惯。很难识别和定义此高级功能,使其包含尽可能多的较小功能,但是这样做会极大地消除状态,进而消除潜在的错误。
始终发出强烈的信号离开您的障碍物。在大多数情况下,失败都是解决方案。您不知道端点块将如何处理它。您可能会遇到时间安排问题,这些问题可能直接影响您的完美实施。
除非性能受到负面影响,否则应避免使用粉状FSM。单纯的FSM更有可能在摩尔之上产生时序问题
..最后是我最不喜欢的一个:“如果没有破裂,就不要修复”。由于涉及的风险和错误的高昂代价,很多时候,黑客入侵是解决问题的更实用的解决方案。其他人则通过提及对现有组件的利用来避免这种情况。
至于与更传统的软件设计进行比较:
离散事件驱动的编程是完全不同的范例。人们看到了Verilog语法,并认为“哦,就像C一样……”……但是,这离事实还远。尽管语法相似,但必须有所不同。例如,传统的调试器在可综合的RTL上几乎毫无意义(Testbench设计不同)。纸上的波形是可用的最佳工具。但是,可以这么说,FSM设计有时可以模仿过程编程。具有软件背景的人倾向于对FSM疯狂(我知道我一开始就是这么做的)。
系统Verilog具有很多(很多)测试台特定的功能。它完全是面向对象的。就测试平台设计而言,它与传统软件设计非常相似。但是,它确实具有与时间相关的1个维度。必须考虑比赛条件和协议延迟
至于验证,它也不同(相同)。主要有3种方法:
...为了完整性,我还需要讨论最佳的测试平台设计实践...但这是另一天
抱歉,长度不够。我在“区域”中:)
HDL像Verilog和VHDL似乎确实鼓励使用意大利面条式代码。大多数模块由多个“总是”(Verilog)或“过程”(VHDL)块组成,这些块可以任意顺序排列。模块的整体算法或功能通常被完全遮盖。弄清楚代码的工作方式(如果您未编写代码)是一个痛苦的过程。
几年前,我遇到了这篇论文,概述了用于VHDL设计的更结构化的方法。基本思想是每个模块只有2个处理块。一种用于组合代码,另一种用于同步(寄存器)。这对于生成可读且可维护的代码非常有用。
在HDL中,代码的某些部分可以同时工作,例如两行代码“可以同时工作”,这是一个明智地使用的优点。这是习惯于逐行使用语言的程序员起初可能很难理解的东西:
应特别注意引导过程-芯片正常工作后,您将获得巨大成功。
在硬件上进行调试通常比调试软件要困难得多,因此:
首选简单代码,在代码运行后,有时还有其他方法可以加快代码速度,例如使用更高速度的芯片等。
避免组件之间的“智能”协议。
HDL中的工作代码比其他软件更宝贵,因为硬件是如此难以调试,如此重用,并且还考虑使用模块的“库”,其中一些是免费的,而另一些则出售。
设计不仅应考虑HDL代码中的错误,还应考虑正在编程的芯片上以及与该芯片接口的其他硬件设备上的故障,因此,人们应该真正考虑一种易于检查的设计。
一些调试技巧:
如果一个设计包含多个构建块,则可能要创建从这些块之间的接口到芯片外部测试点的线。
您将需要在设计中保存足够的行,以转移有趣的数据以供外部设备检查。您也可以使用这行代码,并将代码作为告诉您当前执行状态的一种方式-例如,如果您在某点接收数据,则向这些行写入一些值,在执行的稍后阶段,您写入另一个值,等等”
如果您的芯片是可重配置的,这将变得更加方便,因为您可以定制特定的测试,并在进行时对每个测试的输出进行重新编程(这对于leds来说非常好)。)
编辑:
通过智能协议,我的意思是,如果您的两个物理单元连接,则它们应该与可用的最简单的通信协议进行通信。也就是说,在它们之间不要使用任何复杂的自制协议。
原因是这样的-由于拥有模拟器,在FPGA / ASIC内部“查找”错误非常容易。因此,如果您确定数据随心所欲,并且在程序发送时就消失了,那么您已经达到了硬件乌托邦-能够在软件级别工作:)(使用模拟器)。但是,如果您的数据没有得到您想要的方式,那么您就必须弄清楚原因……您必须连接线路,这并不是那么容易。
在线路上查找错误非常困难,因为您必须使用专用设备连接线路,并记录线路在不同时间的状态,并且必须确保线路按照协议进行操作。
如果您需要连接两个物理单元,则使“协议”尽可能简单,直到不被称为协议为止:)例如,如果这些单元共享一个时钟,则在它们之间添加x条数据线,例如,使一个单元写入它们,而另一个单元读取,因此,例如,在每个时钟下降时,传递一个在它们之间具有x位的“字”。如果您有FPGA,则原始时钟速率对于并行数据而言是否太快-您可以根据实验控制其速度,例如,使数据停留在至少“ t”个时钟周期等行上。我认为并行数据传输比较简单,因为您可以以较低的时钟速率工作并获得相同的性能,而无需在一个单元上拆分字眼,而在另一个单元上重新组装。(希望每个单元在“时钟”之间没有延迟)。甚至这可能太复杂了:)
关于SPI,I2C等,我还没有实现,我可以说我已经连接了两个运行在同一时钟下的FPGA的支路(不记得中间电阻的确切形成)。更高的速率,所以我真的想不出使用这些速率作为在您自己的FPGA之间传递数据的主要方式的好理由,除非FPGA之间的距离很远,这是使用串行而不是串行的原因比并行总线
一些FPGA公司使用JTAG对其产品进行测试/编程,但不确定是否将其用作高速传输数据的方式,并且它是一种协议...(仍然是一个内置于芯片上的协议)支持)。
如果您必须实现任何已知协议,请考虑为此使用预制的HDL代码-可以找到或购买。