数字集成电路设计-10-关于采用两级触发器实现双时钟域信号同步方法的分析与验证

引言

实现双时钟域数据的交换,最常见的方法就是采用双时钟的异步fifo。但是对于单根信号线,如果仍然采用异步fifo就显得非常stupid,这时,往往通过两级触发器来实现同步。

那么,两级触发器是如何实现同步的呢?本小节就做一个简单的分析和验证。


1,一段代码

下面是我刚刚从ORPSoC的工程里看到的一段代码:


//'ddr2_writeback_done':generate in ddr2_if_clk domain
always @(negedge ddr2_if_clk)
	if (ddr2_rst)
		ddr2_writeback_done <= 0;
	else if (ddr2_writeback_done_wb_sync2)
		ddr2_writeback_done <= 0;
	else if (ddr2_write_state_shr[6])
		ddr2_writeback_done <= 1;

//'ddr2_writeback_done' sync to 'wb_writeback_done' in wb_clk domain
always @(posedge wb_clk)
	if (wb_rst)
		begin
			wb_writeback_done_sync <= 0;
			wb_writeback_done_sync2 <= 0;
		end 
     else
		begin
			wb_writeback_done_sync <= ddr2_writeback_done;
			wb_writeback_done_sync2 <= wb_writeback_done_sync;
		end

assign wb_writeback_done = !wb_writeback_done_sync2 & wb_writeback_done_sync;


//use 'wb_writeback_done' in wb_clk domain
always @(posedge wb_clk)
	if (wb_rst)
		do_writeback <= 0;
	else if (wb_writeback_done)
		do_writeback <= 0;
	else if (start_writeback)
		do_writeback <= 1;
		
		
		


这段代码就可以实现两个时钟域(wb_clk和ddr2_if_clk)间ddr2_writeback_done信号的同步。


2,编写test case

为了更清晰的展示其具体的同步过程,我写了一个简单的test case。

a,可综合的sync.v:


/*
* file name		:sync.v
* author		:Rill
* date			:2014-04-12
*/


module sync
(
input clk_a,
input rst_a,
input enable_a,

input clk_b,
input rst_b,
output enable_b
);

reg signal_a;

reg sync1;
reg sync2;

wire signal_b;
reg enable;

assign enable_b = enable;

//'signal_a':generated in clk_a domain
always @(negedge clk_a)
begin
	if (rst_a)
		signal_a <= 0;
	else if (sync2)
		signal_a <= 0;
	else if (enable_a)
		signal_a <= 1;
end
//'signal_a' sync to 'signal_b' in wb_clk domain
always @(posedge clk_b)
begin
	if (rst_b)
		begin
			sync1 <= 0;
			sync2 <= 0;
		end 
	else
		begin
			sync1 <= signal_a;
			sync2 <= sync1;
		end
end

assign signal_b = !sync2 & sync1;


//use 'signal_b' in wb_clk domain
always @(posedge clk_b)
begin
	if (rst_b)
		enable <= 1'b0;
	else if (signal_b)
		enable <= 1'b1;
	else
		enable <= 1'b0;
end


endmodule
		
/********* EOF *************/


b,不可综合的sync_tb.v:


/*
* file name		:sync_tb.v
* author		:Rill
* date			:2014-04-12
*/

`timescale 1ns/1ns


module sync_tb;

reg clk_a;
reg rst_a;
reg enable_a;

reg clk_b;
reg rst_b;
wire enable_b;


parameter CLK_A_PERIOD = 10;
parameter CLK_B_PERIOD = 30;

//gen clk_a
always #(CLK_A_PERIOD/2) clk_a = ~clk_a;

//gen clk_b
always #(CLK_B_PERIOD/2) clk_b = ~clk_b;


//gen clk_a domain test pattern
integer m;

initial
begin
	//rst
	#0
	clk_a = 1'b0;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b1;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b0;
	
	//wait clk_b domain rst done
	repeat (1000) @(negedge clk_a);
	
	//gen test signal
	for(m=0;m<10;m=m+1)
	begin
		enable_a =1'b1;
		@(negedge clk_a);
		enable_a =1'b0;

		repeat (100) @(negedge clk_a);
	end
	
	repeat (1000) @(negedge clk_a);
	$stop;
	
end

initial
begin
	//rst
	#0
	clk_b = 1'b0;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b1;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b0;
	
end


sync SYNC0
(
.clk_a (clk_a),
.rst_a (rst_a),
.enable_a (enable_a),

.clk_b (clk_b),
.rst_b (rst_b),
.enable_b (enable_b)
);

endmodule

/********* EOF *************/



3,前仿验证A

下面是前仿的波形:

数字集成电路设计-10-关于采用两级触发器实现双时钟域信号同步方法的分析与验证_第1张图片


从中可以看出:

a,clk_a是下降沿触发,clk_b是上升沿触发。

b,先看m信号上面的波形:

clk_a时钟域产生了一个enable_a信号,这个信号被clk_b时钟域detect到了(enable_b信号)。

enable_a信号是clk_a时钟域下降沿同步的。enable_b信号是clk_b时钟域上升沿同步的。

那么具体是如何实现同步的呢?我们再看m信号下面的波形。

c,首先clk_a时钟域下降沿产生一个周期enable_a信号。

d,这个信号寄存到和他同一时钟域的signal_a。

e,异步的,clk_b时钟域上升沿采集到signal_a并将之寄存到本时钟域的sync1。

f,在clk_b时钟域,经过一个cycle,将sync1传给sync2。这时,即可采集到signal_b信号,而这个signal_b信号就是在clk_b时钟域的。

g,异步的,在clk_a时钟域,在检测到sync2有效以后的第一个下降沿,将寄存enable_a信号的signal_a清除。

h,整个同步过程用了6个clk_a周期,2个clk_b周期。


4,前仿验证B

从上面的分析,我们可以看出,采用两级触发器确实可以实现双时钟域信号的同步,但是这种方式也不是随便任何时候都能使用的,如果是慢时钟域同步快时钟域的信号,则要求快时钟域的信号产生的不能过快,否则将会丢失部分信号。

还是上面的sync.v,我们修改sync_tb.v中enable_a产生之后等待的时间,就会发现有丢失的情况发生。

修改后的sync_tb.v:


/*
* file name		:sync_tb.v
* author		:Rill
* date			:2014-04-12
*/

`timescale 1ns/1ns


module sync_tb;

reg clk_a;
reg rst_a;
reg enable_a;

reg clk_b;
reg rst_b;
wire enable_b;


parameter CLK_A_PERIOD = 10;
parameter CLK_B_PERIOD = 30;

//gen clk_a
always #(CLK_A_PERIOD/2) clk_a = ~clk_a;

//gen clk_b
always #(CLK_B_PERIOD/2) clk_b = ~clk_b;


//gen clk_a domain test pattern
integer m;

initial
begin
	//rst
	#0
	clk_a = 1'b0;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b1;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b0;
	
	//wait clk_b domain rst done
	repeat (1000) @(negedge clk_a);
	
	//gen test signal
	for(m=0;m<10;m=m+1)
	begin
		enable_a =1'b1;
		@(negedge clk_a);
		enable_a =1'b0;

		repeat (3) @(negedge clk_a);//modify!!!
	end
	
	repeat (1000) @(negedge clk_a);
	$stop;
	
end

initial
begin
	//rst
	#0
	clk_b = 1'b0;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b1;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b0;
	
end


sync SYNC0
(
.clk_a (clk_a),
.rst_a (rst_a),
.enable_a (enable_a),

.clk_b (clk_b),
.rst_b (rst_b),
.enable_b (enable_b)
);

endmodule

/********* EOF *************/

下面是修改后的前仿波形,如下图所示:

从中可以看出,clk_a时钟域一共产生了10次enab_a,但是clk_b时钟域只detect到了4次。

数字集成电路设计-10-关于采用两级触发器实现双时钟域信号同步方法的分析与验证_第2张图片


5,前仿验证C

上面是从快时钟同步到慢时钟,如果快时钟域的信号产生频率太高的话,就会造成丢失。

那么如果是从慢时钟同步到快时钟呢?

将clk_a和clk_b的时钟周期互换,每隔一个clk_a就产生一个enable_a信号,结果如何呢?

修改后的sync_tb.v:


/*
* file name		:sync_tb.v
* author		:Rill
* date			:2014-04-12
*/

`timescale 1ns/1ns


module sync_tb;

reg clk_a;
reg rst_a;
reg enable_a;

reg clk_b;
reg rst_b;
wire enable_b;


parameter CLK_A_PERIOD = 30;
parameter CLK_B_PERIOD = 10;

//gen clk_a
always #(CLK_A_PERIOD/2) clk_a = ~clk_a;

//gen clk_b
always #(CLK_B_PERIOD/2) clk_b = ~clk_b;


//gen clk_a domain test pattern
integer m;

initial
begin
	//rst
	#0
	clk_a = 1'b0;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b1;
	repeat (10) @(negedge clk_a);
	rst_a = 1'b0;
	
	//wait clk_b domain rst done
	repeat (1000) @(negedge clk_a);
	
	//gen test signal
	for(m=0;m<10;m=m+1)
	begin
		enable_a =1'b1;
		@(negedge clk_a);
		enable_a =1'b0;

		repeat (1) @(negedge clk_a);//modify!!!
	end
	
	repeat (1000) @(negedge clk_a);
	$stop;
	
end

initial
begin
	//rst
	#0
	clk_b = 1'b0;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b1;
	repeat (10) @(posedge clk_b);
	rst_b = 1'b0;
	
end


sync SYNC0
(
.clk_a (clk_a),
.rst_a (rst_a),
.enable_a (enable_a),

.clk_b (clk_b),
.rst_b (rst_b),
.enable_b (enable_b)
);

endmodule

/********* EOF *************/


下面是修改后的前仿波形:

从中可以看出,即使慢时钟域每隔一个周期产生一个enable_a信号(一共10次),快时钟域也不会丢失(也检测到10次)。

数字集成电路设计-10-关于采用两级触发器实现双时钟域信号同步方法的分析与验证_第3张图片



6,小结

本小节我们对采用两级触发器实现双时钟域同步的问题进行了简单分析,其实除了快慢时钟的问题,还有亚稳态的问题,由于是前仿,没有延迟信息,所以看不到亚稳态情况。


你可能感兴趣的:(数字集成电路设计-10-关于采用两级触发器实现双时钟域信号同步方法的分析与验证)