【Verilog】跨时钟域设计Clock Domain Crossing (CDC) Design(MCP formulation without feedback )

跨时钟域设计

MCP formulation(不带反馈) [1]

MCP公式是指将不同步的数据发送到与同步控制信号配对的接收时钟域。数据和控制信号同时发送,允许数据在目标寄存器的输入端进行设置,同时控制信号在到达目标寄存器的负载输入端之前同步两个接收时钟周期。

优点:
1.发送时钟域不需要计算要在时钟域之间发送的适当脉冲宽度。
2.发送时钟域只需要将enable切换到接收时钟域,以指示数据已经传递并准备加载。使能信号不需要返回到其初始逻辑级别。
这种方法不同步地传输多个CDC信号,同时向接收时钟域传递一个同步的enable信号。在synchronized enable通过同步到达接收寄存器之前,接收时钟域不允许采样多位CDC信号。也被称为多周期路径,由于同步的数据字是直接传递给接收时钟域和举办多个接收时钟周期,允许一个启动信号同步并认识到接收时钟域允许同步的数据字改变之前。

在这里插入图片描述
图1
在图1中,d的输入在周期1中被拉高;周期4时,通过三个触发器传输q3被拉高;红虚线框内,在周期3时,q2、q3触发器的输出相反,异或可形成使能脉冲。同理,d输入在周期7拉低,周期9 时异或产生使能脉冲。
在这里插入图片描述
图2
图2为等效电路,将产生脉冲电路简化为右边的图。
在这里插入图片描述
图3
q-ouput跟随d输入所延迟的三个周期。q-output常作为反馈信号,并通过发送时钟域中的另一个同步使能作为确认信号。

本例的结构框图4:
在这里插入图片描述
图4

示例代码:
//两级寄存
module sync2FF#(
	parameter DATAWIDTH = 8
)
(
	input 						    CLK,
	input 						    RSTn,
	input      						D,
	output reg  					Q
);

reg  Q1;

always @(posedge CLK or negedge RSTn) begin
	if (!RSTn) begin
		// reset
		Q  <= 0;
		Q1 <= 0;
	end
	else begin
		Q1 <= D;
		Q  <= Q1;
	end
end

endmodule
//source domain
module src_domain#(
	parameter DATAWIDTH = 8
)
(
	input 						     CLK				,
	input 							 RSTn				,
	input 	   [ DATAWIDTH - 1 : 0 ] src_data_in 		,
	output reg [ DATAWIDTH - 1 : 0 ] src2dest_data		,
	output reg 					     src2dest_load		,
	output reg						 src_data_valid
);

reg [ DATAWIDTH - 1 : 0 ] src_data_in_reg; 

always @(posedge CLK or negedge RSTn) begin
	if (!RSTn) begin
		// reset
			src2dest_data <= 0;
			src2dest_load <= 0;
			src_data_in_reg <= 0;
		end
	else begin
		src2dest_load <= src2dest_load ^ src_data_valid;
		src_data_in_reg <= src_data_in;
		end
	if (src_data_valid) begin
			src2dest_data <= src_data_in;
		end
	end



/**********src_data_in changed signal**********/
always @(posedge CLK or negedge RSTn) begin
    
	if (!RSTn) begin
			// reset
			src_data_valid <= 0;
		end
	else if (src_data_in!=src_data_in_reg) 
		begin
	   		src_data_valid <= 1;
		end
	else 
			src_data_valid <= 0;
end

endmodule
module dest_domain#(
	parameter DATAWIDTH = 8 
)
(
	input 								CLK				,
	input 								RSTn			,
	input 		[ DATAWIDTH - 1 : 0 ] 	src2dest_data	,
	input  								src2dest_load	,
	output reg					 		dest_data_valid	,
	output reg	[ DATAWIDTH - 1 : 0 ] 	dest_data_out
);
wire dest_valid;
wire ldtoggle_sy;
reg  ldtoggle_sy_dl;

always @(posedge CLK or negedge RSTn) 
begin
	if (!RSTn) 
		begin
			dest_data_valid <= 0;
			dest_data_out <= 0;
			ldtoggle_sy_dl <= 0;
		end
	else 
		begin
			ldtoggle_sy_dl <= ldtoggle_sy;
			dest_data_valid <= dest_valid;
		end
end

assign dest_valid = ldtoggle_sy ^ ldtoggle_sy_dl;

always @(posedge CLK or negedge RSTn) 
begin
if (dest_valid)
	 	begin
			dest_data_out <= src2dest_data;
		end
end
sync2FF sync2FF_inst(	.CLK	(CLK			)		,
						.RSTn	(RSTn			)		,
						.D 		(src2dest_load	)		,	
						.Q		(ldtoggle_sy 	))		;

endmodule
//top module
module src2dest#(
	parameter DATAWIDTH = 8
)
(
	input 							src_CLK				,
	input 							dest_CLK			,
	input 							RSTn				,
	input 	[ DATAWIDTH - 1 : 0 ] 	src_data_in			,
	output 	[ DATAWIDTH - 1 : 0 ] 	dest_data_out		,
	output							src_data_valid		,
	output							dest_data_valid

);

wire [ DATAWIDTH - 1 : 0 ] src2dest_data;
wire [ DATAWIDTH - 1  :0 ] src2dest_load;

src_domain src_domain_inst(	.CLK 				(src_CLK		)	,
							.RSTn				(RSTn			)	,
							.src_data_in		(src_data_in	)	,
							.src2dest_data		(src2dest_data	)	,
							.src2dest_load		(src2dest_load	)	,
							.src_data_valid		(src_data_valid	))	;
dest_domain dest_domain_inst(.CLK 				(dest_CLK		)	,
							 .RSTn				(RSTn			)	,
							 .src2dest_data		(src2dest_data	)	,
							 .src2dest_load		(src2dest_load	)	,
							 .dest_data_valid	(dest_data_valid)	,
							 .dest_data_out		(dest_data_out	))	;

endmodule
//testbench
module src2dest_tb();
	parameter DATAWIDTH = 8;

	reg 							src_CLK			;
	reg 							dest_CLK		;
	reg 							RSTn			;
	reg 	[ DATAWIDTH - 1 : 0 ] 	src_data_in		;
	wire 	[ DATAWIDTH - 1 : 0 ] 	dest_data_out	;
	wire 							src_data_valid	;
	wire 							dest_data_valid	;


initial
 	begin
		src_CLK = 0;
		forever #20 src_CLK <= ~src_CLK;
		
	end
initial
 	begin
 		dest_CLK = 0;
 		forever #30 dest_CLK <= ~dest_CLK;
 	end
initial
 	begin
		RSTn = 0;
		#10 
		RSTn = 1;
	end

initial
begin
    src_data_in <= 0;
    #200 src_data_in <= 8'd2;
    #240 src_data_in <= 8'd20;
    #200 src_data_in <= 8'd22;
    #280 src_data_in <= 8'd11;
    #300 src_data_in <= 8'd3;
    #340 src_data_in <= 8'd6;
    #300 src_data_in <= 8'd7;
    #240 src_data_in <= 8'd8;
    #240 src_data_in <= 8'd13;
    #300 src_data_in <= 8'd24;
    #300 src_data_in <= 8'd35;
    #240 src_data_in <= 8'd17;
    #400 src_data_in <= 8'd18;
    #260 src_data_in <= 8'd21;
    #240 src_data_in <= 8'd13;
    #300 src_data_in <= 8'd25;
    #240 src_data_in <= 8'd36;
    #280 src_data_in <= 8'd47;
    #400 src_data_in <= 8'd63;
    #440 src_data_in <= 8'd32;
    #800
    $stop;
    
end

src2dest src2deat_inst( .src_CLK		(src_CLK		)	,
						.dest_CLK		(dest_CLK		)	,
						.RSTn			(RSTn			)	,
						.src_data_in	(src_data_in	)	,
						.dest_data_out	(dest_data_out	)	,
						.src_data_valid (src_data_valid	)	,		
						.dest_data_valid (dest_data_valid))	;

endmodule

在这里插入图片描述
在这里插入图片描述

总结:
跨时钟域电路设计使用广泛,一直以来都是一个重要且复杂的问题,传统的做法一般为两级寄存器或是握手,这也成为了很多问题的根源。有几篇不错的文章供大家参考阅读https://github.com/zhangzek/Clock-Domain-Crossing-Design/tree/master/doc&paper,可以花多点时间去阅读CummingsSNUG2008Boston_CDC.pdf(也就是文献[1])

本例使用MCP(Multi-Cycle Path)Formulation,多周期路径去同步,可跨时钟域同步多个位。但是,有一个问题是,源时钟域中的逻辑如何知道何时可以发送另一段数据?就需要带反馈确认来增强(MCP)同步。
以后更新带反馈的MCP

大家新年快乐!

参考文献:
[1]:Clock Domain Crossing (CDC) Design & VerificationTechniques Using SystemVerilog
[2]:Mike Stein, Paradigm Works.Crossing the abyss:asynchronous signals in a synchronous world
[3]:Clifford E. Cummings.Simulation and Synthesis Techniques for Asynchronous FIFO Design
[4]:Ran Ginosar.Metastability and Synchronizers: A Tutorial

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值