介绍
本页介绍 01-signal 采样作为与 source-synchronous 输入连接的方法。有关 source-synchronous 输入的页面概述了与此类数据源交互的其他策略。该页面还解释了 source-synchronous 输入是什么。
01-signal 采样背后的想法是将外部时钟视为数据信号(data signal): 因此,时钟由寄存器(register)采样。该寄存器使用内部时钟,稳定且独立于外部时钟。
数据信号由额外的寄存器采样。相同的内部时钟用于此目的。该内部时钟也用于实现 01-signal 采样的所有逻辑。
该逻辑检测对外部时钟进行采样的寄存器上的变化。当这个寄存器(register)从 '0' 变成 '1'的时候,这意味着外接的时钟上多了一个上升沿(rising edge)。逻辑通过将另一个寄存器的值写入 FIFO来响应此事件。这些寄存器包含外部时钟的上升沿(rising edge)发生时存在的数据信号的值。
该逻辑实现的结果与 FIFO的时钟是外部时钟并且 FIFO的数据输入(data inputs)直接连接到数据信号相同。区别在于逻辑使用的是哪个时钟: 外部时钟或内部时钟。 01-signal 采样的优点是所有逻辑只依赖于内部的时钟。这款时钟稳定可靠。即使外部时钟行为不当,逻辑仍会继续以合理的方式运行。
下图展示了 01-signal 采样:
在此图中, @stable_clk 是 FPGA的内部时钟。 @data_clk 和 @data 是到达 FPGA的信号。 @data_clk_samp 和 @data_samp 是 FPGA内部的寄存器。外部信号 @data_clk (signal @data_clk)由 @data_clk_samp表示。 @data_samp 与 @data的关系也是如此。
该图说明当“0 1”模式出现在 @data_clk_samp中时, @data_samp 的值被写入 FIFO。这就是该方法被称为“01-signal 采样”的原因。
这里以 FIFO 为例,说明如何处理到达的数据。这通常是一个合适的解决方案,因为内部时钟的频率明显高于传输速率(data rate)。因此,使用基于较低时钟频率的逻辑继续处理数据通常很方便。 FIFO 是一种将数据移交给不同时钟域(clock domain)中的逻辑的便捷方法。
尽管如此,仍可以使用与 01-signal 采样相同的内部时钟来实现逻辑的其余部分。使用 FIFO 只是一种可能性。
Verilog中的示例
这段 Verilog 代码说明了这个想法。外部的时钟是 @data_clk。
module top (
input stable_clk,
input data_clk,
input [7:0] data
);
reg [7:0] data_guard, data_samp;
reg data_clk_guard, data_clk_samp, data_clk_samp_d;
wire fifo_wr_en;
always @(posedge stable_clk)
begin
data_guard <= data;
data_clk_guard <= data_clk;
data_samp <= data_guard;
data_clk_samp <= data_clk_guard;
data_clk_samp_d <= data_clk_samp;
end
assign fifo_wr_en = data_clk_samp && !data_clk_samp_d;
data_fifo fifo_i
(
.wr_clk(stable_clk),
.din(data_samp),
.wr_en(fifo_wr_en),
[ ... other ports connected here ... ]
);
endmodule
重要的部分是FIFO的 wr_en: @fifo_wr_en。此信号等于“data_clk_samp && !data_clk_samp_d”。因此,由于在 @data_clk_samp上检测到“0 1”模式,该信号处于高电平。这会导致 @data_samp 的值被写入 FIFO。
请注意,逻辑仅将 @stable_clk 用作时钟。 @data_clk 被视为普通的 I/O 输入。
@data_guard 和 @data_clk_guard 的时序要求(timing requirements)不予保证: 输入端口相对于 @stable_clk是异步的。 @data_guard 和 @data_clk_guard 因此是 metastability guards。逻辑不直接依赖于这两个寄存器的值。另一方面, @data_samp 和 @data_clk_samp 直接被逻辑使用,因为这些寄存器是 metastability guard的第二步。
但这真的有效吗?答案就在时序分析(timing analysis)中。
时序分析
01-signal 采样的时序分析与通常的做法不同: 通常,时钟边沿(clock edge)和数据信号采样时刻之间存在恒定的时间差。这是因为用于采样(sampling)的时钟与数据信号同步。但对于 01-signal 采样, @data 的采样是通过 @stable_clk完成的。因此采样的时刻与数据信号自己的时序(timing)无关。相反,逻辑仅选择接近上升沿的 @data_clk_samp 值。
所以数据时钟(data clock)的上升沿时刻和实际采样发生时刻之间存在随机时间差。这种方法怎么可能可靠呢?
逻辑设计中有一个单独的页面介绍时序的基础知识。该页面解释了 tsu 和 thold的含义。长话短说,触发器(flip-flop)的输入端口必须在时钟的上升沿之前和之后保持稳定(假设触发器在上升沿上激活)。 tsu 定义了输入端口必须在上升沿之前稳定的时间。 thold 定义了输入端口在上升沿之后必须保持稳定的时间。如果违反这些条件之一,触发器对上升沿的响应将不可预测。
可以用另一种方式来看待这个问题: 输入端口必须在上升沿附近的特定时间段内保持稳定。我们把这个时间段称为 Δt = tsu + thold。基于 Δt 和其他参数(parameters),我们现在将找到确保可靠运行的时序要求。
整个时序分析(timing analysis)是基于 @data_clk_guard 为高、 @data_clk_samp 为低的情况。发生这种情况时,时钟周期(clock cycle)后会检测到“0 1”模式。换句话说,在下一个时钟周期(clock cycle)上, @data_clk_samp_d 将是“0”, @data_clk_samp 将是“1”。 @fifo_wr_en 将因此而变高,因此 @data_samp 必须包含数据源预期的值。
因此,分析将集中在这个问题上: 当 @data_clk_guard 为高电平、 @data_clk_samp 为低电平时,对 @data 有什么要求才能保证信息正确到达?
分析将分两部分进行: 在这两个部分中,我都假设 @data_clk_guard 为高电平, @data_clk_samp 为低电平。在第一部分中,问题是: 在不打破这个假设的情况下, @data_clk 可以从低到高变化的最新时刻是什么?然后我将找到关于 @data 的时序要求,以确保 @data_samp 包含正确的值。
在分析的第二部分,我会问相反的问题: 在不打破 @data_clk_guard 和 @data_clk_samp假设的情况下, @data_clk 最早能从低电平变为高电平的时刻是多少?接下来我会对时序要求做类似的分析。
但首先,让我们定义一些符号:
- tclk: @stable_clk的时钟周期。
- tskew: 这是 @data_clk 和 @data的端口之间的偏移(skew)。这是从信号的源到 FPGA的触发器的所有连接中信号时延(signal delays)之间的最大差异。
- tj: 抖动(jitter)对时钟周期的贡献最大。换句话说,两个时钟边沿(clock edges)之间的时间差始终是 tclk - tj 和 tclk + tj之间的值。或者更准确地说,时钟周期超出这些限制的可能性可以忽略不计。
第一部分分析
这份时序图(timing diagram)展示了 @stable_clk的两个时钟周期。在下面的讨论中, @data_clk_guard 将其新值从上升沿上的 @data_clk 复制到右侧。同样, @data_guard 将其新值从 @data 复制到同一时钟边沿上。
同样的原理, @data_clk_samp 和 @data_samp 与左边的 @stable_clk的上升沿相关。
在这种情况下, @data_clk 恰好在黄色区域的末端变为高电平。触发器的时序要求被侵犯,所以结果不可预测。但有可能 @data_clk_guard 会高, @data_clk_samp 会低。
然而,如果 @data_clk 稍后改变其值, @data_clk_guard 肯定会很低,因为触发器的行为是可以预测的: 在这种情况下,输入端口在整个黄色期间都处于低电平。这就是为什么我们可以这样说: 如果 @data_clk_guard 为高且 @data_clk_samp 为低,则 @data_clk 从低到高的变化早于上面时序图(timing diagram)中所示的情况。因此,时序图显示了 @data_clk 在最晚可能时刻发生变化的场景。
为了确保 @data_guard 包含可靠的值, @data 必须在黄色区域之前保持稳定。这给了我们第一个时序要求(timing requirement): @data 必须在 @data_clk的上升沿之前稳定 Δt 的时间段。
注意,如果 @data_clk 提前从低电平变为高电平,这个时序要求仍然可以保证 @data_guard的 tsu 要求。因此,触发器的 tsu 在 @data_clk_guard 为高、 @data_clk_samp 为低的所有场景下都有保证。
上面的时序图没有显示任何与偏移相关的内容。为了考虑到偏移,时序要求为: @data 必须在 @data_clk的上升沿之前稳定 Δt + tskew 的时间段。
本场景无需考虑抖动,因为讨论中只涉及一台时钟边沿。
第二部分分析
这是针对此场景的时序图:
在这种情况下, @data_clk 恰好在 @stable_clk的前一个时钟边沿的黄色区域的开始处变为高电平。
毫无疑问,在这种情况下, @data_clk_guard 会很高。但是, @data_clk_samp 的值无法预测,因为之前的时钟周期上有时序违反(timing violation)。与之前一样, @data_clk_samp 可能会很低。但如果 @data_clk 的值在此之前改变, @data_clk_samp 肯定会很高。
因此,如果 @data_clk_guard 为高且 @data_clk_samp 为低,则 @data_clk 从低到高的变化晚于上面时序图中所示的时间。因此,时序图尽早展现了 @data_clk 发生变化的场景。
为了确保 @data_guard 包含正确的值, @data 必须稳定在黄色区域向右之后。
根据上面的时序图, @data_clk的上升沿与第二个黄色区域结束的时间差为 Δt + tclk。但我们还需要考虑偏移和抖动。因此, @data 必须在 @data_clk之后的 Δt + tclk + tskew + tj 时间内保持稳定。
请注意,如果 @data_clk 较早地从低变为高,则此时序要求仍然覆盖 @data_guard的 hold 要求。因此,触发器的 thold 在 @data_clk_guard 为高、 @data_clk_samp 为低的所有场景下都有保证。
时序要求
总结上面的时序分析,这是保证 01-signal 采样可靠运行的两个时序要求:
- @data 必须在 @data_clk的上升沿之前稳定至少 Δt + tskew 的时间段。
- @data 必须在 @data_clk的上升沿之后至少稳定 Δt + tclk + tskew + tj 的时间段。
这些要求可能看起来很复杂,但通常很容易确保实现它们。例如,如果 @data 随着 @data_clk的 falling 边沿一起改变,通常很容易保证这些要求。如果 @stable_clk 的速度是 @data_clk的三倍,通常就足够了。
在大多数情况下,无需知道 Δt、 tskew 或 tj的准确值。通常,根据上述两个要求计算出 Δt 和 Δt + tskew + tj 允许的大小就足够了。如果 @stable_clk的频率足够高,这两个表达式的值通常大于 FPGA的实际可能值。
IOB 寄存器应该用于最小化 FPGA和输入端口(tskew) 之间的时序差异。另外,时序约束(timing constraints)应该与这些输入端口相关联地编写,以确保使用IOB 寄存器 。
01-signal 采样的变体
在到目前为止的讨论中,我假设 @data 与 data_clk的下降沿(falling edge)一起变化。如果 @data 与 @data_clk的 rising 边沿一起变化,则应调整逻辑,使其在 @data_clk_guard 为低电平且 @data_clk_samp 为高电平时变为活动状态。换句话说,逻辑应该通过查找“1 0”模式来检测 @data_clk 的下降沿。
也可以选择不同的标准来获取 @data的值。例如,将 @fifo_wr_en 延迟几个时钟周期可能会更好。有时,如果 @fifo_wr_en 比上面建议的更早激活会更好。这取决于 @data_clk 和 @data之间的时序关系。参考信号源的数据手册(datasheet),并分析如上所示的时序。
如果 @data_clk的频率比较高,可以用 DDR 寄存器来采样 @data_clk 和 @data。实现此解决方案的逻辑稍微复杂一些,但原理相同。
@data_guard 真的是 metastability guard吗?
这个问题的简短答案是肯定的。 @data 与 @stable_clk异步,因此 @data_guard的时序要求不保证。
但让我们将讨论范围缩小到实际使用 @data_guard内容的时钟周期: 请注意,上面的两个时序要求确保实现 @data_guard 的触发器可靠运行。这些触发器的 tsu 和 thold 都是有保障的。
因此, @data_guard 并非真正用作 metastability guard。就此寄存器的使用方式而言,它仅用作时延寄存器(delay register)。但额外的寄存器可防止时序违反发生故障(如果物理信号(@data) 出现故障),这不会造成任何损害。当信号通过连接器连接到 FPGA 时,这一点尤其重要。
一个现实生活中的例子
在另一个页面上有一个逻辑与 OV7670 camera sensor 连接的示例。此逻辑通过 01-signal 采样从此摄像头传感器获得像素数据(pixel data)。摄像头传感器的输入如下:
input pclk_in;
input [7:0] D_in;
input hsync_in, vsync_in;
执行 01-signal 采样的逻辑如下:
(* IOB = "TRUE" *) reg [7:0] D_guard;
(* IOB = "TRUE" *) reg pclk_guard, hsync_guard, vsync_guard;
reg [7:0] D;
reg pclk, hsync, vsync;
wire sample_valid;
reg previous_pclk;
always @(posedge stable_clk)
begin
// Metastability guards on asynchronous inputs
D_guard <= D_in;
pclk_guard <= pclk_in;
hsync_guard <= hsync_in;
vsync_guard <= vsync_in;
D <= D_guard;
pclk <= pclk_guard;
hsync <= hsync_guard;
vsync <= vsync_guard;
previous_pclk <= pclk;
end
assign sample_valid = pclk && !previous_pclk;
为了清楚起见,这里介绍的 Verilog 代码与 OV7670页面上的 Verilog 代码略有不同。这些差异对逻辑的工作方式没有影响。
让我们将这款 Verilog 代码与我在本页顶部介绍的代码(code)进行比较。信号的名称不同,但它们的含义相同: 我们现在有了 @D_in、 @hsync_in 和 @vsync_in,而不是 @data。我们现在有了 @pclk_in,而不是 @data_clk。我们现在有了 @sample_valid,而不是 @fifo_wr_en。名称的变化可能会让人困惑,但与上面的 Verilog 代码相比并没有什么变化。
请注意寄存器声明之前的“(* IOB = "TRUE" *)”部分。使用 Vivado时,这是请求将寄存器插入 IOBs的可能方法。
在此示例中,未显示 FIFO 。这是因为我们不想将所有数据写入 FIFO: 当 @sample_valid 为高电平时,意味着 @D、 @hsync 和 @vsync 包含来自相机传感器的正确值。但这并不意味着我们要将 @D 写入 FIFO: 取决于 @hsync 和 @vsync。因此, OV7670 的示例中还有额外的逻辑,可确保仅将像素写入 FIFO。
但让我们进入有趣的部分: 时序分析。
本例中 @stable_clk 的频率为 100 MHz, @pclk_in 的频率为 25 MHz,根据 OV7670的数据手册,保证 @pclk_in 由高变低后, 5 ns 期间信号 @D_in、 @hsync_in 、 @vsync_in 稳定(下降沿)。
@pclk_in的时钟周期是 40 ns。因此,从下降沿到上升沿的距离是 20 ns。现在我们来与时序要求进行比较。
第一个时序要求是 @D_in、 @hsync_in 和 @vsync_in 必须在 @pclk_in的上升沿之前稳定 Δt 的时间段。实际上,这些信号从下降沿之后 5 ns 开始就稳定了。因此,这些信号至少在下一个上升沿之前的 15 ns 是稳定的。回想一下 Δt = tsu + thold。所以时序要求其实就是 tsu + thold 比 15 ns小。对于每台 FPGA都是如此。
第二个要求是这些信号在 @pclk_in的上升沿之后至少稳定一段时间 Δt + tclk + tskew + tj 。但这些信号只会因下降沿而改变。因此要求是 Δt + tclk + tskew + tj 小于 20 ns。 tclk 等于 10 ns ,因为 @stable_clk的频率是 100 MHz。因此实际要求是 Δt + tskew + tj 小于 10 ns。同样,每 FPGA都很明显。
此示例演示了如何在不了解 FPGA的确切时序参数(timing parameters)的情况下轻松确保时序要求。
结论
当传输速率相对于时钟频率较低时,01-signal 采样是一个很好的解决方案,而 FPGA 可以支持: 数据时钟不必稳定,另外,数据时钟的具体频率也不必事先知道,只要保证两个时序要求就足够了。
此方法还有其他优点: 如果此时钟短暂处于非活动状态,唯一的后果是数据(data)在该特定时间段内无法被收集。此时钟的任何故障造成的损害仅限于数据流中断。这将导致系统出现可见故障,但此故障看起来像是时钟的问题(而不是FPGA 被鬼魂困扰)。
因此,尽管数据信号的采样具有固有的随机性,但 01-signal 采样是 source synchronous 输入的可靠且稳健的解决方案。唯一真正的缺点是传输速率的限制。