跳转至

时钟相关

时钟是时序逻辑中最重要的组成部分,驱动电路的状态发生变化。在 FPGA 逻辑设计中,正确使用时钟非常重要。

时钟产生

FPGA 中,时钟可以由以下两种方式产生:

  • 外部输入:由 FPGA 芯片外部的晶振的周期震荡产生并输入到 FPGA 中;
  • 内部生成:FPGA 内部具有 PLL (Phase-Locked Loops) 或 MMCM (Mixed-Mode Clock Manager) 等时钟管理组件可以根据已有的时钟产生新的时钟,并更改频率、相位等属性。

通常,如果需要的时钟频率与输入的不一致(更高或者更低),就需要使用 PLL/MMCM 来生成新的时钟。Quartus 与 Vivado 均提供了相应的 IP Core,分别称为 PLL 和 Clocking Wizard。 在初始化 IP 时,提供输入时钟的频率、抖动(可取默认值)等信息,而后指定需要的时钟信息,即可生成新的时钟供使用。 一般来说,此类 IP 会提供一个名称类似 locked 的信号,表明输出是否稳定,可以用作其生成的时钟对应的(异步)复位信号(注意极性)。

禁止分频

在非 testbench 代码中,禁止使用分频的方法生成时钟,否则可能会导致不可预料的时序问题。

对于少数需要输出分频后的时钟信号到外设的协议,需要十分谨慎地实现(例如从寄存器直接输出,减少延迟),并辅以相应的时序约束(保证输出信号到芯片输入端时满足 setup/hold 等条件),实现前请查阅资料并咨询助教。

频率不准确

由于内部器件的限制,可能无法得到需要的精确频率(配置 IP 时会告知实际频率),并且同一个 PLL 产生的不同时钟频率也很可能会相互制约。在这种情况下,可以使用多个 PLL 尝试缓解问题,但同时也要注意 FPGA 的 PLL 数量是有限的。

每个(输入或者生成的)时钟与它驱动的所有寄存器,构成了一个独立的 时钟域(clock domain) 。注意,这是时钟域的 唯一判断方式 。即使两个时钟的频率和相位相同,甚至由同一个 PLL 生成,也不能轻易认为属于一个时钟域。

注意事项

在使用时钟信号时,需要注意以下事项,否则可能导致时序问题:

  • 禁止 使用时钟信号的下降沿。
  • 禁止 将时钟信号进行手动分频或者与其他信号进行逻辑运算后,再作为时钟信号使用(称为门控时钟,gate-controlled clock)。
  • 不建议 用 PLL 生成频率太高(如数百 MHz)或者过低(如低于 1000Hz)的时钟,这通常并不能成功。在设计中,通常也不需要这么做。
  • 不推荐 在设计中使用过多的时钟,核心逻辑尽量使用单一时钟域。
  • 建议 在时钟信号名称中标注频率或用途,以防混淆。

跨时钟域

由于两个时钟域的信号变化周期不同,因此通常不能直接使用跨过时钟域采样的信号;否则,可能由于数据建立时间不足产生的亚稳态获得错误的结果。当需要在两个时钟域间(包括 FPGA 与外部器件间)传递数据时,一般采取以下方法:

谨慎思考

有很多常见的操作不需要跨时钟域完成,尤其是每隔一段固定时间间隔进行某项操作。为达成这一目的,使用状态机计数即可。通常来说,VGA、SFP 等对于时序要求比较严格的信号,才需要自己的时钟域。在查看下面的说明之前,请务必仔细思考跨时钟域的必要性。

寄存器同步

对于少量控制信号,使用(至少)两个寄存器进行采样即可(clk 为需要使用此信号的时钟域):

wire other_domain;
reg [1:0] sync;
wire this_domain;

assign this_domain = sync[1];

always @(posedge clk) begin
    if (rst) begin
        sync <= 2'b0;
    end else begin
        sync <= {sync[0], other_domain};
    end
end

可以将此逻辑封装成模块,以便使用。对于 Vivado 用户,也可使用 XPM (Xilinx Parameterized Macros, UG974) 中的 XPM_CDC_SINGLEXPM_CDC_ARRAY_SINGLE 等模块来直接进行同步(其中同步寄存器级数等都是可配置的参数)。如有更高级的需求,可以参见 XPM_CDC_HANDSHAKE 等模块

异步 FIFO

对于分隔于两个时钟域的生产者——消费者模型,可以通过具有两个端口的 FIFO 来进行同步,每端使用自己的时钟进行 enqueuedequeue 即可。

Quartus 中对应的 IP Core 名称为 FIFO(IP 设置中选择 No common clock,对应的模块名字为 DCFIFO (读写宽度相同)和 DCFIFO_MIXED_WIDTHS(读写宽度不同)),Vivado 中为 FIFO Generator (PG057),XPM 中也有相应的 XPM_FIFO_ASYNC 模块。

异步双口 RAM

对于更一般的需求,可以通过具有两个时钟域的两个读写端口的 Block RAM 来传递数据,每端使用自己的时钟进行操作。

Quartus 中对应的 IP Core 名为 Simple Dual-port RAM(一口读、一口写)和 True Dual-port RAM(两口均可读写),Vivado 中为 Block Memory Generator(配置时同样可选前述两种模式),XPM 中有相应的 XPM_MEMORY_SDPRAMXPM_MEMORY_TDPRAM 模块。


最后更新: 2022年4月14日
作者:Jiajie Chen (6.1%), Harry Chen (92.68%), Wende Tan (1.22%)

评论