介绍
本页讨论 source-synchronous data 输出。这种技术意味着数据输出(data outputs)与 FPGA 与这些输出(outputs)并行生成的时钟同步。
如果与外部组件的接口仅由 FPGA中的输出端口(output ports)组成,则此方法是显而易见的选择。如本页底部所述,如果 I/O 是双向的,它也适用。还有一个单独的页面讨论了时钟和数据(data)之间的一般关系。
基本应用
考虑这 Verilog 示例:
module top (
input clk,
output reg [7:0] data_out,
output reg sync_clk_out
);
always @(posedge clk)
begin
sync_clk_out <= !sync_clk_out;
if (sync_clk_out)
data_out <= data_out + 1;
end
endmodule
请注意, @data_out 与 @sync_clk_out的下降沿(falling edge)同时更改其值: 当 @sync_clk_out 为低电平时, @data_out 不变。当 @sync_clk_out 为高电平时, @data_out 响应 @clk的上升沿(rising edge)变化为一个新值。
如果IOB 寄存器同时用于 @data_out 和 @sync_clk_out,则所有这些输出信号的值几乎同时改变。因此,接收这些信号的外部组件可以使用 @sync_clk_out 作为其时钟。通常,通过这种方式很容易满足外部组件的时序(timing)要求(假设 @data_out 的采样(sampling)与 @sync_clk_out的上升沿同步)。
使用 DDR 寄存器
上面的例子有一个明显的缺点: @sync_clk_out 的频率是 @clk的频率的一半。因此, @data_out 的传输速率(data rate)也限制为 @clk 的频率的一半。唯一的例外是如果外部组件对 @sync_clk_out的两个时钟边沿(clock edges)都敏感(即它有一个 DDR 输入)。通常情况并非如此。
可以使用输出 DDR register (output DDR register)来生成与数据输出对齐的输出时钟(output clock)。这个想法是使用一个触发器(flip-flop)来改变它在两个时钟边沿上的值。因此,这样的触发器(flip-flop)行为如下:
always @(posedge clk or negedge clk)
if (clk)
sync_clk_out <= 0;
else
sync_clk_out <= 1;
@sync_clk_out 响应上升沿或 @clk而变为低。下降沿则相反。结果是输出时钟和频率相同。
但是,这段 Verilog 代码无法与大多数综合工具(synthesizers)兼容,因为逻辑阵列(logic fabric)中的触发器通常无法同时对时钟边沿(clock edges)敏感。只有 IOB 寄存器才具备这种能力,而综合工具很少聪明到能够理解这种可能性。
因此,需要例化(instantiation)或逻辑 primitive (logic primitive)。 primitive 特定于所使用的 FPGA 。此示例显示了如何为 Kintex Ultrascale完成此操作:
ODDRE1 ODDR_ins
(.C(clk), .Q(sync_clk_out),
.D1(1'b0), .D2(1'b1),
.SR(1'b0));
但现在时钟是用输出 DDR register 生成的,而数据信号(data signals)是用常规 IOB 寄存器生成的。数据和时钟之间的对齐并不像以前那么明显。因此,重要的是阅读所有输出的时序报告(timing reports),并验证时延(delays)之间的差异是否足够小。
也可以将输出 DDR registers (output DDR registers)用于数据: 如果给 D1 和 D2赋予相同的值,则输出对每个时钟周期(clock cycle)只改变一次:
module top (
input clk,
output [7:0] data_out,
output sync_clk_out
);
reg [7:0] data;
ODDRE1 ODDR_ins
(.C(clk), .Q(sync_clk_out),
.D1(1'b0), .D2(1'b1),
.SR(1'b0));
ODDRE1 ODDR_data_ins [7:0]
(.C(clk), .Q(data_out),
.D1(data), .D2(data),
.SR(1'b0));
always @(posedge clk)
data <= data + 1;
endmodule
此方法确保数据和时钟对齐。输出 DDR registers 的显式例化也确保使用 IOB 寄存器。这种方法的唯一缺点是它特定于每 FPGA。
请注意,输出 DDR registers 的行为可能出乎意料且令人困惑。例如,注意 @data 与 @clk的 positive 边沿是同步的。但是 ODDRE1 在两个时钟边沿上都是活跃的。请务必阅读 FPGA 的文档,了解如何以及何时对两个数据输入(data inputs)进行采样。
另请注意,此示例中未使用复位(reset)。这可能是不安全的,特别是在复杂的 FPGAs上: 输出 DDR register 可以是 IOB内部更复杂的逻辑单元(logic element)的一部分。如果没有复位,此类逻辑单元可能无法正常运行。
时序约束(Timing constraints)
那么这种方法需要时序约束吗?如果时钟输出(clock output)和数据输出由于使用 IOB 寄存器而几乎完美对齐,这还不够吗?
答案是即使没有时序约束,这种方法也可以完美地工作。然而,利用时序约束仍然是个好主意。原因与关于 IOB 寄存器的页面中的相同主题相同。
其他注意事项
但是,使用 IOB 寄存器并不是唯一要确保的事情: 还要注意输出端口之间的物理距离。如果输出端口彼此相距较远,则此物理距离可能会对时钟偏移(clock skew)和 FPGA产生不良影响。 traces (即线(wires)和电路板)的长度差异也会产生负面影响。此外,所有输出的电气参数(电压、 I/O 标准、压摆率(slew rate)、 drive current 等)应相同。
如果 FPGA 没有 IOB 寄存器,那么时序约束就变得至关重要: 使用 source-synchronous 输出的基础是能够确保所有相关输出端口之间的低偏移(skew)。在触发器和 I/O 引脚之间控制时延通常就足够了: 如果此时延接近相关 FPGA可能的最小值,则输出之间的差异对于大多数用途而言足够小。
双向 I/O
source-synchronous 输出的主要特点是相关的时钟是由 FPGA生成的。当所有数据信号都指向外部组件时,这显然是合适的。但即使在两个方向上都有数据信号,依赖 FPGA生成的时钟也是有意义的。
但是请注意,数据输入(data inputs)与电路板上的时钟同步,而不是 FPGA 芯片内的时钟。当然,这两个时钟具有完全相同的频率。但存在一个时间差,这取决于输出端口的 clock-to-output 时延。这可能是几纳秒。
因此建议在电路板上做一个回环(loopback): 将 FPGA的时钟输出连接到输入端口。可以将此输入端口视为system synchronous 时钟 : 外部组件的行为就好像它的时钟来自专用的 oscillator,而不是来自 FPGA。这类似于system synchronous 时钟 ,因为 FPGA 和外部组件都依赖于电路板上的相同物理信号。
也可以将数据输入视为source-synchronous 输入 。特别是,如果使用相位 shifting (phase shifting)技术,则不需要时钟回环(clock loopback)。这实际上是与 DDR SDRAM memories接口的常用方式。
重要的结论是在电路板制造之前仔细规划输入信号的时序,必要时考虑增加一个时钟回环(clock loopback)。