无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)

从秋招的经验来看,Verilog设计类的题目,如:奇偶分频,状态机,序列检测,波形产生,跨时钟域处理,门控时钟,同步FIFO,格雷码与二进制码转换,异步复位同步释放,时钟切换,异步FIFO等,其中最为复杂的恐怕属于时钟切换了吧。

无毛刺的时钟切换英文名叫:Glitch-free clock switching circuit,不要把 Clock Domain Conversion当成了时钟切换,傻傻地设计了半天答非所问。

Clock Domain Conversion为跨时钟域传输问题,就是最常考也是经常听到的CDC问题(应该会有专门的总结)。

今天的主题是Glitch Free Clock Switching:

就像这样:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第1张图片

clk0和clk1是无关时钟,如何实现二者之间的一个无毛刺切换是接下来需要解决的问题?


先从最容易相当的设计说起:

可以使用纯组合逻辑来设计时钟切换电路,但是这种设计不可避免的产生毛刺:

如下,两种一个道理:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第2张图片

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第3张图片

如何改进这种设计呢?

之前的博客也写过这个话题,今天仿真了下,用Verilog描述了下,确实有点意思?


相关时钟源的时钟切换电路:

从前面组合逻辑时钟切换电路的设计,我们得出的切换波形如下:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第4张图片

可见,会产生毛刺,而产生毛刺的原因在于sel信号改变的位置没有和需要切换的时钟对齐。

如何改进这种问题呢?

我们可以用当前时钟的下降沿来采样(sel与反馈的输出相与的结果),可以简单地理解为采样sel信号。具体的设计如下:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第5张图片

可以这么理解,如下图:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第6张图片

sel为0时,选择输出clk0,如上图,先对sel在时钟域clk0进行下降沿采样,得到sel_clk0,取反得到sel_clk0_n,这个信号与时钟clk0相与输出得到clkout0;

sel_clk0反馈到时钟域clk1的输入端,与sel相与之后,再经过clk1下降沿采样得到sel_clk1,这个信号与clk1想与得到输出clkout1;

clkout0与clkout1进行或运算得到输出clkout,这个时钟便是进行时钟切换后的时钟输出。

这样经过下降沿采样以及反馈就可以得到无毛刺的相关时钟切换电路。


对于无关时钟源的无毛刺时钟切换电路设计

上一部分讲了相关时钟源的无毛刺时钟切换,用的是下降沿采样sel信号从而达到去除毛刺的原理;

下面给出无关时钟源的无毛刺时钟切换方案:

如下图:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第7张图片

画出相关波形图:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第8张图片

sel为0时候,先取反得到seln,然后clk0时钟下上升沿采样seln_clk0_r1,之后下降沿采样得到seln_clk0_r2,这个信号与clk0相与得到clkout0;

seln_clk0_r2n反馈到时钟域clk1,与clk1相与,用clk1上升沿采样得到sel_clk1_r1,再用clk1下降沿采样得到sel_clk1_r2,这个信号与clk1相与得到输出clkout1;

最终的输出为clkout0与clkout1的或,即输出clkout为无毛刺的时钟切换波形。

对于无关时钟源的无毛刺时钟切换电路的设计,参考文章

无关时钟源的时钟切换电路设计:

设计的Verilog描述:

module glitch_free (
	input clk0,    // Clock
	input clk1,
	input select,
	input rst_n,  // Asynchronous reset active low
	
	output clkout
);

	wire mid_clk0;
	reg mid_clk0_r1, mid_clk0_r2, mid_clk0_r2n;
	wire mid_clk1;
	reg mid_clk1_r1, mid_clk1_r2, mid_clk1_r2n;

	assign mid_clk1 = select & mid_clk0_r2n;

	assign mid_clk0 = ~select & mid_clk1_r2n;

	
	//第一级触发器用上升沿采样,选择信号与反馈信号的与运算
	always@(posedge clk1 or negedge rst_n) begin
		if(~rst_n) mid_clk1_r1 <= 0;
		else mid_clk1_r1 <= mid_clk1;
	end

	//第二级触发器用下降沿采样
	always@(negedge clk1 or negedge rst_n) begin
		if(~rst_n) begin
			mid_clk1_r2 <= 0;
			mid_clk1_r2n <= 1;
		end
		else begin
			mid_clk1_r2 <= mid_clk1_r1;
			mid_clk1_r2n <= ~mid_clk1_r1;
		end
	end


	//第一级触发器用上升沿采样,选择信号与反馈信号的与运算
	always@(posedge clk0 or negedge rst_n) begin
		if(~rst_n) mid_clk0_r1 <= 0;
		else mid_clk0_r1 <= mid_clk0;
	end

	//第二级触发器用下降沿采样
	always@(negedge clk0 or negedge rst_n) begin
		if(~rst_n) begin
			mid_clk0_r2 <= 0;
			mid_clk0_r2n <= 1;
		end
		else begin
			mid_clk0_r2 <= mid_clk0_r1;
			mid_clk0_r2n <= ~mid_clk0_r1;
		end
	end

	wire mid_clk11, mid_clk00;
	assign mid_clk11 = clk1 & mid_clk1_r2;
	assign mid_clk00 = clk0 & mid_clk0_r2;

	assign clkout = mid_clk11 | mid_clk00;



endmodule

Testbench文件:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
module test_tb(

    );
    reg clk0, clk1;
    reg select;
    wire clkout;
    
    initial begin
    clk0 = 0;
    forever
    #2 clk0 = ~clk0;
    end
    
    initial begin
    clk1 = 0;
    forever
    #5 clk1 = ~clk1;
    end
    
    
    reg rst_n;
    reg in;
    initial begin
    select = 0;
    rst_n = 0;
    #5
    rst_n = 1;
    #30
    select = 1;
    #40
    select = 0;
    
    
    end
    
    
    
   	glitch_free inst_glitch_free (.clk0(clk0), .clk1(clk1), .select(select), .rst_n(rst_n), .clkout(clkout));

      
    
endmodule

仿真波形:

无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)_第9张图片

 

你可能感兴趣的:(#)