zoukankan      html  css  js  c++  java
  • 时钟无缝切换

    转载自:https://blog.csdn.net/u010668547/article/details/80250997

    本文讨论了时钟切换的两种基本情况以及两种基本电路结构,讨论了一些问题:

    下图是一个时钟选择的简单实现以及时序图,使用AND-OR多路复用逻辑,其中SELECT信号为时钟选择信号,如图中所示,直接切换会产生毛刺(glitch)

    时钟切换分为两种情况:(1)CLK0与CLK1为相关时钟源,即CLK0与CLK1成整数倍关系;(2)CLK0与CLK1之间没有关系;

    (1)CLK0与CLK1为相关时钟源

    (2)CLK0与CLK1为无关时钟源

    时钟切换源代码:

    `timescale 1ns/1ns
    module clk_syn_tst (
                    rst_n,
                    clka, 
                    clkb, 
                    sel_clkb, 
                    clk_o
                       );
    input rst_n;
    input clka;
    input clkb;
    input sel_clkb;
    
    output clk_o;
    
    reg    sel_clka_d0;
    reg    sel_clka_d1;
    reg    sel_clka_dly1;
    reg    sel_clka_dly2 ;
    reg    sel_clka_dly3;
    reg    sel_clkb_d0;
    reg    sel_clkb_d1;
    reg    sel_clkb_dly1;
    reg    sel_clkb_dly2;
    reg    sel_clkb_dly3;
    
    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 @ (negedge 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'b1;
       sel_clkb_d1 <= 1'b1;
       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 @ (negedge clkb or negedge rst_n)
    begin
       if (!rst_n) 
       begin
          sel_clkb_dly1 <= 1'b1;
          sel_clkb_dly2 <= 1'b1;
          sel_clkb_dly3 <= 1'b1;
       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

    时钟切换测试代码:

    `timescale 1ns/1ns
    module clk_syn_tb;
    
    parameter DLY=1;
    
    reg rst_n;
    reg clka;
    reg clkb;
    reg sel_clkb;
    
    wire clk_o;
    clk_syn_tst test1(
                           //input
                           .rst_n           (rst_n), //system reset
                           .clka            (clka), //clock A
                           .clkb            (clkb), //clock B
                           .sel_clkb        (sel_clkb),//pulse input from clka
                           // output 
                           .clk_o           (clk_o)//pulse output in clkb
                      );
    initial
    begin
    rst_n=0;
    clka=0;
    clkb=0;
    sel_clkb=0;
    #20 rst_n =1;
    #500 sel_clkb=1;
    #500 sel_clkb=0;
    end
    
    always #5 clka<=~clka;
    
    always #20 clkb<=~clkb;
    
    //always @(posedge clka or negedge rst_n)
    //begin
    //if(rst_n==1'b0)
    //puls_a_in<=0;
    //else
    //puls_a_in<=# DLY $random %2;
    //end
    
    endmodule

    本文代码部分中clka即为CLK0,clkb即为CLK1,sel_clkb即为SELECT,时钟切换的结构有些不同,

       begin
       sel_clkb_d0 <= sel_clkb & (~sel_clka_dly3) ;
       sel_clkb_d1 <= sel_clkb_d0 ;
       end

    代码相当于将(sel_clkb)&(~sel_clka_dly3)之后,再打两拍在反馈回路上增加两个DFF,结合sel_clkb_d1之后经过了三拍延迟,

    begin
    sel_clkb_dly1 <= sel_clkb_d1;
    sel_clkb_dly2 <= sel_clkb_dly1 ;
    sel_clkb_dly3 <= sel_clkb_dly2 ;
    end

    故由sel_clkb由0变为1时,clk_o在经过5个clkb下降沿之后,才由clka切换至clkb。同时在这里需要考虑两个问题:

    (1)为什么part4中,需要在下降沿(negedge)对sel_clkb_d1进行采样,假设采用上升沿(posedge)对sel_clkb_d1进行采样,结果又会怎样:

    //always @ (posedge clka_n or negedge rst_n)
    always @ (negedge 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

    采用下降沿对sel_clkb_d1进行采样:

    采用上升沿对sel_clkb_d1进行采样:

    圆圈中的毛刺是由于在sel_clkb由0变为1时,需要5个clka时钟输出clk_o才关闭,但是由于是在clka上升沿进行采样,之后与clka进行&操作,&操作在clk上升沿之后,故进行与操作会产生毛刺。

    (2)在对寄存器进行赋值时,为什么复位初始值都设置为0,这里面出于什么考虑:

    // part2
    //always @ (posedge clka_n or negedge rst_n)
    always @ (negedge 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

    复位初始值设置为0:

    此时sel_clka_dly3与sel_clkb_dly3都为0,即在复位时将clk_o锁住,输出为0,在rst_n被拉起后,经过5个clka,clka的使能信号sel_clkb(0)才被采到。

    假设与clkb相关的寄存器复位状态都设置为1,与clka相关的寄存器复位状态都设置为0,则时序图为:

    此时相当于clk_o由时钟clkb向clka进行了切换,因此在复位的时候进行了依次切换。

  • 相关阅读:
    Hadoop: No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).解决办法
    Libnfc
    生产服务GC调优实践及基本流程总结
    万年历算法
    C#中操作Oracle时的SQL语句参数的用法
    对DataGrid的初步了解
    C#中HashTable的用法
    正则表达式
    C#动态调用WebService
    Ubuntu 故障处理笔记
  • 原文地址:https://www.cnblogs.com/p1332668050/p/13788045.html
Copyright © 2011-2022 走看看