单bit信号跨时钟域

单bit信号跨时钟域

原文链接:https://blog.csdn.net/u013668469/article/details/99480694

1.电平同步器

单bit信号跨时钟域最为简单的方法就是通过寄存器打两拍进行同步,也就是所谓的电平同步器。给出电路图:
在这里插入图片描述
为了使同步器正常工作,从原时钟传来的信号应该先通过原时钟上的一个触发器,以消除所带的毛刺,而后不经过任何组合逻辑,进行打两拍,这一要求非常重要,因为同步器的第一级触发器对组合逻辑所产生的毛刺非常敏感,一旦毛刺满足条件时序要求时,会给同步时钟送出虚假的信号。本质来说,电平同步器是用来降低跨时钟域时可能产生的亚稳态

根据上述电路,信号在两个同步时钟周期以后,便可以成为新时钟域下的有效信号。考虑到时钟关系,信号的延时是新时钟域中的一到两个时钟周期。

但这样的电路有一个明显的局限性,可同步的信号需要满足较为苛刻的要求。从本质上说,就是信号必须要被新时钟域所采到,而不能有遗漏,因而原时钟域下的信号必须足够长。即:

从慢时钟域传递到快时钟域(快采慢)

  • 从慢时钟域传递到快时钟域(快采慢)。信号肯定被采到,故最为适用。但此时输出信号一般为电平信号,如果要求获得与新周期等宽的脉冲信号,则不可用。

快时钟域传递到慢时钟域下(慢采快)

  • 快时钟域传递到慢时钟域下(慢采快),传递的信号必须为较宽的电平信号,要求保持高电平或低电平一个同步时钟周期以上。和输入信号关系较大,不可传递原时钟周期的脉冲信号。因而不适用与快时钟传递到慢时钟。
//电平同步器
`timescale 1ns/1ps
module level_syc(
    input wire clk_1,
    input wire clk_2,
    input wire din,
	input wire rst_n,
    
	output wire dout
);
	
    reg src_state;
	reg src_state_d0, src_state_d1;
	
	//原时钟域信号寄存器输出,消除毛刺
	always @(posedge clk_1 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
			src_state <= 1'b0;
		else
			src_state <= din;
	end
	
	//同步至新时钟域
	always @(posedge clk_2 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
		begin
			src_state_d0 <= 1'b0;
			src_state_d1 <= 1'b0;		
		end
		else
		begin
			src_state_d0 <= src_state;
			src_state_d1 <= src_state_d0;
		end
	end
	
	assign dout = src_state_d1;
	
endmodule

//测试文件
module level_syc_tb();
	reg clk_1, clk_2, rst_n;
	reg din;
	
	//		慢时钟域到快时钟域
	always
		begin
			#30 clk_1 = ~clk_1;
		end
	always
		begin
			#10 clk_2 = ~clk_2;
		end
	
	//		快时钟域到慢时钟域
/*	always
		begin
			#10 clk_1 = ~clk_1;
		end
	always
		begin
			#30 clk_2 = ~clk_2;
		end
*/	
	initial
		fork
			clk_1 = 1'b1;
			din = 1'b0;
			#5 clk_2 = 1'b1;
			#10 rst_n = 1'b0;
			#50 rst_n = 1'b1;		
			
			//慢时钟域到快时钟域
			#200 din = 1'b1;	
			#260 din = 1'b0;
			
			//快时钟域到慢时钟域,高电平持续两个同步时钟周期
/*			#320 din = 1'b1;
			#380 din = 1'b0;
			
			//快时钟域到慢时钟域,高电平持续小于两个同步时钟周期
			#800 din = 1'b1;
			#820 din = 1'b0;
*/		
        join
		
	level_syc u1(.clk_1(clk_1), .clk_2(clk_2), .rst_n(rst_n),
				 .din(din), .dout(dout));
				 
endmodule

最后给出Modelsim的仿真结果:

慢时钟域到快时钟域:
在这里插入图片描述

信号顺利完成跨时钟域。同步延时为80ns。

从波形图中推导可得,电平同步器所引起的延迟区间为[2Dsetup + T2, T1 + 2T2]。

快时钟域到慢时钟域:
在这里插入图片描述
第一个信号完成跨时钟域,第二个信号被滤掉。

小结:

  • 通过波形推导,并忽略原时钟下的触发器的延时,信号的同步的延时介于(T2 + Dsetup)和(2T2)之间,或者换另一种说法, 信号在两个新时钟有效沿之后,就成为了新时钟域下的有效信号。

  • 从慢时钟域跨域到快时钟域的信号,在输出时,是一个多周期的信号,如果电路要求输出一个与周期等宽的脉冲信号,则电平同步器是不适用的。??

  • 从快时钟域向慢时钟域传递时钟周期的脉冲信号时,信号很可能会被滤掉。结合波形图可得,信号必须持续至少一个同步时钟周期,才能确保肯定被采到,完成跨时钟域。考虑到跨时钟域下,时钟的相互关系并不确定,因而,采用电平同步器进行块到慢时钟域的跨越是不合理的。

2、边沿同步(检测)器

在电平同步器的基础上,通过输出端的组合逻辑,可以完成对于信号边沿的提取,识别上升沿、下降沿以及双边沿,并发出相应的脉冲。给出电路图(省略了原时钟域下的触发器输出):

在这里插入图片描述
基本思想:信号在新时钟域打两拍完成同步之后,再外接一个触发器,相当于将信号再向后延迟一个周期。之后,通过非门和与门,对标准同步信号以及延迟信号进行逻辑组合,从而完成边沿的提取,最终得到一个与新时钟周期等宽,高电平有效的脉冲信号。

设上述三个触发器从左到右的输出分别为 Q0, Q1, Q2,则有:

提取上边沿 pules = Q1 & (~Q2);
提取下边沿 pulse = (~Q1) & Q2;
提取双边沿 pulse = Q1 ^ Q2;

综上,相比于电平同步器,边沿同步器的作用主要是在跨时钟域的基础上,检测信号的边沿而作脉冲输出,因而适合要求脉冲输出的电路。对于跨时钟的频率要求与电平同步器一致,仅适合从慢时钟域跨到快时钟域的信号

//边沿同步器
module edge_syc(
    input wire clk_1,
    input wire clk_2,
    input wire din,
	input wire rst_n,
    
	output wire dout_r,
	output wire dout_f,
	output wire dout_e
	
);
    reg src_state;
	reg src_state_d0, src_state_d1, src_state_d2;
	
	//原时钟域下脉冲信号转变为电平信号
	always @(posedge clk_1 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
			src_state <= 1'b0;
		else
			src_state <= din;
	end
	
	//同步至新时钟域
	always @(posedge clk_2 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
		begin
			src_state_d0 <= 1'b0;
			src_state_d1 <= 1'b0;
			src_state_d2 <= 1'b0;		
		end
		else
		begin
			src_state_d0 <= src_state;
			src_state_d1 <= src_state_d0;
			src_state_d2 <= src_state_d1;
		end
	end
	
	//边沿检测产生新的脉冲
	assign dout_r = src_state_d1 & ~src_state_d2; //上升沿
	assign dout_f = !src_state_d1 & src_state_d2; //下降沿
	assign dout_e = src_state_d1 ^ src_state_d2;  //双边沿
	
endmodule

//测试文件
module edge_syc_tb(); 
	reg clk_1, clk_2, rst_n;
	reg din;
	
	always
		begin
			#30 clk_1 = ~clk_1;
		end
	
	always
		begin
			#10 clk_2 = ~clk_2;
		end
		
	initial
		fork
			clk_1 = 1'b1;
			din = 1'b0;
			#5 clk_2 = 1'b1;
			#10 rst_n = 1'b0;
			#50 rst_n = 1'b1;								
			
			#200 din = 1'b1;	
			#260 din = 1'b0;
			
			#320 din = 1'b1;
			#380 din = 1'b0;
			
//			#400 din = 1'b1;
//			#460 din = 1'b0;
		join
		
	edge_syc u1(.clk_1(clk_1), .clk_2(clk_2), .rst_n(rst_n),
				 .din(din), .dout_r(dout_r) ,.dout_f(dout_f),
				 .dout_e(dout_e));
endmodule

在这里插入图片描述
从上述波形可以看出,边沿同步器成功的将慢时钟域下的一个信号,转化为了快时钟域下的一个与周期同宽的脉冲信号

波形给出的周期延时为80ns,忽略原时钟触发器引起的40ns的延时,则边沿同步器给出的延时时间为40ns,恰好为两个新时钟的周期。通过观察和推导,同样可得,这个延迟最高为2个同步时钟周期,40ns,最低为1个同步时钟周期+Dsetup。此外,考虑到输出的组合逻辑,需要再加上组合逻辑所带来的延时,Tlogic,因而边沿同步器的延时区间为[T2 + Dsetup + Tlogic, 2T2 + Tlogic]。

3、 脉冲同步器

之前所考虑的两个同步器,都只适合从慢时钟域到快时钟域,不必考虑新时钟域下采不到信号的问题。从快时钟域传递单bit信号到慢时钟域,则需要用到脉冲同步器。

基本思想:先将原时钟域下的脉冲信号,转化为电平信号,再进行同步,同步完成之后再把新时钟域下的电平信号转化为脉冲信号(边沿检测器的功能)。结合之前所了解的两种同步器,实际上在这里只需要考虑完成脉冲到电平的转化便可以了。

脉冲转化电平有两种基本的方法,后续了解到其他的方法再进行补充。

  • 1、通过二选一选择器进行转化,给出脉冲同步器的电路图:

在这里插入图片描述
分析:假设初始时Q为0,Q非为1,在DATA为低电平时,Q值保持不变;在DATA脉冲的高电平到来时,Q完成翻转,变为1,此时Q非为0;而后DATA脉冲结束,变为低电平,Q值保持高电平不变,一直为高,直到下一次脉冲到来完成翻转。

  • 2、通过异或门进行转化
    在这里插入图片描述
    分析:假设初始Q为0。在in_pulse为低电平时,异或门输入相同,输出为0,Q值保持为0不变;在in_pulse脉冲的高电平到来时,异或门输入不同,输出为1,Q值变为1;此后in_pulse脉冲的低电平到来,异或门输入仍旧不同,Q值保持为1不变,直到下一次脉冲带来才发生翻转。

限制
此外,脉冲同步器有一个重要的限制,即输入脉冲的需要有两个同步时钟周期的间隔。输入脉冲如果相距过近,则新时钟域中的输出脉冲也会紧密相邻,结果输出的脉冲宽度高于一个同步时钟周期,这是不期望地。

//脉冲同步器
module pulse_syc(
    input wire clk_1,
    input wire clk_2,
    input wire din,
	input wire rst_n,
    
	output dout
);
    reg src_state;
	reg src_state_d0, src_state_d1, src_state_d2;
	
	//原时钟域下脉冲信号转变为电平信号
	always @(posedge clk_1 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
			src_state <= 1'b0;

//		else if(din == 1'b1)		//通过2选1MUX完成翻转功能,脉冲到来完成从脉冲到电平的转换
//			src_state <= ~src_state;

		else 
			src_state <= din ^ src_state;		//通过异或门做处理
	end
	
	//同步至新时钟域
	always @(posedge clk_2 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
		begin
			src_state_d0 <= 1'b0;
			src_state_d1 <= 1'b0;
			src_state_d2 <= 1'b0;		
		end
		else
		begin
			src_state_d0 <= src_state;
			src_state_d1 <= src_state_d0;
			src_state_d2 <= src_state_d1;
		end
	end
	
	//边沿检测产生新的脉冲
	assign dout = src_state_d1 ^ src_state_d2;
	
endmodule

//		高频到低频		//
module pulse_syc_tb();
	reg clk_1, clk_2, rst_n;
	reg din;
	
	always
		begin
			#10 clk_1 = ~clk_1;
		end
	
	always
		begin
			#30 clk_2 = ~clk_2;
		end
		
	initial
		fork
			clk_1 = 1'b1;
			din = 1'b0;
			#5 clk_2 = 1'b1;
			#10 rst_n = 1'b0;
			#50 rst_n = 1'b1;			
			#100 din = 1'b0;		
			
			#200 din = 1'b1;	//间隔两个同步周期的脉冲信号
			#220 din = 1'b0;
			#320 din = 1'b1;
			#340 din = 1'b0;
			
			#600 din = 1'b1;	//间隔一个同步周期的脉冲信号
			#620 din = 1'b0;
			#680 din = 1'b1;
			#700 din = 1'b0;
			
			#900 din = 1'b1;	//等于两个原时钟周期的脉冲信号
			#1020 din = 1'b0;
		join
		
	pulse_syc u1(.clk_1(clk_1), .clk_2(clk_2), .rst_n(rst_n),
				 .din(din), .dout(dout));
endmodule

在这里插入图片描述
自己仿真如下:
在这里插入图片描述

观察上述波形可得如下结论:

  • 当输入脉冲间隔为120ns(两个同步时钟周期)时,脉冲同步器可正常完成输出;当输入脉冲间隔为80ns(低于两个同步时钟周期)时,脉冲同步器错误输出了一个更宽的脉冲。因而,脉冲同步器对于脉冲的间隔有比较严格的要求:输入脉冲的最小间隔必须等于两个新时钟的时钟周期。

  • 脉冲同步器完成同步的延时为100ns,即在一个同步周期和两个同步周期之间。由于不考虑原时钟域下的触发器以及组合逻辑的延迟,因此,脉冲同步器的延时时间与边沿同步器相同:[T2 + Dsetup + Tlogic, 2T2 + Tlogic]。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值