电子说
先说为什么要做clock switching,在现代的SoC设计中,很多时候我们设计的模块并不是要跑在一个固定的频率下,而是根据性能和功耗的要求要跑在不同的时钟频率下,当需要降低功耗以及不需要很高性能的时候,我们可以降低时钟频率,将驱动这个模块的时钟换成一个慢速时钟。通常时钟控制模块会产生不同频率的时钟,可能来自不同的PLL,也可能是来自不同的时钟分频器,然后有选择逻辑来选择让哪一个时钟驱动电路。
那么针对这个,面试官可能直接问你如何做时钟切换。如果你对于时钟切换完全没有概念,你脑子里可能想到的是一个MUX结构,如下图所示
看起来解决了问题,当SEL为0的时候,CLK0被上面的AND门给block住,当SEL为1的时候,CLK1被下面的AND门给block住。但是问题会出在SEL变化的时候,AND的门变化是立刻的,当SEL恰好在CLK0为高的时候从1变为0,或者恰好在CLK1为高的时候从0变为1,那么输出的CLK_OUT就会产生一个毛刺glitch。
(电路中产生了glitch为什么有害?)
那么接下来我们就要思考如何避免毛刺的产生。一个思路就是,我们要先把当前正选中的时钟完全停下来,然后再切换成我们想要的目标时钟。
然后问题就变成了,我们在什么时候可以把当前时钟停下来且无毛刺,以及我们在什么时候可以让目标时钟通过且无毛刺。
关于什么时候让时钟停下来且无毛刺,我们回想一下我们在设计clock gating里也遇到过类似的问题,解决的办法是利用一个负沿触发的latch,使得真正的gating发生在clock为低的时候。这里我们也要利用这个想法,即在clock为低的时候停住clock。相应的,对于目标clock,我们也在它为低的时候打开,这样下一个正沿就可以完整的输出。
利用clock gating的设计,这样我们就可以得到下面一个电路:
上面的两个flop注意都是负沿触发的,也就是说它们的Q会在CLK1和CLK2的下降沿到来之后才发生变化,这样就可以保证CLK1/2为高的半个周期完整的输出,同时,切换为另外一路的时候由于Flop之前的AND门,也保证了即使SELECT立刻变化,AND门的输出也是在另外一路停下来之后才能发生变化,这样就保证了先停一路,再切另外一路。
看起来问题解决了,但是上面的电路有什么问题呢?如果你看过老李之前的跨时钟域系列文章,应该很迅速地发现,这里面有个CDC的问题。请问,SELECT信号到底是同步于CLK1还是CLK2呢?甚至有没有可能SELECT信号是异步于CLK1和CLK2呢?我们说,如果CLK1和CLK2为两个异步的CLK,那么SELECT至多和其中之一同步,和另外一路必然异步。既然是一个异步信号,那么直接去寄存器的D端就会产生CDC的问题,flop可能会产生亚稳态!当flop出现亚稳态的时候,输出CLK_OUT当然也不是干净稳定的,造成的恶果不亚于毛刺!
好,那么要怎么解决问题亚稳态的问题?很简单加synchronizer,也就是要在flop前面再加一级flop,来达到利用2flop synchronizer来synchronize SELECT的目的。电路变成下面
这样我们就解决了metastable的问题。一般来说,面试的时候你能把上面这个电路图画出来,并且解释清楚里面每一个门的作用,以及这样设计的思路,基本就达到了面试官的考察要求。
当然老李给大家分析面试题通常都不会止步于此,上面的这个电路结构大家在任何地方一搜索就可以得到。老李下面带来时钟切换中更加进阶的干货内容。
我们再来仔细思考上面的电路,有几个点需要注意
下面老李可能会问,如果我不希望你用负沿触发的flop,因为我的工艺库里面没有,那么你要怎么设计?
思路就是用一个clock gating cell来替换掉上面的negedge flop和AND 门,clock gating我们说过打开时钟和关闭时钟都是无毛刺的。所以你可能会想到下面的电路
但是很不幸,上面的电路看起来完美无缺,但是其实并不能达到无毛刺的时钟切换。往下看老李的提示之前,请你自己思考一下为什么?和上面的negedge flop + AND有什么区别呢?
其实原因就是en0变为0的时候CLK_OUT并不是立刻停住的!这是clock gating cell的特征。那么当你把en0b传给下一路,下面CLK1可能会在CLK0没有完全停下的时候就打开了,还是会产生glitch。你可能会说,en0b到en1不还得经过一段时间吗?en1变为1到ICG打开不也得要时间吗?你期望CLK0利用这段时间完全关断。但是这并不一定,因为你并不知道CLK0和CLK1的频率关系,如果CLK1比CLK0快很多,下面的synchronizer delay可能并不够。我们要设计的是一个能够在clk0和clk1在任意频率下都可以工作的电路。
所以改进的设计是要在en0之后再加一级flop,将延一拍之后的en0b_dly反馈给另外一路,这样才能保证在当前路完全关断的情况下切换。
再扩展问一个问题,上面我们设计的都是两路clock之间进行切换,如果要求你设计一个多路时钟切换的电路,甚至要求时钟的路数是参数化的,你要怎么设计呢?
关于多路时钟切换,第一个要考虑的问题是,如何设计SEL?是用binary来编码SEL还是别的方式?这里推荐用独热码onehot来编码SEL,因为任意时刻只会选中其中一路,天然就和onehot的编码性质相同,也就是N路clock,那么SEL就是N位,每一位对应一路。利用onehot encoding的还有一个好处是参数化方便,如果利用binary,那么可能会遇到不合法的输入,比如只有3路clock,但是SEL=2‘b11。
另外一个要考虑的问题是最后的那个OR门的实现,当我们只有2路,3路clock需要切换的时候,库里面有2输入的普通OR门,但是并不推荐大家直接用。原因在于,由于最终我们输出的是一个时钟信号,那么我们通常要求时钟信号的transition是非常干净的,而且上升沿和下降沿是平衡的,即tr(rising delay)和tf(falling delay)是几乎差不多的,而普通的2输入OR门并不保证它们tr和tf相等,而且是与2输入中连哪一个输入pin有关。为了解决这个问题,工艺库厂家一般会提供一些特殊的CELL,进行了专门的设计,可以使得tr和tf几乎相等,并且两个输入pin的延时也是一样的,并不区分,所以我们要例化那些特殊的专门给clock path上用的cell。但是当输入变成了多路clock,比如4路,5路,甚至更多路比如8路,16路的时候,厂家也不会提供balanced 多输入clock OR cell。这个时候我们就要专门利用NAND tree来实现最后的OR逻辑,并且要保证每一路所需要的NAND cell delay是一样的(思考题:为什么要balance每一路?)举个例子一个利用NAND tree来达到2input OR的结构如下
最后再提一个很多人都会忽视的问题,请问大家,在上面设计的无毛刺时钟切换电路,对于其中synchronizer flop,以及clock gating cell,你有没有考虑reset信号的影响? 如果你的reset信号是异步reset,那么当你reset active的时候,clock会立刻被切断,那么是不是也会产生glitch?
这个问题的解决思路有两个,一是假设你在reset状态下选中的是clock0,那么需要你在assert reset之前要先切换到clock0;如果不是,比如reset assert的时候是clock3,那么你需要保证所有被clock3驱动的下游逻辑也都会被这个reset信号给复位。
全部0条评论
快来发表一下你的评论吧 !