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进行了切换,因此在复位的时候进行了依次切换。

  • 相关阅读:
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
  • 原文地址:https://www.cnblogs.com/p1332668050/p/13788045.html
Copyright © 2011-2022 走看看