大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看完后喜欢的话就请关注我吧~谢谢~
对于现在的芯片来说,其内部通常都有很多个时钟,在芯片运行过程中还可能有时钟切换的过程,那么如何安全的进行时钟切换呢?这就是我们本篇文章主要讨论的内容,关于时钟切换也是ic面试的一个重点内容了,如果你是想要入行ic行业,那么就需要好好阅读喽。
一、组合逻辑的时钟切换
关于如何进行时钟切换,如果你对该内容没有了解的话,第一反应或许是用组合逻辑进行时钟切换,即使用以下的电路结构:
对应的verilog代码为:
assign clk_out = (!sel & clk_in0) | (sel & clk_in1)
或者是这种:
assign clk_out = sel ? clk_in1 : clk_in0
以上两种组合逻辑电路,看似能解决时钟切换的问题,但是如果sel信号发生改变,那么从一个时钟切换到另外一个时钟时,其实很容易出现毛刺,即:
可见,在sel信号发生改变时,由于是纯组合逻辑,sel的变化可能发生在任何时刻,因此毛刺的产生是无法避免的。因此我们需要使用其他的方式进行无毛刺的时钟切换,即Glitch-free clock switching circuit。
二、成倍数的同步时钟的时钟切换
既然纯组合逻辑不行,我们理所当然的想到了可以使用和时钟相关的触发器或者是锁存器来进行时钟切换,既然从一个时钟的高电平切换到另外一个时钟的低电平时,会出现毛刺,那么我们自然的会想到:只要利用时钟的下降沿来采样sel信号,并且将采样后的sel信号取反作为另外一个时钟的使能信号,就可以确保切换到另外一个时钟时,前一个时钟的电平不会是高电平。有以下电路可以实现这个目标:
我们对上图的电路结构进行说明:
首先上图使用的两个触发器都是下降沿触发。
当sel信号为0时:
此时选择的时钟为clk_in0,当clk_in0的下降沿采样后,dff0 的Q端输出1,QN端输出0,并反馈至dff1,使dff1Q端输出始终为0,从而不会在与门处产生毛刺。且dff1QN端始终为1,从而不会影响dff0的输入。此时输出的clk_out为clk_in0。
- 当sel信号切换为1时:
- 只有当clk_in0的下降沿来临后,dff0的Q端输出才会是0,其QN端输出才会是1,随后dff1的D端的输入才会是1,此时clk_out保持为低电平。
- 当clk_in1的下降沿来临后,其dff1的Q端输出变为1,QN为0,但由于此时clk_in1为低电平,因此clk_out仍然为0。
- 当clk_in1切换到高电平后,clk_out才会切换到高电平。
时序图如下图所示:
可见一个时钟域下的触发器的QN端反馈能保证其时钟被取消选择后,输出传播另一个时钟,从而避免产生任何毛刺。
但是以下路径也需要满足一定的时序约束关系:sel控制信号到dff0或dff1的D端,dff0的QN端输出到dff1的D端,dff1的QN端输出到dff0的D端。如果这三条路径中的任何一条路径上的信号与目标触发器的捕获边缘同时发生变化,则该寄存器的输出很可能变为亚稳态。因此这两个时钟之间需要有明确的关系,即是同步时钟,且频率成倍数关系,就能够始终满足时序约束关系,不会出现毛刺和亚稳态。
三、异步时钟的时钟切换
那么,我们该如何针对异步时钟进行时钟切换呢?如果直接使用上面的电路,那么由于不确定的时钟关系,很可能会出现亚稳态,因此一个合理的思路就是再加一级寄存器以消除亚稳态,确保负沿触发器采样的信号是稳定的信号,如下面的电路所示:
如上图所示,我们再原先的电路之前再添加了一级正边沿敏感的触发器。它们的作用如下面的时序图所示:
描述一下时序图:
- sel为0时:
此时dff1的Q端输出为1,QN端输出为0,并反馈至dff2和dff3,使他们的Q端输出始终为0,QN端始终为1。此时输出的clk_out为clk_in0。
sel切换为1时:
当clk_in0的上升沿来临后,dff0的Q端才变为0;
等到clk_in0的下降沿来临后,dff1的Q端才变为0,同时QN端输出为1,随后clk_in1的路径中dff2的D端输入才为1.
当clk_in1的上升沿来临后,dff2的Q端输出变为1.
随后clk_in1的下降沿来临后,dff3的Q端才为1,且clk_out切换至clk_in1。
通过上面的描述我们可以发现:通过再负沿敏感的触发器前添加了两个正沿触发器,使得:
sel控制信号到负沿敏感触发器dff1或dff3的D端;
负沿触发器dff1的QN端输出到负沿触发器dff3的D端;
负沿触发器dff3的QN端输出到负沿触发器dff1的D端。
这3条路径上的输入信号和负沿触发器的捕获时钟沿之间都有了一定的缓冲时间,从而可以避免亚稳态的产生。也就是使用两级触发器防止跨时钟域的亚稳态传递和毛刺信号出现。
上述电路的verilog代码如下图所示:
module clock_glitch_free (
input clk_in0,
input clk_in1,
input sel,
input rst_n,
output clk_out
);
reg dff0_q, dff1_q;
wire dff1_qn;
reg dff2_q, dff3_q;
wire dff3_qn;
//第一级触发器用上升沿采样 选择信号与反馈信号进行与操作
always@(posedge clk_in1 or negedge rst_n) begin
if(!rst_n) begin
dff2_q <= 0;
end else begin
dff2_q <= sel && dff1_qn;
end
end
//第二级触发器用下降沿采样
always@(negedge clk_in1 or negedge rst_n) begin
if(!rst_n) begin
dff3_q <= 0;
end else begin
dff3_q <= dff2_q;
end
end
assign dff3_qn = ~dff3_q;
//第一级触发器用上升沿采样 选择信号与反馈信号取反后进行与操作
always@(posedge clk_in0 or negedge rst_n) begin
if(!rst_n) begin
dff0_q <= 0;
end else begin
dff0_q <= ~sel && dff3_qn;
end
end
//第二级触发器用下降沿采样
always@(negedge clk_in0 or negedge rst_n) begin
if(!rst_n) begin
dff1_q <= 0;
end else begin
dff1_q <= dff0_q;
end
end
assign dff1_qn = ~dff1_q;
assign clk_out = clk_in0 & dff1_q | clk_in1 & dff3_q;
endmodule
四、总结
本文探讨了如何实现无毛刺的时钟切换。对于成倍数的同步时钟,我们可以使用基于下降沿触发器的电路结构;而对于异步时钟,则需要添加额外的触发器来消除亚稳态和毛刺。
如果你喜欢这篇文章的话,请关注我的公众号-熊熊的ic车间,里面还有ic设计和ic验证的学习资料和书籍等着你呢~欢迎您的关注!