时钟切换逻辑 glitch free

时钟切换逻辑避免产生glitch的原理

先关闭当前时钟,再打开目标时钟。而不管关闭还是使能,都必须保证当前时钟或目标时钟的使能信号的跳变都分别在时钟为低电平期间进行的,防止产生时钟glitch。

这样在时钟切换时就必然要经历4个阶段:

1)选择信号改变、2)在clk1为低时停掉clk1的选择 、3)在clk2为低时打开clk2的选择端、3)正常工作,完成切换。

 

无缝切换需要解决两个问题:

一是异步切换信号的跨时钟域同步问题,这里需要使用《Verilog基本电路设计之一》里的同步电路原理消除亚稳态;

二是同步好了的切换信号与时钟信号如何做逻辑,才能实现无毛刺

第2步恐怕很多人未必能考虑到,做ASIC工程师就是要不断反复考虑自己的设计,不到大批量用上1、2年,你恐怕都要时刻的反省。这很重要,也很累。

module clk_switch (
                rst_n,
                clka,
                clkb,
                sel_clkb,
                clk_o
                );
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_dly3), .Q(clka_g)  .TE(1'b0) );
clk_gate_xxx clk_gate_b ( .CP(clkb), .EN(sel_clkb_dly3), .Q(clkb_g)  .TE(1'b0) );
//assign clka_g = clka & sel_clka_dly3 ;
//assign clkb_g = clkb & sel_clkb_dly3 ;
assign clk_o = clka_g | clkb_g ;
endmodule</span>
上面是我认为比较合理的无缝切换电路,其他切换方式跟这个会有些许出入,但基本大同小异原理是一样的。
有几点说明:
抛开注释掉的电路不看,由于part5部分直接调用库里的clock gating cell,使得整个切换电路全部只需要用到时钟上升沿,无需额外定义反向时钟,精简了DC综合的时钟约束;直接调用gating cell的 另一个好处是,前后端工具会自动检查gating cell的CP信号与EN信号的setup/hold时间,使得gating后的Q时钟输出无毛刺尖峰。TE端可以根据实际需要接上scan测试模式信号。如果使用part5部分的gating cell实现,前面的part1,2,3,4全部替换成注释掉的反相时钟也是没有问题。
part2和part4部分,具体需要多少级DFF,甚至完全不要也是可以的,这就回到了《Verilog基本电路设计之一》里讨论的到底多少级DFF消除亚稳态才算合理的问题。时钟频率很低可能无所谓,如果时钟频率达到GHz,这部分建议至少保留三级DFF,因为三级DFF延时也仅仅只有3ns的时间裕度。没必要为了省这么几个DFF降低电路可靠性,在复杂IP以及大型SOC系统中,你会发现多几十个DFF,面积上可以忽略,系统可靠性和稳定性才是首要的。
如果part5部分希望使用注释掉的两行“与”逻辑实现时钟gating,此时part1与part3使用正相或者反相时钟都可以,但是必须把part2和part4部分改为注释掉的反相时钟实现,目的是初步从RTL设计上避免“与”逻辑的毛刺,同时还需要后端配合,因为很多后端工具对时钟“与”逻辑的clock gating check未必会检查。用clk下降沿拍出的en信号,再跟clk做与逻辑得到的门控时钟,在RTL仿真阶段看到的一定不会有毛刺,但是布线完成后,如果clk相对en后移,那与逻辑得到的门控时钟就有毛刺了。这就是用与逻辑做门控的缺点,由于后端工具可能不会去检查这个与门的时序关系而导致出错。但直接调用库里的gating cell,工具天然就会去检查这个时序,免去人工确认的后顾之忧。

 

参考2

下面是收集的几种无毛刺的时钟切换电路。

http://www.cnblogs.com/xinlukk/p/6923061.html

1.  openMSP430 ipcore中的时钟切换电路     (来源:OpenCore)

`timescale  1ns/10ps

module  clock_mux (

    // OUTPUTs

    //=========

    output         clk_out,        // Clock output

 

    // INPUTs

    //=========

    input          clk_in0,        // Clock input 0

    input          clk_in1,        // Clock input 1

    input          reset,            // Reset

    input          select_in       // Clock selection

);

 

 

 

//----------------------------------------

//  Regs declare

//----------------------------------------

 

reg   dff0a,dff0b;

reg   dff1a,dff1b;

 

wire  clk_in0_inv = ~clk_in0;

wire  clk_in1_inv = ~clk_in1;

 

//----------------------------------------

//  clk_in0 path

//----------------------------------------

 

// negedge of clk_in0

always @(posedge clk_in0_inv or posedge reset)

  if(reset)   dff0a <=  1'b1;

  else      dff0a <=  !select_in & !dff1b;

 

always @(posedge clk_in0 or posedge reset)

  if(reset)   dff0b <=  1'b1;

  else      dff0b <=  dff0a;

 

wire  clk_in0_gate  = ~(~clk_in0 & dff0b);

 

//----------------------------------------

//  clk_in1 path

//----------------------------------------

// negedge of clk_in1

always @(posedge clk_in1_inv or posedge reset)

  if(reset)   dff1a <=  1'b0;

  else      dff1a <=  select_in & !dff0b;

 

always @(posedge clk_in1 or posedge reset)

  if(reset)   dff1b <=  1'b0;

  else      dff1b <=  dff1a;

 

wire  clk_in1_gate  = ~(~clk_in1 & dff1b);

 

//-----------------------

//  clock mux out

//-----------------------

assign  clk_out = clk_in0_gate & clk_in1_gate;

 

endmodule

 

 

  1. 《verilog编程艺术》中的时钟切换电路

 

 

`timescale  1ns/1ns

module  clk_switch

  (

    input   clk_a,

    input   clk_b,

    input   select,

    output  out_clk

  );

 

reg q1,q2,q3,q4;

wire  or_one,or_two,or_three,or_four;

 

always @(posedge clk_a)

  if(clk_a==1'b1) begin

    q1  <=  q4;

    q3  <=  or_one;

  end

 

always @(posedge clk_b)

  if(clk_b==1'b1)  begin

    q2  <=  q3;

    q4  <=  or_two;

  end

assign  or_one  = (!q1) | (select == 1'b1);

assign  or_two  = (!q2) | (select == 1'b0);

 

assign  or_three  = (q3) | (clk_a);

assign  or_four = (q4) | (clk_b);

 

assign  out_clk = or_three & or_four;

 

endmodule

 

module  clk_switch_tb;

reg clk_a,clk_b;

reg select;

wire  out_clk;

 

clk_switch  clk_switch(clk_a,clk_b,select,out_clk);

 

initial begin

  clk_a = 0;

  forever #50 clk_a = !clk_a;

end

initial begin

  clk_b = 0;

  forever #10 clk_b = !clk_b;

end

 

initial begin

  select  = 0;

  #1000;

  select  = 1;

  #1000;

  select  = 0;

  #1000;

  select  = 1;

  #1000;

  $finish;

end

 

endmodule

 

 

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值