FPGA时钟无毛刺切换电路

时钟无毛刺切换电路

(文字由AI润色完成)

本文旨在对几种经典的时钟切换电路进行梳理和总结。

具体的时钟切换时序图可以参考网上各大博客。

有毛刺的切换电路

下图是一个由基本门电路构成的时钟选择器。其本质就是一个组合逻辑的多路选择器(MUX)。

问题所在:选择信号 sel 相对于 clk0clk1 都是异步的。如果 sel 的跳变发生在任一时钟的高电平期间,输出端 clk_out 就可能产生一个非常窄的脉冲,即“毛刺”。这个毛刺会被下游逻辑误采,引发功能错误。

无毛刺时钟切换电路

为了规避上面的异步信号带来的影响,需要加入同步电路,即打2拍把异步信号同步到本地时钟域。

实现无毛刺切换的核心原则是 “先停后启”:即先关闭当前输出的时钟,再开启新的时钟,确保切换过程中不会产生不完整的时钟脉冲。

相关时钟

当两个时钟源 clk0clk1 存在明确的频率或相位关系(如倍频、分频)时,可以采用如下相对简单的设计。

这里本质上sel信号相对于clk0和clk1仍然是异步信号,但由于这里时钟存在相关性,对异步sel信号进行一定的约束即可规避掉亚稳态问题。

非相关时钟

这是最通用、最稳健的设计,适用于两个时钟之间无任何关联的场景。

该电路的逻辑非常精妙。下面以 sel0 切换到 1 (即时钟从 clk0 切换为 clk1)为例说明:

  1. 关闭 clk0sel 变为 1 后,该信号被 clk0 域的两级触发器(FF2, FF3)同步。由于 FF3 是下降沿触发,它会在 clk0 的下降沿(即低电平)到来后,才改变其输出。FF3/Qn 变为 0,从而安全地关闭通往 clk0 的与门(AND3)。注意此时时钟输出为低电平。
  2. 开启 clk1FF3/Qn 输出的 0 会传递给 clk1 域的两级触发器(FF0, FF1)。同样,由于 FF1 是下降沿触发,它会在 clk1 的下降沿到来后,才使其输出 FF1/Q 变为 1,从而安全地打开通往 clk1 的与门(AND2)。

至此,时钟源被平稳地从 clk0 切换到了 clk1,全程无毛刺风险。

简单总结流程是:sel信号从01,经历2级clk0同步关闭clk0,经历2级clk1同步打开clk1

RTL code

代码编写还是比较容易,将电路描述出来即可。为了仿真观察波形,笔者加入了0.1个时间单位的延迟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
`timescale 1ns / 1ps

module clock_switching_no_related (
input wire clk0,
input wire clk1,
input wire rst_n,
input wire sel,
output reg clk_out
);

// under clock 0
reg sel_0_ff_0;
reg sel_0_ff_1;
// under clock 1
reg sel_1_ff_0;
reg sel_1_ff_1;

always @(posedge clk0 or negedge rst_n) begin
if (~rst_n) begin
sel_0_ff_0 <= #0.1 1;
end
else begin
sel_0_ff_0 <= #0.1 ~sel & ~sel_1_ff_1;
end
end

always @(negedge clk0 or negedge rst_n) begin
if (~rst_n) begin
sel_0_ff_1 <= #0.1 1;
end
else begin
sel_0_ff_1 <= #0.1 sel_0_ff_0;
end
end

always @(posedge clk1 or negedge rst_n) begin
if (~rst_n) begin
sel_1_ff_0 <= #0.1 0;
end
else begin
sel_1_ff_0 <= #0.1 sel & ~sel_0_ff_1;
end
end

always @(negedge clk1 or negedge rst_n) begin
if (~rst_n) begin
sel_1_ff_1 <= #0.1 0;
end
else begin
sel_1_ff_1 <= #0.1 sel_1_ff_0;
end
end

// 输出
always @(*) begin
if (~rst_n) begin
clk_out = 0;
end
else begin
clk_out = clk1 & sel_1_ff_1 | clk0 & sel_0_ff_1;
end
end
endmodule

总结

  1. 用纯组合逻辑(MUX)进行异步时钟切换,极易产生毛刺。
  2. 设计中的下降沿触发是关键,它确保了对时钟路径的使能/禁止操作都发生在时钟的稳定低电平期间。

参考



FPGA时钟无毛刺切换电路
https://blog.zorogh.top/2025/08/25/glitch-free-clock-switching/
作者
ZoroGH
发布于
2025年8月25日
更新于
2025年9月7日
许可协议