返回列表 发帖

[原创] Verilog基本电路设计之二(时钟无缝切换)

[原创] Verilog基本电路设计之二(时钟无缝切换)

时钟切换分成两种方式,普通切换和去毛刺无缝切换。

普通切换,就是不关心切出的时钟是否存在毛刺,这种方式电路成本小。如果时钟切换时,使用此时钟的模块电路处于非工作状态,或者模块内电路被全局复位信号reset住的,即使切出毛刺也不会导致DFF误触发,这样的模块可以选择用此种切换方式。
写法很简单 assign clk_o = sel_clkb ? clkb : clka ,当sel_clkb为1时选择clkb,否则选择clka。不过在实际设计中,建议直接调用库里的MUX单元并set_dont_touch,不要采用这里的assign写法,因为这种写法最后综合得到的可能不是MUX而是复杂组合逻辑,给前后端流程的时钟约束和分析带来不便。


无缝切换,就是切换时无毛刺时钟平稳过渡。在时钟切换中,只要出现比clka或者clkb频率更高的窄脉冲,不论是窄的高电平还是窄的低电平,都叫时钟毛刺。工作在切换后时钟clk_o下的电路模块,综合约束是在max{clka,clkb}频率下的,也就是说设计最后signoff的时候,只保证电路可以稳定工作的最高频率是max{clka,clkb},如果切换中出现更高频的时钟毛刺,电路可能出现无法预知的结果而出错。无缝切换,一般用在处于工作状态的模块需要调频或者切换时钟源,比如内部系统总线,CPU等。你刚用手机打完游戏后马上关屏听音乐,这两种场景中,CPU在满足性能前提下为了控制功耗,其工作频率会动态地从很高调至较低,此时就可能是在CPU一直处于工作状态下,通过无缝切换时钟源头实现的。

在无缝切换电路中,切换信号sel_clkb可以是任意时钟域下的信号,包括但不限于clka或者clkb域,但是sel_clkb必须是一个DFF输出信号;clka与clkb的频率大小相位关系可以任意。无缝切换需要解决两个问题,一是异步切换信号的跨时钟域同步问题,这里需要使用《Verilog基本电路设计之一》里的同步电路原理消除亚稳态;二是同步好了的切换信号与时钟信号如何做逻辑,才能实现无毛刺。

下面写出无缝切换电路的主体部分,忽略了内部信号的定义声明等。

module clk_switch (
                rst_n          , //
                clka            , //
                clkb            , //
                sel_clkb      , //
                clk_o            //
                );

//assign clka_n = ~clka;
//assign clkb_n = ~clkb;

// part1
//always @ (posedge clka_n or negedge rst_n)
always @ (posedge clka or negedge rst_n)
begin
    if (!rst_n) begin
        sel_clka_d0 <= 1'b0;
        sel_clka_d1 <= 1'b0;
    end
    else begin
        sel_clka_d0 <= (~sel_clkb) & (~sel_clkb_dly3) ;
        sel_clka_d1 <= sel_clka_d0 ;
    end
end

// part2
//always @ (posedge clka_n or negedge rst_n)
always @ (posedge clka or negedge rst_n)
begin
    if (!rst_n) begin
        sel_clka_dly1 <= 1'b0;
        sel_clka_dly2 <= 1'b0;
        sel_clka_dly3 <= 1'b0;
    end
    else begin
        sel_clka_dly1 <= sel_clka_d1;
        sel_clka_dly2 <= sel_clka_dly1 ;
        sel_clka_dly3 <= sel_clka_dly2 ;
    end
end

// part3
//always @ (posedge clkb_n or negedge rst_n)
always @ (posedge clkb or negedge rst_n)
begin
    if (!rst_n) begin
        sel_clkb_d0 <= 1'b0;
        sel_clkb_d1 <= 1'b0;
    end
    else begin
        sel_clkb_d0 <= sel_clkb & (~sel_clka_dly3) ;
        sel_clkb_d1 <= sel_clkb_d0 ;
    end
end

// part4
//always @ (posedge clkb_n or negedge rst_n)
always @ (posedge clkb or negedge rst_n)
begin
    if (!rst_n) begin
        sel_clkb_dly1 <= 1'b0;
        sel_clkb_dly2 <= 1'b0;
        sel_clkb_dly3 <= 1'b0;
    end
    else begin
        sel_clkb_dly1 <= sel_clkb_d1   ;
        sel_clkb_dly2 <= sel_clkb_dly1 ;
        sel_clkb_dly3 <= sel_clkb_dly2 ;
    end
end

// part5
clk_gate_xxx clk_gate_a ( .CP(clka), .EN(sel_clka_dly1), .Q(clka_g)  .TE(1'b0) );
clk_gate_xxx clk_gate_b ( .CP(clkb), .EN(sel_clkb_dly1), .Q(clkb_g)  .TE(1'b0) );
//assign clka_g = clka & sel_clka_dly1 ;
//assign clkb_g = clkb & sel_clkb_dly1 ;
assign clk_o = clka_g | clkb_g ;

endmodule


上面是我认为比较合理的无缝切换电路,其他切换方式跟这个会有些许出入,但基本大同小异原理是一样的。有几点说明:
1、抛开注释掉的电路不看,由于part5部分直接调用库里的clock gating cell,使得整个切换电路全部只需要用到时钟上升沿,无需额外定义反向时钟,精简了DC综合的时钟约束;直接调用gating cell的 另一个好处是,前后端工具会自动检查gating cell的CP信号与EN信号的setup/hold时间,使得gating后的Q时钟输出无毛刺尖峰。TE端可以根据实际需要接上scan测试模式信号。如果使用part5部分的gating cell实现,前面的part1,2,3,4全部替换成注释掉的反相时钟也是没有问题。

2、part2和part4部分,具体需要多少级DFF,甚至完全不要也是可以的,这就回到了《Verilog基本电路设计之一》里讨论的到底多少级DFF消除亚稳态才算合理的问题。时钟频率很低可能无所谓,如果时钟频率达到GHz,这部分建议至少保留三级DFF,因为三级DFF延时也仅仅只有3ns的时间裕度。没必要为了省这么几个DFF降低电路可靠性,在复杂IP以及大型SOC系统中,你会发现多几十个DFF,面积上可以忽略,系统可靠性和稳定性才是首要的。

3、如果part5部分希望使用注释掉的两行“与”逻辑实现时钟gating,此时part1与part3使用正相或者反相时钟都可以,但是必须把part2和part4部分改为注释掉的反相时钟实现,目的是初步从RTL设计上避免“与”逻辑的毛刺,同时还需要后端配合,因为很多后端工具对时钟“与”逻辑的clock gating check未必会检查。用clk下降沿拍出的en信号,再跟clk做与逻辑得到的门控时钟,在RTL仿真阶段看到的一定不会有毛刺,但是布线完成后,如果clk相对en后移,那与逻辑得到的门控时钟就有毛刺了。这就是用与逻辑做门控的缺点,由于后端工具可能不会去检查这个与门的时序关系而导致出错。但直接调用库里的gating cell,工具天然就会去检查这个时序,免去人工确认的后顾之忧。

最后,请大家仔细看看sel_clka_d0 <= (~sel_clkb) & (~sel_clkb_dly3)  和sel_clkb_d0 <= sel_clkb & (~sel_clka_dly3) 这两处逻辑,按理说,sel_clkb跟sel_clka_dly3以及sel_clkb_dly3之间相互都是异步的,而按照异步信号同步处理原则,两个不同时钟域下的信号是不允许直接做组合逻辑的,为什么这里可以这样使用?


写得很好!
对于最后那个问题,我的理解是:因为后级是对这个与门的输出进行同步,所以是允许的。
而且这里必须进行这样的与操作,保证在一个时钟输出关闭后才开启另一个时钟输出,否则在时钟频率相差较大时,两路时钟可能都输出了。

TOP

good
关于最后的问题:因为(~sel_clkb_dly3)或者 (~sel_clka_dly3)最后都是会保持为1的,也就是说他们的0/1变换仅发生时钟切换点上,而这个切换点又经过目标时钟的同步(以及处理亚稳态的过程),所以最后的选通信号不会有问题

TOP

TOP

TOP

问一个问题,part1和part3中的组合逻辑 (~sel_clkb) & (~sel_clkb_dly3)和sel_clkb & (~sel_clka_dly3) 已经做同步处理了,为啥还有经过part2和part4。
海阔天空,漠大无边

TOP

回复 6# haimo

这个问题请看后面的第2点注解,连同最后面抛出的那个问题一起思考,理解清楚了,你就可以根据不同的时钟应用条件对于part2以及part4部分游刃有余地做灵活增减处理。

TOP

回复 7# 杰克淡定

就是说是也是可以完全不要的,理由就是我说的已经在part1和part3做过同步了。
海阔天空,漠大无边

TOP

回复 2# 老阮

嗯,你从时钟切换中需要先关clk1再开clk2角度,回答了要做这个与逻辑的原因。我问的侧重点在于,为什么这里能够把异步时钟域的组合逻辑信号后再往另一个时钟域去同步,在上一篇帖子里特别指出过,被两级同步过去的异步信号输入,必须是另一个时钟域的DFF信号,不能是组合逻辑信号。对于c=a&b的逻辑,如果信号a来自clk1域,信号b来自clk2域,信号c又将在clk3域被使用,从常规设计规则里是不允许的,原因在于组合逻辑会产生毛刺,更何况这里是两个异步时钟域下的信号在做与逻辑,而毛刺信号却可能被另一个异步时钟clk3抓到。


你楼下那位兄弟基本回答了同步这个组合逻辑信号过去不会存在问题的原因,重点就是在于设计中保证a和b信号不会同时变化,得到的c不会有毛刺,我后面再详细回复。

TOP

好东西啊 好东西啊

TOP

返回列表

站长推荐 关闭


欢迎访问 TI SLL(信号链)专区

欢迎访问 TI SLL(信号链)专区


查看