IC基础知识:时钟无毛刺切换

  • Post author:
  • Post category:其他


参考这篇文章:

数字电路时钟无毛刺切换



因为直接用组合逻辑判断去切换时钟,会产生毛刺

,如下图所示。

在这里插入图片描述

在这里插入图片描述


产生该毛刺的原因

: sel没有在时钟跳边沿改变。因为我们一般是用clk上升沿采样数据,所以我们sel选择信号最好是在clk下降沿改变,可以让切换的时候,不会错过上升沿。


我理解的整体思路

: 将en信号先经过打3拍(异步时钟要打三拍),然后经过一个ICG(integrate clock Gating)

ICG框图

在这里插入图片描述

作用:让sel同步于时钟并且在下降沿改变。

设计代码



module clock_switch(
        input                   s_rst_n                 ,       
        input                   s_clk_1                 ,       
        input                   s_clk_2                 ,       
        input                   s_clk_3                 ,       
        input [1:0]             i_clk_sel               ,//00: clk1, 01: clk2, 10/11: clk3       
        output                  o_clk
);


//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/
wire                            clk1_sel                        ;       
reg                             clk1_sel_syn1                   ;       
reg                             clk1_sel_syn2                   ;       
reg                             clk1_sel_syn3                   ;    

wire                            clk2_sel                        ;       
reg                             clk2_sel_syn1                   ;       
reg                             clk2_sel_syn2                   ;       
reg                             clk2_sel_syn3                   ;       

wire                            clk3_sel                        ;       
reg                             clk3_sel_syn1                   ;       
reg                             clk3_sel_syn2                   ;       
reg                             clk3_sel_syn3                   ;       

reg                             clk1_sel_real                   ;       
reg                             clk2_sel_real                   ;       
reg                             clk3_sel_real                   ;       

assign clk1_sel = (i_clk_sel == 2'b00)? 1'b1: 1'b0;
assign clk2_sel = (i_clk_sel == 2'b01)? 1'b1: 1'b0;
assign clk3_sel = (i_clk_sel == 2'b10 || i_clk_sel == 2'b11)? 1'b1: 1'b0;

always  @(posedge s_clk_1 or negedge s_rst_n) begin
        if(s_rst_n == 1'b0) begin
            clk1_sel_syn1       <= 'd0            ;       
            clk1_sel_syn2       <= 'd0            ;       
            clk1_sel_syn3       <= 'd0            ;    
        end
        else begin
            clk1_sel_syn1       <= clk1_sel  & (~clk2_sel_syn3) & (~clk3_sel_syn3)       ;       
            clk1_sel_syn2       <= clk1_sel_syn1    ;       
            clk1_sel_syn3       <= clk1_sel_syn2    ;   
        end
                
end


always  @(posedge s_clk_2 or negedge s_rst_n) begin
        if(s_rst_n == 1'b0) begin
            clk2_sel_syn1       <= 'd0            ;       
            clk2_sel_syn2       <= 'd0            ;       
            clk2_sel_syn3       <= 'd0            ;    
        end
        else begin
            clk2_sel_syn1       <= clk2_sel & (~clk1_sel_syn3) & (~clk3_sel_syn3)        ;       
            clk2_sel_syn2       <= clk2_sel_syn1    ;       
            clk2_sel_syn3       <= clk2_sel_syn2    ;   
        end
                
end

always  @(posedge s_clk_3 or negedge s_rst_n) begin
        if(s_rst_n == 1'b0) begin
            clk3_sel_syn1       <= 'd0            ;       
            clk3_sel_syn2       <= 'd0            ;       
            clk3_sel_syn3       <= 'd0            ;    
        end
        else begin
            clk3_sel_syn1       <= clk3_sel  & (~clk1_sel_syn3) & (~clk2_sel_syn3)       ;       
            clk3_sel_syn2       <= clk3_sel_syn1    ;       
            clk3_sel_syn3       <= clk3_sel_syn2    ;   
        end
end



always  @(negedge s_clk_1 or negedge s_rst_n) begin
    if(s_rst_n == 1'b0)
         clk1_sel_real <= 'd0;
    else
         clk1_sel_real <= clk1_sel_syn3;
//         clk1_sel_real <= clk1_sel_syn2;
end

always  @(negedge s_clk_2 or negedge s_rst_n) begin
    if(s_rst_n == 1'b0)
         clk2_sel_real <= 'd0;
    else
         clk2_sel_real <= clk2_sel_syn3;
         //clk2_sel_real <= clk2_sel_syn2;
end

always  @(negedge s_clk_3 or negedge s_rst_n) begin
    if(s_rst_n == 1'b0)
         clk3_sel_real <= 'd0;
    else
         clk3_sel_real <= clk3_sel_syn3;
         //clk3_sel_real <= clk3_sel_syn2;
end
//验证了,只打两拍确实有问题
assign o_clk = (clk1_sel_real & s_clk_1) | (clk2_sel_real & s_clk_2) | (clk3_sel_real & s_clk_3);

endmodule

testbench 代码

`timescale      1ns/1ns

module clock_switch_tb();

reg                             s_rst_n                         ;       
reg                             s_clk_1                         ;       
reg                             s_clk_2                         ;       
reg                             s_clk_3                         ;       
reg  [1:0]                      i_clk_sel                       ;       
wire                            o_clk                           ;       

clock_switch  clock_switch_inst(
        .s_rst_n                (s_rst_n                ),
        .s_clk_1                (s_clk_1                ),
        .s_clk_2                (s_clk_2                ),
        .s_clk_3                (s_clk_3                ),
        .i_clk_sel              (i_clk_sel              ),
        .o_clk                  (o_clk                  )
);

always #5 s_clk_1 = ~s_clk_1;
always #10 s_clk_2 = ~s_clk_2;
always #20 s_clk_3 = ~s_clk_3;

initial begin
        s_rst_n     = 0                    ;       
        s_clk_1     = 0                    ;       
        s_clk_2     = 0                    ;       
        s_clk_3     = 0                    ;       
        i_clk_sel   = 0                    ;   
        #20;
        s_rst_n = 1;
        #200;
        i_clk_sel = 1;
        #200;
        i_clk_sel = 2;
        #200;        
        i_clk_sel = 3;
        #200;
        $finish;
end
//generate fsdb file
initial begin
	$fsdbDumpfile("clock_switch_tb.fsdb");
  	$fsdbDumpvars(0,clock_switch_tb);
end
endmodule

仿真波形

在这里插入图片描述

一般无毛刺切换,切换始终的时候,都会需要几个clock的缓冲。