当事情变得奇怪时
FPGAs 一般而言是非常可靠的组件,但与任何一项技术一样,如果使用不当,它们将不会做得那么好。
不幸的是,可用的 FPGA 设计工具在指导我们人类避免犯错误方面毫无帮助,这往往会导致不可靠和不可预测的结果。更具体地说,只有遵循某些设计 practices (design practices), FPGA 工具才能保证可靠运行,并且有一个基本假设,即我们知道自己在做什么。与软件编译器(software compilers)不同,如果源代码(source code)出现错误则拒绝生成 executable 代码, FPGA 工具只是说: “这是你的比特流(bitstream),喜欢就试试吧,顺便说一下,这里是 200 warnings,如果你明白 warning #143 是什么意思,你也会明白,有一个严重的问题需要修复”。
使用 FPGAs 的人经常会觉得电子设备被某种魔力所控制,没有任何意义。一个问题可能会因为完全不相关的变化而出现和消失,而且看不到任何合理的解释。许多受过良好教育的工程师倾向于采用关于 FPGAs 的荒谬理论,试图为这种奇怪的行为找到一些解释。
这是“Black Magic 模式”: 当明智的人不再相信对他们的问题有合理的解释时,而是寻找基于他们经验的解决方案。当 FPGA 真的很冷时,一切正常吗?好吧,放一个巨大的 heatsink。问题只出现在某些电路板上而不是其他电路板上?好的,测试每块电路板,并扔掉那些没有的。等等。
它看起来像什么
这是可能影响工程师进入非理性思维模式的部分问题列表。它总是“一切正常,除非......”
- ...它突然没有,没有明显的原因。
- ...在 Verilog 代码(或 VHDL)或 FPGA 工具的设置或版本中进行了随机的、不相关的更改。
- ... 空调开/关。
- ...系统已经工作了一段时间/从冷启动。
- ... 使用来自另一个制造批次的 FPGA 组件。
- ... 使用了来自另一个制造批次的其他组件。
- ...我把手指放在这里/我的手放在那个上面/按下那个不相关的按钮。
这通常伴随着一种信念,即电子设备本身存在缺陷并且不符合其数据手册(datasheet)。关于一家大公司如何运送有缺陷的组件的阴谋论并不罕见。
所以这不仅仅是一个错误。确实,虫子可以让人发疯,但它们往往具有某种程度的可重复性,而且它们肯定不会因为空气状况而出现和消失。我从未听过软件工程师将错误归咎于 PC 计算机本身(尽管在极少数情况下实际上是这种情况)。相比之下, FPGA 设计中的错误肯定会导致硬件级别的故障。从那里指责整个世界的距离很短。
有一个合乎逻辑的解释。真的。
而且触手可及。不一定容易。
在这个领域做了很长一段时间的自由职业者,偶尔会解决这样的情况,当我告诉你的时候相信我: 除了极少数情况外, FPGA 都很好,问题可能出在比特流上。
坏消息是, FPGA 设计通常存在不止一个缺陷,而这些缺陷可能是造成明显问题的原因。因此,可能有很多事情需要解决。有好几次,我被要求修复“几乎可以工作”的 FPGA 设计,很快我就意识到我面临着一个不切实际的期望,即“这个小问题”会很快得到修复。稳定设计通常意味着大量工作而没有任何明显的进展。
这样或那样,别无选择。在本页中,我将尝试通过列出电子设备中出现鬼魂的可能原因来恢复理性思维。
当然,最好的办法是从一开始就避免这种情况。遵循我在不同页面上列出的黄金法则是一个好的开始。
为什么事情变得奇怪
嗯,简短的回答是 FPGA 设计出了点问题。在这种情况下,原则上有两种可能性。相对幸运的可能性是当 FPGA 应该做的事情出现明显且稳定的故障时。这就像一个软件错误: 找到它,修复它,看看修复后它是否有效,完成。
不太幸运的可能性是 FPGA 可以工作,但这主要是一个幸运的巧合。因此,当某些情况发生变化时, FPGA 会突然停止正常工作,然后可能会恢复正常工作。但是为什么会这样呢?
所以重点是: FPGA 是一种电子产品,因此在制造过程中存在误差。更重要的是,当 silicon 的温度发生变化时, transistors 改变状态的速度也会发生变化,信号在逻辑阵列(logic fabric)上传播的速度也是如此。 supply 电压的变化也会影响 FPGA内部发生的事情的速度。
因此,如果 FPGA 变热或变冷,相对于时钟,信号可能会稍晚或稍早到达触发器(flip-flop)。仅此一项就可以让这个触发器(flip-flop)错过它应该采样的信号,或者采样触发器通常会错过的信号。当芯片上相邻(可能不相关)的逻辑变得更少或更多活动时,即使 silicon 的局部加热也会导致这种情况。
同样, FPGAs 也存在制造误差。尽管出厂的所有 FPGAs 都通过了确保它们与其规格兼容的测试,但某些 FPGAs 可能比其他 silicon 更快。这就是为什么一个制造不当的逻辑设计可能在一 FPGA 上工作而在另一 FPGA 上不能工作的原因。
所有这些随机参数都会影响逻辑阵列上的小组件何时改变其状态,而时间上的差异可能会导致完美运行的东西与灾难之间的差异。因此,制造、温度和电压的微小偏差会产生明显的差异。
那么 FPGA 如何可靠呢?正确制作 FPGA 设计后, FPGA 设计工具可确保一切始终按定义工作。或者更准确地说,一切都适用于任何已通过制造测试并在数据手册要求范围内使用的 FPGA 。这归结为使环境温度在所需范围内(必要时进行冷却),以及 FPGA引脚上的正确电压。
但是,如果不遵循所需的 FPGA 设计实践,这些工具也不能确保正常运行。这意味着不应该产生任何影响的参数变得至关重要,整块电路板停止工作并恢复工作,这取决于根本不重要的事情。奇怪的事情可以变得没有限制。
因此,冒着重复自己的风险,这里有几个例子:
- 一切都可以完美运行,然后你做一个微不足道的改变,再次运行实现(implementation),加载新的比特流,然后砰,完全失败了。这通常是因为逻辑在 FPGA的逻辑阵列上的放置不同,逻辑单元(logic elements)之间的布线(routing)也是如此。结果,传播延迟(propagation delays)发生了变化,一些以前通过正确的时序(timing)到达其目的地的信号不再这样做了。
- 您拥有三个相同的电路板: 一块电路板完美运行,第二块电路板只在早上工作,第三块电路板从不工作。这可能是因为 FPGAs的 silicon略有不同。结果,在设计的某处, FPGA 内部有一个信号到达目的地,第一块电路板上有正确的时序。在第二块电路板上,余量更小,因此不同的环境温度使 FPGA 跨越了从工作到不工作的阈值。而第三台 FPGA 一直都在这个门槛的错误一侧。
- “我打开这个东西,另一个东西停止工作,但它们完全不相关”。这些通常与温度有关。即使一个功能的逻辑与另一个功能完全无关,一个活动的逻辑单元也可以加热它的邻居。
这值得一遍又一遍地重复: 当 FPGA 设计正确完成时,这一切都不会发生。或者至少非常罕见。很少有人意识到当数据手册被读取和跟踪时电子设备的可靠性,并且 FPGA 也被正确使用。
但我想这个讲道对于那些阅读这个页面的人来说有点太晚了——问题已经存在了。因此,根据我自己的经验,我列出了 FPGA看似闹鬼的一些常见原因。如果您面临这样的问题,很有可能就是其中之一。
原因1: 时序
很大程度上,这些工具是通过实现你给他们的时序约束(timing constraints)来保证 FPGA 的稳定运行的。这是你和工具之间的交易: 只要 FPGA在其允许的温度和电压范围内运行,您就可以准确地表达时序要求,并且这些工具可以确保在您使用的任何 FPGA 上都能实现这些要求。
时序约束只是一个包含参考时钟(reference clock)频率的约束(constraint)并不罕见。这可能已经足够好了,但如果您刚刚从其他设计复制了这一行并且嘿,它有效,那么就有充分的理由进行审查。
实际上,这是关于正确地做时序。这不是一项简单的任务,即使对于最有经验的 FPGA 设计师来说也是如此。这意味着确保设计中的每一条信号路径(signal path)都由约束控制,以确保最终的触发器始终正确接收信号。除了那些不需要 constraining的路径(paths)。
所以首先要检查: 设计达到时序约束了吗?这是非常基本的,但由于大多数 FPGA 工具无论如何都会生成比特流, FPGA newbies 可能会犯这个简单的错误。
接下来是回顾时序约束。有一个单独的页面讨论这种检查,但长话短说: 你明白时序约束是什么意思吗?它们的含义是否完全符合其应有的含义?如果有选择性时序约束——也就是说,它们覆盖了一些路径,特别是滤波器 conditions (filter conditions)——它们是否确实适用于正确的路径?
然后,应该仔细阅读时序报告(timing report)。再一次,这个单独的页面详细阐述了这个主题。
另一件值得关注的是 clock domain crossings。是否存在不安全地从一个时钟域(clock domain)传输到另一个时钟域的信号?这可能是由于缺乏对逻辑与每个时钟相关的关注。跨时钟域(clock domain crossings)是否仅使用由 FPGA 工具创建的 FIFOs 完成?如果没有, crossings 是否正确且安全地完成?
原因2: 不合适的复位(resets)
它可能看起来不相关,但如果不能确保逻辑的初始状态,它很可能会导致 black magic 行为。
规则很简单: 如果您还没有认真考虑过复位和逻辑的唤醒功能,那么您可能弄错了。
特别是,考虑这个例子:
always @(posedge clk or negedge resetn)
if (!resetn)
the_reg <= 0;
else
the_reg <= [ ... ] ;
如果您对复位的看法是编写这样的代码,而没有明确考虑停用 @resetn 时会发生什么(即在此示例中更改为高电平),那么您绝对应该查看此页面。
无论哪种方式,最好检查它们应该在的位置是否有复位,并且它们是否正常工作。这意味着什么将在有关该主题的一系列简短页面中进行讨论。
原因 3: 时钟
时钟的品质可能是 digital 设计中最被低估的话题。它通常就像“是的,它从高到低再变回来,让我们将它用作时钟”。
与 FPGA 逻辑配合使用的时钟应该是稳定的,并且具有足够的抖动(jitter)性能。同样重要的是,时钟与 FPGA 的物理连接必须稳定可靠。
所以在这样的声明中,
always @(posedge clk)
任何用作 @clk 的东西都必须非常小心。理想情况下,此时钟源自专用时钟 generator (clock generator)组件 (oscillator),可确保时钟稳定且抖动较低。一般来说,把这个外接的时钟当做参考时钟转锁相环(PLL)使用比较好,而不是直接连接逻辑。即使锁相环不改变时钟的频率也是如此。
这是因为使用锁相环可以监控锁相环的 lock detector。因此,当锁相环解锁时,依赖于该时钟的逻辑可以保持在复位状态。如果参考时钟出现稳定性问题(特别是在上电(powerup)之后不久),这样做会显着降低出现问题的机会。
锁相环可能会被误认为是麻烦制造者,并且会导致显然不必要的复位,因为它偶尔会丢失其 lock 。可以通过移除锁相环并将外部时钟直接连接到逻辑来错误地“修复”,然后一切似乎工作正常。在这种情况下,参考时钟很可能存在问题。在这种情况下移除锁相环并不能解决问题,而是将其推入逻辑阵列,可能导致 black magic 情况。
到目前为止,我已经从专用 oscillators讨论了时钟,这确实是一个简单的案例。其他来源会变得更糟: 由处理器或其外围设备之一创建的时钟应谨慎使用(如果有的话)。这样的时钟可以被处理器暂时停止,或者偶尔产生非法的波形。这可能是因为软件写入相关的 hardware 寄存器,可能是不相关任务的一部分。当用 oscilloscope检查时钟时,这些短暂的事件可能是不可见的,但这些短暂的事件仍然会导致奇怪的故障。
另一个常见的问题来源是source-synchronous 时钟处理不当。换句话说,当一个外部元件提供一个时钟信号和一个或多个数据信号时,使数据与时钟同步。通常数据信号可能仅与时钟的上升沿(rising edges)一起更改值(或仅在下降沿(falling edges)上)。
一种常见但相当危险的方法是将 source-synchronous 时钟直接连接到 FPGA内部的应用逻辑。部分问题在于 source-synchronous 时钟通常不打算用作连续的时钟,因此可能会暂时停止或使用 spurious 脉冲。
另一个可能的问题是 source-synchronous 接口通常通过物理连接器连接到 FPGA ,例如当数据源是通过电缆连接到 main 电路板的相机时。尽管连接器通常是可靠的,但即使由于振动而失去一纳秒的物理接触也足以导致时钟信号上出现非法脉冲(pulse)。当然,这也可能发生在数据信号上,但通常不太重要,特别是当数据源是相机时。但是当这样的时钟信号直接连接到应用逻辑上时,一纳秒的脉冲绝对可以搅得一团糟。
因此, source-synchronous与时钟和数据接口的最佳解决方案是将时钟和数据(data)都视为常规信号。因此, source-synchronous 时钟和数据信号都使用触发器进行采样,使用速度明显更快且稳定且安全的时钟。优选地,这是通过与 I/O 引脚相邻的专用触发器来完成的。
当 source-synchronous 时钟从低电平变为高电平时,这反映在采样此信号的触发器的输出(output)中的类似变化。因此, source-synchronous 时钟的上升沿可以与同步逻辑进行检测,通过这个触发器的输出由低变为高的简单事实。这个逻辑当然是基于更快更稳定的时钟。当这个逻辑检测到这样的上升沿时,它会将数据标记为有效。换言之,包含数据输入(data inputs)值的触发器的输出被标记为有效数据。
这种方法与 01-signal 采样的明显优势在于,无论时钟信号发生什么情况, FPGA的逻辑都会继续依赖安全的时钟。如果 source-synchronous 时钟发疯,则由逻辑检测边沿(edges)做出充分响应。
对于 source-synchronous 时钟的相对较低频率(通常高达 200-300 MHz,取决于 FPGA的速度以及是否使用 DDR 采样),这种技术是可能的。
对于更快的信号源,首选解决方案是将信号源的时钟馈入锁相环,并将锁相环的输出与应用逻辑一起使用。如上所述,当锁相环指示未锁定时,应重置逻辑。出于不同的原因,这也可能是正确的解决方案: 当 01-signal 采样的频率像刚才建议的那样太高时,确保采样(sampling)正确的唯一方法是通过时钟的相位 shifting (phase shifting)找到时间。这意味着逻辑会自动调整时序,直到在采样的信号中没有检测到错误。这种技术无论如何都需要使用锁相环。
原因#4: 违反 RTL 设计规则
综合(synthesis)的正确 Verilog 代码(或 VHDL)必须遵循一些严格的规则,尤其是 RTL 范式(寄存器 Transfer Level(Register Transfer Level))。其中,这意味着作为某种内存(例如触发器)的任何逻辑单元仅作为时钟边沿(clock edge)的结果更改值。唯一的例外是异步复位(asynchronous reset),它不能只是任何信号。
当综合工具(synthesizer)遇到违反这些规则的 Verilog 代码时,它通常会尝试合作并生成可能与模拟显示的行为不同的逻辑。另一种可能性是综合的结果大部分时间都满足预期的行为,但它可能会随机失败。
例如,为 0 和 14之间的计数器考虑这个错误的设计:
reg [3:0] counter;
wire reset_cnt;
assign reset_cnt = (counter == 15); // This is so wrong!
always @(posedge clk or posedge reset_cnt)
if (reset_cnt)
counter <= 0;
else
counter <= counter + 1;
可怕的错误是将 @reset_cnt 用作异步复位。
但让我们开始解释它是如何在模拟中工作的: @counter 对 @clk的上升沿进行计数。但是当 @counter 达到 15值时, @reset_cnt 变为 '1' 并将 @counter 异步复位为零。因此,当使用 @clk对 @counter 进行采样时,它应该显示 0 到 14的值。
在硬件中,这可能不起作用。问题是 @reset_cnt 是 @counter的组合函数(combinatorial function)。因此当 @counter 的值从 7 变为 8 时,计算 @reset_cnt 的逻辑可能会短暂地将 @counter 的值视为 15 。这是因为 7 是 binary 代码中的 0111 ,而 8 编码为 1000。因此,如果比特 3 (bit 3)与计算 @reset_cnt的逻辑之间的传播延迟最短,则该信号可能短暂为 '1' 。因此, @counter 有时会从 0 计数到 14 ,有时会从 0 计数到 7。温度和其他不相关的因素可能会影响观察到的选项之一。
然而,这个例子为什么是错误的解释被大大简化了。这些工具可以以最具创造性的方式免费实现组合逻辑(combinatorial logic),因此时钟边沿之间几乎可以发生任何事情。工具唯一保证的是信号是稳定的,符合触发器在目的地(setup time 和 hold time)的时序要求。
因此,除非逻辑设计严格遵守有关 RTL 设计的规则,否则肯定会发生奇怪的事情。
原因#5: 温度和 power supplies
这不是造成麻烦的常见原因,而且很容易检查。然而,温度和 power supplies 可能是奇怪问题的根本原因。
很自然,如果 silicon的温度超出允许范围,则无法保证正常工作。最常见的原因是由于散热计划不足而导致的过热,或者风扇与灰尘作斗争。
至于 power supplies,他们可能会因为各种原因生产出故障的输出。使用 oscilloscope 进行简单检查通常会显示电压是否在指定范围内。但是请注意,电压应始终保持在此范围内。平均电压正确是不够的: switching power supplies 总是产生的噪音和偶尔的 spikes 都不允许超过限制。
请注意,即使不长于 1 μs 的 spike 看起来是无辜的,但在 FPGA内部却有数十到数百个时钟周期(clock cycles),因此这是 FPGA 输入错误电压的重要时间段。最好测量靠近 FPGA 的 decoupling capacitors 处的电压,以查看实际到达的电压。还要确保在上限和下限电压下配置 oscilloscope的触发器,并确保 oscilloscope 不会对这些做出反应。在 oscilloscope的显示屏上注意到简短的 spikes 并不容易,但触发器会捕捉到它们。
有时 power supply 问题是电路板设计性能不佳的直接结果。许多 power supply 模块都有一个经常被忽视的 minimal 电流。如果这个 minimal 电流没有从 power supply 模块中排出,它可能会变得不稳定并产生不符合其规格的电压,或者更糟糕的是,它可能偶尔会出现 oscillations。
另一个常见的错误是将 switching power supply 放在需要电压 regulator (voltage regulator)的地方。特别是有 low-jitter clock oscillators 需要非常干净的输入。如果这样的 oscillator 由嘈杂的 power supply馈送,则此噪声会反射到时钟输出(clock output)上的抖动中。如果 Gigabit transceiver 使用此时钟(例如 PCIe、 USB 3.x、 fiber optics 等),这通常会导致数据 link(data link)不可靠。
同样,当 DDR memories 是设计的一部分时,需要参考 voltage power supply (reference voltage power supply)。 FPGA 和 DDR memories 都使用此电压作为 '0' 和 '1' 之间在这两个组件之间的电线上的电压阈值。如果这个电压是由 switching power supply产生的,那么电压 supply noise (voltage supply noise)可能会使 FPGA 和 DDR memories之间无错误地传输数据变得更加困难,甚至不可能。
原因#6: 你在开玩笑吧?
有时, black magic 情况的原因是一个如此大的缺陷,以至于人们想知道一切是如何工作的。例如,当电路板上的接线与相关 FPGA 引脚完全断开,而正确的信号通过串扰(crosstalk)或 parasitic 电容到达 FPGA 。
这种情况尤其容易发生在时钟上,因为它们通常在电路板各处布线,而且它们是周期性信号这一事实提高了它们到达 FPGA 的机会,足以让它们看起来不错。
所以无论如何,拿一 oscilloscope 并检查所有时钟尽可能靠近 FPGA。如果时钟有 AC coupling capacitor ,这是一个检查的好地方,特别是因为您可能会发现 capacitor 丢失了。
理由 7: 普通的错误
或者更准确地说: 设计从未工作过。从来没有人坐下来弄清楚逻辑是如何确保工作的。相反,代码逐渐用 trial and error编写,部分使用模拟,部分使用硬件。当一切看起来正常时,这个过程就完成了,但看看代码,它的工作似乎是一个奇迹: 因为它已经被修补了很多次来修复那个小东西,所以不可能跟踪正在发生的事情,更不用说进行更改了。
我把这个原因放在最后,因为它不是真正的 black magic 行为。这只是一个非常烦人的错误。然而,这是 FPGA 项目卡住的最常见原因。
如果你仍然认为这是 FPGA的错
有时这不是你的错。 FPGA 本身或供应商的软件中可能存在错误。这种情况发生的频率比人们倾向于责怪 FPGA的供应商要少得多,但在极少数情况下,情况确实如此。
因为责备别人的自然诱惑,请帮自己一个忙,不要通过指责 FPGA来结束驱魔会议,除非您拥有以下两者之一,或两者兼而有之:
- 供应商提供的 errata record ,与您的情况完全匹配: 问题的原因和结果。这可能有点难以确定,因为 erratas 倾向于故意以模糊的方式编写,特别是为了使问题显得极其具体和罕见,并淡化后果。不要试图将类似的 errata record 解释为与您的情况相匹配。总有一个与您的情况类似的 errata record 。
- 无可争议的 smoking gun: 如果您可以反复暴露一个特定的错误,该错误可以准确解释您的问题发生的原因。仅仅证明这些工具或 FPGA 本身的行为毫无意义是不够的。您需要将其缩小到有缺陷的特定且可重复的逻辑模式。
如果您在没有任何这些的情况下结束并设法以某种方式解决问题,那么您以后很有可能会再次遇到该问题。
我对 FPGA 本身的错误的最佳示例是很久以前,在 Xilinx的 Virtex-4的 hardware FIFO 上。也就是说, FIFO 的 control 逻辑直接在 silicon 中实现(即不在逻辑阵列中)的 dual clock FIFO 。
通过 FIFO 的数据流时不时地卡住。经过一番调查,发现 FIFO 在正常工作一段时间后,同时保持其 empty 信号和 full 信号处于活动状态。这是非法条件,除非 FIFO 被固定在复位中,但事实并非如此。因此,在完全确定我观察到了正确的信号之后,我结束了这个案例,得出的结论是存在错误
FPGA的 FIFO。我选择了在逻辑阵列中实现的 FIFOs 。
几个月后,我在这些 FIFOs 上发现了一 errata record ,除非我事先知道问题所在,否则我是不会理解的。但在仔细阅读描述后,我可以得出结论,它证实了我的观察。
这只是一个例子,说明 FPGA的错误必须有多明显才能将问题声明为“不是我的错”。
概括
当 FPGA 似乎违背自然规律时,人们很容易采用偏离常识的解释。尽管如此,寻找一个合理的解释仍然很重要——而且这种解释通常不需要超级大国就可以找到。
然而,寻找原因可能需要对设计进行彻底的审查,这不一定是一件坏事。这样的狩猎可能令人沮丧,但无论如何它都可能对设计的质量做出重大贡献。