相关阅读数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482在数字系统设计中有时需要在系统运行过程中对系统时钟进行切换例如在不同工作模式下切换主时钟源、在功能时钟与测试时钟之间切换或者在低功耗场景中切换到较低频率时钟。从直观上看最简单的做法是使用一个多路选择器(MUX)在两个时钟之间进行选择例如下面这段代码所示module clock_switch(input clk_1, clk_2, select, output reg clk_out); always(*) begin if(select 1b1) clk_out clk_1; else clk_out clk_2; end endmodule这种写法从功能上看似乎没有问题但实际上会带来一个非常严重的隐患毛刺(glitch)。原因在于select信号很可能在两个时钟都未处于安全切换窗口时发生变化从而使输出时钟在切换瞬间出现异常窄脉冲。对于普通数据信号来说偶发毛刺有时尚可容忍但对时钟信号而言毛刺往往是致命的因为它可能被后级触发器误认为有效时钟沿进而导致寄存器误触发甚至引发亚稳态。由于时钟是整个系统的核心控制信号一旦这里出现问题就可能影响整个系统的正常运行严重时甚至导致系统宕机。一个简单的基于数据选择器的时钟切换电路如图1所示其切换时产生毛刺的波形如图2所示。图1 简单的数据选择器图2 有毛刺的波形一种不会产生毛刺的时钟切换电路如下图3所示。该电路的输出为两个时钟门控输出的与上半部分电路控制时钟clk_A当门控信号a2i_2为1时门控关闭时钟clk_A当门控信号a2i_2为0时门控打开时钟clk_A。下半部分电路控制时钟clk_B当门控信号a4i_2为1时门控关闭时钟clk_B当门控信号a4i_2为0时门控打开时钟clk_B。控制信号sel用于选择clk_A还是clk_B当sel为0时a3o输出0由于sel和a3i_2信号都不是clk_B时钟域的信号因此这个输出需要经过clk_B时钟域的两级同步器得到a3o_sync信号最后a3o_sync信号通过clk_B的下降沿采样得到a4i_2和!a4i_2a4i_2用于关闭clk_B而!a4i_2用于拉高a1o从而最后将a2i_2拉高即打开时钟clk_A注意到对于a1o即使sel为0a1i_1为1也不会立刻拉高因为!a4i_2仍然为0对sel为1的分析与上面类似在此不详述。可以看到这种结构在时钟切换的过程中首先关闭正在运行的时钟此时没有时钟输出输出恒为0然后再开启另一个时钟且这个关闭和开启的动作都是由本时钟所同步的行为即clk_A负责关闭和开启clk_Aclk_B负责关闭和开启clk_B这样就在一定程度上避免了毛刺的产生。S3和S6这两个触发器需要下降沿触发这是为了在关闭和打开时钟时不产生毛刺因为寄存器S3和S6的输出有一定延迟。如果使用上升沿触发此时时钟信号为高电平但门控信号a2i_2和a4i_2需要延迟一段时间才会拉高或拉低此时会在a20和a4o产生毛刺下降沿触发则不会有这个问题因为时钟信号为低这保证了a20和a4o一定为低如图4和图5所示。但值得注意的是这在无形中对时钟的占空比提出了要求即占空比不能太高最好为50%左右否则还是会导致输出出现毛刺在S3S6延迟较大时。图中的B2、B3、B4、B5实际综合后可能不存在因为有专门的下降沿触发的寄存器同时寄存器也有取反输出端。图3 无毛刺的时钟切换电路图4 使用上升沿触发出现毛刺图5 使用下升沿触发不出现毛刺图6给出了无毛刺时钟切换电路的波形。从图中可以看出这种切换方式确实会引入一定延迟也就是说输出不会在select信号变化的瞬间立刻切换到新时钟而是要经历一个先关闭、后开启的过程。图6 没有毛刺的波形下面给出上述无毛刺时钟切换电路的一种Verilog描述。这里尤其需要注意的是S3和S6对应的寄存器使用的是下降沿触发。module clock_switch(input clk_1, clk_2, select, rst_n1, rst_n2, output clk_out); //上半部分时钟控制的逻辑 wire a1i_1, a1o, a2o; reg a1o_r, a1o_syn, a2i_2, a4i_2; assign a1i_1 !select; assign a1o a1i_1 !a4i_2; always(posedge clk_1 or negedge rst_n1)begin //打两拍同步 if(!rst_n1)begin a1o_r 0; a1o_syn 0; end else begin a1o_r a1o; a1o_syn a1o_r; end end always(negedge clk_1 or negedge rst_n1)begin //注意这里用下降沿触发 if(!rst_n1) a2i_2 0; else a2i_2 a1o_syn; end assign a2o a2i_2 clk_1; //下半部分时钟控制的逻辑 wire a3o, a4o; reg a3o_r, a3o_syn; assign a3o select !a2i_2; always(posedge clk_2, negedge rst_n2)begin //打两拍同步 if(!rst_n2)begin a3o_r 0; a3o_syn 0; end else begin a3o_r a3o; a3o_syn a3o_r; end end always(negedge clk_2 or negedge rst_n2)begin //注意这里用下降沿触发 if(!rst_n2) a4i_2 0; else a4i_2 a3o_syn; end assign a4o a4i_2 clk_2; //输出的与门逻辑 assign clk_out a2o | a4o; endmodule文中图3来源于《数字IC设计入门》