01signal.com

FPGA上的Asynchronous resets : 并不像许多人认为的那么容易

此页面是 FPGAs中有关 resets的系列中的三个中的第一个。因为很多人在使用 asynchronous resets 时并不知道它们并没有像他们期望的那样真正工作,所以第一页解释了为什么整个主题并不像看起来那么容易。

你的 reset 真的好用吗?

考虑这个重置 state machine的示例,这是错误的:

always @(posedge clk or negedge resetn)
     if (!resetn)
       state <= ST_START;
     else
       case (state)
	 ST_START:
	   begin
	      state <= ST_NEXT;
	      [  ... do some stuff maybe? ... ]
	   end

	 ST_NEXT:
	   begin
	      [ ... do something ... ]
	   end
       endcase

那么这里的 reset 有什么问题,您可能想知道?这与他们在教科书中显示的完全一样!一个 active low asynchronous reset,它将实现 @state 的 register 带到其初始状态。会出什么问题?

为了便于讨论,我们假设 @resetn 信号本身是可以的。换句话说,它没有直接连接到 pushbutton 或类似的东西。可能使用了设计用于生成 reset 信号的 chip ,或者 reset 是由 FPGA内部生成的。这样或那样,我们将假设 reset 以稳定的方式变为活动状态,它保持活动状态足够长的时间,然后变为非活动状态。还是错了。

当我说错时,我的意思是那种让 FPGA 时不时地表现得很奇怪的错误,没有明显的原因。

所以有什么问题?好吧,因为 reset 的活动时间足够长,它肯定会将 @state 带入初始状态。但是当变为非活动状态(即在上面的示例中变回 '1' )时会发生什么?这时候相关的 flip-flops 应该开始响应 @clk的 rising edges 。

然而, flip-flop 从 reset 信号中恢复并开始在 rising clock edges上对 data input 进行采样需要一点时间。由于 reset 根据定义是异步的,因此与 @clk相关的任何时候都可以停用它。

如果 @clk 的第一个 rising edge 在 reset停用后过早到达,则 flip-flop 会忽略此 rising edge。如果连接到 @clk 的所有 flip-flops 都这样做,那就太好了。但并非所有 flip-flops 都完全相同,有些人比其他人更早地获得 clock edge ,有些人比其他人更晚获得 reset 的停用。

说实话,这个解释有点过于简单化了。为了更准确地了解问题,请参阅解释 timing基础知识的页面上关于 Recovery 和 Removal 的部分。

所以归结为: 如果运气不好, reset 可能会变得不活跃,离 clock的 rising edge足够近,因此一些 flip-flops 会响应第一个 rising edge,而其他人会忽略它。事实上,一些 flip-flops 可能需要一些额外的时间来决定要做什么。怪 flip-flops 在同一个 chip 上的区别还是怪 clock skew 和 reset skew: 底线是一些 flip-flops 比其他 clock cycle 领先。

要了解这有多糟糕,请考虑上面的示例。如果 synthesizer 识别出这是 state machine,那么它很有可能会使用 one-hot encoding实现 state variable 。换句话说,它为每个 state分配一个 single-bit register 。当 state machine 位于相关的 state中时,这些 registers 中的每一个都处于活动状态。假设 synthesizer 为名为 ST_START的 state 分配了一个名为 hot_state_0 的 register ,为 ST_NEXT分配了 hot_state_1 。显然, reset 使 hot_state_0 激活并禁用 hot_state_1。

现在请注意, state machine 无条件地从 ST_START 移动到 ST_NEXT。因此, hot_state_0 在 reset 发布后的第一个 clock 上变为非活动状态,而 hot_state_1 变为活动状态。

但是,如果 reset 在不走运的情况下变得不活跃,以至于一些 flip-flops 错过了第一个 clock edge 而另一些没有呢?一种可能是 hot_state_0 错过了第一个 clock,但 hot_state_1 响应它。结果,两者都变为活动状态,这是 one-hot encoding的非法条件。如果反过来,两个 registers 都处于非活动状态,所以实际上 state machine 的所有 one-hot registers 都处于非活动状态。无论哪种方式, state machine 都可能永远无法恢复到合法状态。

这在实践中会如何?当然,这取决于应用程序,但很有可能在 reset 再次应用于 FPGA 之前,某些东西将无法正常工作。找到这种行为的原因可能非常困难,因为这个问题会随机出现,而且不一定经常出现。从一个 FPGA design 的 compilation 到另一个,它的行为也可能不同,并且可能从一个电子板到另一个不同。简而言之,这是一种能让你发疯的错误。在这个讨论中听起来可能并没有那么糟糕,因为问题的根源是这个讨论的主题。但是当现实生活中出现这样的不稳定性时,它可以是任何事情,而且常常感觉 FPGA 是闹鬼的

但我一直这样做,而且有效!

的确。在绝大多数情况下,如果某些 flip-flops 错过了 reset之后的第一个 clock ,这并不重要。

上面的 state machine 示例失败的主要原因是它在第一个 clock cycle上保留了初始状态。现实生活中的 designs 中的大多数 state machines 都有远离初始状态的规则,所以在最初的几个 clock cycles中它们总是停留在这个状态。所以一个人逃脱了这个错误。

但这是另一个例子。一个简单的 counter:

reg [15:0] counter;

   always @(posedge clk or negedge resetn)
     if (!resetn)
       counter <= 0;
     else
       counter <= counter + 1;

在这种情况下, @counter 由 16 个 flip-flops组成。每个 flip-flops 在其 data input以及 @resetn 在其 asynchronous reset input处接收下一个 clock cycle 的计数器值。

当 @resetn 有效时, @counter 的值为 0,下一个 clock cycle 的计数器值为 1。因此,除了 counter[0] 之外的所有 flip-flops 都保持为零,无论它们是否错过了第一个 clock edge 。因此,无论哪种方式,它都会正确开始计数。在编写这样的代码的绝大多数情况下,计数器是否错过了第一个 clock cycle 并不重要。

然而,这是一个不同的故事:

reg [15:0] counter;

   always @(posedge clk or negedge resetn)
     if (!resetn)
       counter <= 0;
     else
       counter <= counter - 1;

一个很小的差异,但很重要: 如果计数器从零开始并向计数,则下一个 clock cycle 处的计数器值为 0xffff。换句话说,所有 flip-flops 必须在 reset之后的第一个 clock 上更改它们的值。因此,如果有些人在 reset之后响应第一个 clock edge ,而其他人不响应,则计数器可以从几乎任何随机值开始。

但是谁用零值重置计数器然后倒计时呢?

所以这里有一个更现实的例子: clock enable 信号使 logic 的行为就像 clock的频率降低到一半(因此如果需要,允许 multi-cycle path ):

reg en;

   always @(posedge clk or negedge resetn)
     if (!resetn)
       en <= 0;
     else
       en <= !en;

   always @(posedge clk)
     if (en)
       [ ... do something ... ]

人们可能会认为这里没有任何问题: clock enable、 @en是单个 register,所以它什么时候开始切换并不重要,还是......?问题是 clock enable 信号往往具有较高的 fan-out,因此 synthesizer 可能会复制它以避免超出 fan-out的限制。

我对 Vivado synthesizer 的轶事实验表明,实现 @en 的每个复制 registers 都依赖于自己的 output signal。换句话说,没有一个单一的信号可以让所有 flip-flops 用来决定他们的下一个 output 应该是什么。相反,有许多独立的 flip-flops 总是在 clock的 rising edge 上改变它们的价值。因此,如果这些 flip-flops 没有开始在同一个 clock cycle上切换,它们的 outputs 将无限期地保持不同。

如果发生此类事故, logic 很可能会完全失灵。因此,如果您坚持使用 asynchronous reset,至少要确保所有 clock enables 都依赖于单一来源,可能如

reg pre_en; // Apply some don't-touch synthesis directive on this
   reg en;

   always @(posedge clk or negedge resetn)
     if (!resetn)
       pre_en <= 0;
     else
       pre_en <= !pre_en;

   always @(posedge clk or negedge resetn)
     if (!resetn)
       en <= 0;
     else
       en <= pre_en;

   always @(posedge clk)
     if (en)
       [ ... do something ... ]

诀窍是 @pre_en 是决定下一个值的 register 。 flip-flop 的 fan-out 较低,可能还有一些属性告诉 synthesizer 不要篡改它。这样一来,它肯定是单个 register。所有实现 @en 的 flip-flops 都依赖于 @pre_en,所以他们在下一个 clock上都同意 @en 的值。至于 reset之后的第一个 clock ,被一些 flip-flops漏掉也没关系,因为第一个 clock cycle 上的 @en 值无论如何都是零。

所以底线是错误地使用 asynchronous reset 通常会很好,主要是因为 logic 通常可以容忍 reset 相对于 clock何时变为非活动状态的不确定性。然而,不小心使用 asynchronous resets 可能会导致偶发的错误行为,这可能是极难解决的。

在 reset 和 clock之间使用 timing constraint

避免 reset的停用和 clock的 rising edge 之间的不确定时序关系的看似明显的方法是在 reset 信号上使用 timing constraint 。但是,这样做会使 reset 信号同步。

但是与哪个 clock同步?为整个 logic design使用一个全局 asynchronous reset 通常很方便。这个 reset 是用与一个特定的 clock同步的 logic 创建的。如果此 reset 与与不同 clock同步的 logic 一起使用,我们就有了 clock domain crossing。这本身就是一个话题,但最重要的一点是工具可能会忽略相关 paths上的 timing 。

所以在 asynchronous resets 上使用 timing constraints 的出发点是每个 clock必须有一个单独的 asynchronous reset 。或者,如果您坚持,为每组 related clocks分配一个单独的 asynchronous reset 。否则 timing constraint就没有意义了。如果这听起来很奇怪,那是因为 asynchronous reset 不再是异步的。

Verilog 代码使用 asynchronous reset 的模式这一事实没有任何区别。是否使用 flip-flop的 asynchronous reset input 以及 flip-flop 是否配置为将其 reset input 视为异步都无关紧要: 如果 reset 与 clock同步,并且使用 timing constraint ,那么 reset 实际上是同步的。对于这种情况,您可以考虑直接使用 Verilog 模式:

always @(posedge clk)
     if (!resetn)
       state <= ST_START;
[ ... ]

也就是说, Intel在 timing closure上的 Youtube 视频宣传使用与 clock同步的 asynchronous resets ,也应该使用 timing constraints 。选择 asynchronous reset 是为了在 FPGA中为 global routing 使用专用资源。我觉得这很奇怪,因为即使 global routing 也可能有很大的 delay,特别是在大型 FPGAs上。但肯定有一些场景是有意义的。

实际上,即使 asynchronous reset 是有效同步的,仍然使用 asynchronous reset 还有另一个原因,这将在下一页讨论: 它允许异步传播 reset 信号的激活,这在 ASICs的模拟和测试中很有用。

如果使用具有同步 asynchronous reset 的此方法,请务必确保 timing constraint 在转到 flip-flops的 asynchronous reset inputs的所有 signal paths 上强制执行。 reset 由与目标 flip-flop相同的 clock 同步的 flip-flop 生成这一事实本身并不能确保它们之间的 path 是定时的。默认情况下,某些 timing 工具会忽略以 asynchronous inputs结尾的任何 path ,因此可能需要明确启用此强制执行。务必在 timing reports 中验证这些 paths 确实被 timing constraints覆盖。很容易落入这个。

本系列关于 resets的第一页到此结束。下一页讨论 resets 的不同选项和FPGA 的初始化

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (6f913017)