3. 时钟资源与时钟网络

在Quartus多时钟系统设计中,时钟资源不是“随便接一根线”就能用的。FPGA内部有专门的时钟网络,它们负责把时钟信号以低偏斜、低抖动的方式送到成千上万个触发器。如果时钟信号走的是普通布线资源,偏斜可能达到纳秒级,建立/保持时间余量会被迅速吃掉,系统在时序收敛阶段就会非常痛苦。因此,理解全局时钟、区域时钟、时钟控制块以及时钟使能和分频资源,是写出稳定多时钟设计的前提。

全局时钟网络与区域时钟网络

以Intel/Altera Cyclone、Arria、Stratix系列为例,器件内部通常提供两类主要时钟网络:全局时钟网络(Global Clock Network)和区域时钟网络(Regional Clock Network)。全局时钟网络可以驱动整个芯片内所有逻辑单元,适合高扇出、需要全芯片同步的时钟,例如系统主时钟、DDR参考时钟、全局复位同步时钟。区域时钟网络则只覆盖芯片的某个象限或区域,扇出较小、功耗较低,适合局部模块使用的时钟,例如某个高速接口的采样时钟、局部状态机的节拍时钟。

在Quartus中,时钟网络的使用并不是完全由设计者手工指定的。当你把某个引脚或PLL输出分配给时钟输入引脚时,Fitter会尽量把它分配到专用时钟网络上。但如果你把普通用户逻辑产生的信号当作时钟使用,Fitter可能只能把它放在普通布线资源上,这时就会在编译报告中看到“Global clock not used”或“Clock signal is not on a global network”之类的警告。正确的做法是:外部时钟从专用时钟引脚输入,PLL输出从专用时钟输出引脚引出,内部产生的分频时钟尽量用PLL或时钟控制块实现,而不是用计数器分频后直接当时钟。

FPGA时钟资源与时钟网络结构示意 外部晶振 50 MHz 专用时钟引脚 CLK_IN PLL 倍频/分频/相移 c0=100MHz c1=25MHz 时钟控制块 Clock Control Block 使能/旁路/选择 动态切换 全局时钟网络 Global Clock Network 低偏斜 · 高扇出 · 覆盖全芯片 区域时钟网络 A 局部象限 · 低功耗 区域时钟网络 B 局部象限 · 低功耗 触发器 / 寄存器阵列 时钟到达各触发器的偏斜决定时序余量 全局时钟输出 区域时钟输出 时钟 使能 CE 时钟 分频 /2 /4

时钟控制块的作用

时钟控制块(Clock Control Block,CCB)位于时钟网络的入口处,它决定了哪个时钟源可以进入全局或区域时钟网络。每个时钟控制块通常支持以下功能:时钟选择(从多个候选时钟中选一个)、时钟使能(Clock Enable,CE)、时钟旁路(Bypass)以及动态切换。在Quartus中,时钟控制块一般不需要手工实例化,而是由Fitter根据你的时钟分配自动推断。但如果你需要动态切换时钟源,例如在运行中从100 MHz切换到25 MHz,就需要使用ALTCLKCTRL原语或通过PLL的时钟切换功能来实现。

时钟控制块的一个重要价值是:它允许你在不停止整个系统的情况下关闭某个时钟域。例如,某个低速外设模块只在特定阶段工作,你可以通过时钟控制块把它的时钟关掉,从而降低动态功耗。在Quartus的PowerPlay Power Analyzer中,这种时钟门控会直接反映在功耗估算结果里。

时钟使能与时钟分频的正确用法

很多初学者习惯用计数器分频产生低频时钟,例如:

reg [3:0] cnt;
always @(posedge clk_100m) begin
    cnt <= cnt + 1'b1;
end
wire clk_6m = cnt[3];  // 错误:用普通逻辑当全局时钟

这种写法虽然功能上能跑,但clk_6m会走普通布线资源,偏斜大、抖动大,而且Quartus会给出时序警告。正确的做法有两种:一是用PLL输出低频时钟,二是用时钟使能(Clock Enable)代替分频时钟。时钟使能的写法是:

reg [3:0] cnt;
always @(posedge clk_100m) begin
    cnt <= cnt + 1'b1;
end
wire ce_6m = (cnt == 4'd15);  // 每16个周期使能一次
always @(posedge clk_100m) begin
    if (ce_6m) begin
        // 低速逻辑在这里执行
    end
end

这样整个设计仍然只有一个时钟clk_100m,所有触发器都在同一个全局时钟网络上,时序分析只需要针对一个时钟进行,既简单又可靠。时钟使能的本质是“让触发器在特定周期才更新”,而不是“换一个时钟”。在多时钟系统中,优先使用时钟使能而不是分频时钟,是降低时序收敛难度的关键策略。

时钟资源分配的实际检查方法

在Quartus中完成编译后,可以通过以下方式确认时钟资源是否被正确使用:打开Compilation Report,查看Fitter → Resource Section → Clock Signals,这里会列出每个时钟信号被分配到了哪个时钟网络、是全局还是区域、扇出是多少。如果看到某个时钟被标记为“Non-global”,就需要检查它是否来自普通逻辑或普通引脚。另一个有用的报告是Timing Analyzer → Clock Domains,它会列出所有时钟域及其频率、偏斜和不确定性。对于多时钟设计,建议在SDC文件中为每个时钟显式创建时钟约束,例如:

create_clock -name clk_100m -period 10.000 [get_ports clk_in]
create_clock -name clk_25m  -period 40.000 [get_ports {pll_c1}]
derive_pll_clocks
derive_clock_uncertainty

这样Timing Analyzer才能正确计算跨时钟域路径的建立/保持关系。如果某个时钟没有约束,Quartus会把它当作默认时钟处理,时序报告就失去了参考价值。

实践建议:在多时钟设计中,尽量让每个时钟域都来自PLL或专用时钟引脚,避免用计数器分频产生时钟。如果确实需要低频节拍,优先使用时钟使能。每个时钟域都应在SDC中显式约束,并在Compilation Report中确认它被分配到了全局或区域时钟网络。

时钟分频资源的取舍

PLL的分频输出和计数器分频有本质区别。PLL输出经过专用时钟网络,偏斜和抖动都经过优化;计数器分频输出走普通布线,偏斜不可控。因此,当分频比是整数且频率较低时,可以用PLL的c1、c2输出;当分频比需要动态改变或分频比很大时,可以用时钟使能配合计数器实现。还有一种情况是“分频后仍然需要作为时钟使用”,例如某些低速串行接口的采样时钟,这时应该用PLL输出而不是计数器分频。

总结来说,全局时钟网络负责全芯片同步,区域时钟网络负责局部低功耗,时钟控制块负责时钟源的选择和门控,时钟使能负责在不换时钟的前提下降低逻辑翻转率,PLL分频负责产生高质量的低频时钟。把这四者用对,多时钟系统的时序收敛就会顺利很多。