时钟相关¶
时钟是时序逻辑中最重要的组成部分,驱动电路的状态发生变化。在 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 代码中,禁止使用分频的方法生成时钟,否则可能会导致不可预料的时序问题。
频率不准确
由于内部器件的限制,可能无法得到需要的精确频率(配置 IP 时会告知实际频率),并且同一个 PLL 产生的不同时钟频率也很可能会相互制约。在这种情况下,可以使用多个 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)
if (rst) begin
sync <= 2'b0;
end else begin
sync <= {sync[0], other_domain};
end
end
可以将此逻辑封装成模块,以便使用。对于 Vivado 用户,也可使用 XPM (Xilinx Parameterized Macros, UG974) 中的 XPM_CDC_SINGLE
和 XPM_CDC_ARRAY_SINGLE
等模块来直接进行同步(其中同步寄存器级数等都是可配置的参数)。如有更高级的需求,可以参见 XPM_CDC_HANDSHAKE
等模块
异步 FIFO¶
对于分隔于两个时钟域的生产者——消费者模型,可以通过具有两个端口的 FIFO 来进行同步,每端使用自己的时钟进行 enqueue
和 dequeue
即可。
Quartus 中对应的 IP Core 名称为 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_SDPRAM
和 XPM_MEMORY_TDPRAM
模块。