无毛刺的时钟切换
无毛刺时钟切换要点:
常规组合逻辑电路:电平相反时切换必出现毛刺
相关时钟切换:下降沿切换(反馈实现),下个上升沿切换生效,要会画电路图
无关时钟切换:下降沿触发器之前有个上升沿触发器,消除亚稳态,要会画电路图
前言:在设计多时钟系统中,需要切换时钟源,这两个时钟可能是没有关联的(相位、频率),或者他们为倍数关系。这两种情况都有可能在开关时产生毛刺(glitch),而系统上的毛刺对系统来说是危险的,他可能能够被一些寄存器捕获为触发边沿,而其他寄存器忽略此毛刺。
无毛刺的时钟切:Glitch-free clock switching circuit,不要混淆了Clock Domain Conversion为跨时钟域传输问题,就是最常考也是经常听到的CDC问题。
参考文章:李锐博恩
正确的无毛刺时钟切换效果就像:
clkout在select的控制下在clk0和clk1之间无毛刺切换.
那么如何实现这种完美切换呢?
先从最容易想到的设计说起:
可以使用纯组合逻辑来设计时钟切换电路,但是这种设计不可避免的产生毛刺,纯组合逻辑为电平触发,无法规避毛刺:
那么如何通过电路的设计来改善呢?
可以通过clk0和clk1之间有无特定的关系分为两类分别讨论:
相关时钟源的时钟切换电路:
我们可以用当前时钟的下降沿来采样(sel与反馈的输出相与的结果),可以简单地理解为采样sel信号。具体的设计如下:
sel为0时,选择输出clk0,如上图,先对sel在时钟域clk0进行下降沿采样,得到sel_clk0,取反得到sel_clk0_n,这个信号与时钟clk0相与输出得到clkout0;
sel_clk0反馈到时钟域clk1的输入端,与sel相与之后,再经过clk1下降沿采样得到sel_clk1,这个信号与clk1想与得到输出clkout1;
clkout0与clkout1进行或运算得到输出clkout,这个时钟便是进行时钟切换后的时钟输出。
这样经过下降沿采样以及反馈就可以得到无毛刺的相关时钟切换电路。
上述电路的缺点 没有解决异步的问题,有可能会出现亚稳态问题。注意:电平相反的时候切换时钟,肯定有毛刺;电平相同的时候,有可能产生毛刺。这里我们关注低电平上产生的毛刺,同为高电平的时候切换,有可能产生毛刺,影响了上升沿,因此我们就选择在两者都是低电平的时候进行切换。这里“影响上升沿”的意思是:两个时钟信号同为高电平的时候,如果进行时钟切换,那么刚切换完之后产生的时钟信号的高电平脉冲宽度不确定,有可能产生毛刺(也就是高电平脉冲宽度比较小小的情况);而触发器对时钟的最小脉冲宽度是有要求的,当切换时钟前来了一个时钟上升沿,然后进行切换后的时钟信号产生了毛刺,触发器的时钟端就可能不认之前来的那个上升沿,因此这里说的影响了上升沿是指影响了切换前的时钟上升沿。
对于无关时钟源的无毛刺时钟切换电路设计:
sel为0时候,先取反得到seln,然后clk0时钟下上升沿采样seln_clk0_r1,之后下降沿采样得到seln_clk0_r2,这个信号与clk0相与得到clkout0;
seln_clk0_r2n反馈到时钟域clk1,与clk1相与,用clk1上升沿采样得到sel_clk1_r1,再用clk1下降沿采样得到sel_clk1_r2,这个信号与clk1相与得到输出clkout1;
最终的输出为clkout0与clkout1的或,即输出clkout为无毛刺的时钟切换波形。
无关时钟源的时钟切换电路设计:
设计的Verilog描述:
module glitch_free (
input clk0, // Clock
input clk1,
input select,
input rst_n, // Asynchronous reset active low
output clkout
);
wire mid_clk0;
reg mid_clk0_r1, mid_clk0_r2, mid_clk0_r2n;
wire mid_clk1;
reg mid_clk1_r1, mid_clk1_r2, mid_clk1_r2n;
assign mid_clk1 = select & mid_clk0_r2n;
assign mid_clk0 = ~select & mid_clk1_r2n;
//第一级触发器用上升沿采样,选择信号与反馈信号的与运算
always@(posedge clk1 or negedge rst_n) begin
if(~rst_n) mid_clk1_r1 <= 0;
else mid_clk1_r1 <= mid_clk1;
end
//第二级触发器用下降沿采样
always@(negedge clk1 or negedge rst_n) begin
if(~rst_n) begin
mid_clk1_r2 <= 0;
mid_clk1_r2n <= 1;
end
else begin
mid_clk1_r2 <= mid_clk1_r1;
mid_clk1_r2n <= ~mid_clk1_r1;
end
end
//第一级触发器用上升沿采样,选择信号与反馈信号的与运算
always@(posedge clk0 or negedge rst_n) begin
if(~rst_n) mid_clk0_r1 <= 0;
else mid_clk0_r1 <= mid_clk0;
end
//第二级触发器用下降沿采样
always@(negedge clk0 or negedge rst_n) begin
if(~rst_n) begin
mid_clk0_r2 <= 0;
mid_clk0_r2n <= 1;
end
else begin
mid_clk0_r2 <= mid_clk0_r1;
mid_clk0_r2n <= ~mid_clk0_r1;
end
end
wire mid_clk11, mid_clk00;
assign mid_clk11 = clk1 & mid_clk1_r2;
assign mid_clk00 = clk0 & mid_clk0_r2;
assign clkout = mid_clk11 | mid_clk00;
endmodule
Testbench文件:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
module test_tb(
);
reg clk0, clk1;
reg select;
wire clkout;
initial begin
clk0 = 0;
forever
#2 clk0 = ~clk0;
end
initial begin
clk1 = 0;
forever
#5 clk1 = ~clk1;
end
reg rst_n;
reg in;
initial begin
select = 0;
rst_n = 0;
#5
rst_n = 1;
#30
select = 1;
#40
select = 0;
end
glitch_free inst_glitch_free (.clk0(clk0), .clk1(clk1), .select(select), .rst_n(rst_n), .clkout(clkout));
endmodule
仿真电路和波形:
哈姆雷特,请保持前行!