aboutsummaryrefslogtreecommitdiff
path: root/rtl/src/verilog/cdc_bus_pulse.v
blob: 104bfa525663c0060d62c5a3717d7535c5a39910 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
`timescale 1ns / 1ps

module cdc_bus_pulse
	(
		src_clk, src_din, src_req,
		dst_clk, dst_dout, dst_pulse
	);
	
		/* This module is based on design suggested on page 27 of an article titled
		   "Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog"			
			by Clifford E. Cummings (Sunburst Design, Inc.)
		*/
		
		//
		// Parameters
		//
	parameter	DATA_WIDTH	= 32;		// width of data bus
	
	
		//
		// Ports
		//
	input		wire							src_clk;		// source domain clock
	input		wire	[DATA_WIDTH-1:0]	src_din;		// data from source clock domain
	input		wire							src_req;		// start transfer pulse from source clock domain
	
	input		wire							dst_clk;		// destination domain clock
	output	wire	[DATA_WIDTH-1:0]	dst_dout;	// data to destination clock domain
	output	wire							dst_pulse;	// transfer done pulse to destination clock domain
	
	
		//
		// Source Side Registers
		//
	reg							src_ff		= 1'b0;						// transfer request flag
	reg	[DATA_WIDTH-1:0]	src_latch	= {DATA_WIDTH{1'bX}};	// source data buffer
	
	
		//
		// Source Request Handler
		//
	always @(posedge src_clk)
		//
		if (src_req) begin				// transfer request pulse?
			src_ff		<= ~src_ff;			// toggle transfer request flag...
			src_latch	<= src_din;			// ... and capture data in source buffer
		end
				
		
		//
		// Source -> Destination Flag Sync Logic
		//
		
	/* ISE may decide to infer SRL here, so we explicitly instantiate slice registers. */
		
	wire	flag_sync_first;		// first FF output
	wire	flag_sync_second;		// second FF output
	wire	flag_sync_third;		// third FF output
	wire	flag_sync_pulse;		// flag toggle detector output
	
	FDCE ff_sync_first
	(
		.C		(dst_clk),
		.D		(src_ff),				// capture flag from another clock domain
		.Q		(flag_sync_first),	// metastability can occur here
		.CLR	(1'b0),
		.CE	(1'b1)
	);
	FDCE ff_sync_second
	(
		.C		(dst_clk),
		.D		(flag_sync_first),	// synchronize captured flag to remove metastability
		.Q		(flag_sync_second),	// and pass it to another flip-flop
		.CLR	(1'b0),
		.CE	(1'b1)
	);	
	FDCE ff_sync_third
	(
		.C		(dst_clk),
		.D		(flag_sync_second),	// delay synchronized flag in another flip-flip, because we need
		.Q		(flag_sync_third),	// two synchronized flag values (current and delayed) to detect its change
		.CLR	(1'b0),
		.CE	(1'b1)
	);

		// when delayed flag value differs from its current value, it was changed 
		// by the source side, so there must have been a transfer request
	assign flag_sync_pulse = flag_sync_second ^ flag_sync_third;
	
	
		//
		// Destination Side Registers
		//
	reg							dst_pulse_reg	= 1'b0;						// transfer done flag
	reg	[DATA_WIDTH-1:0]	dst_latch		= {DATA_WIDTH{1'bX}};	// destination data buffer
	
	assign dst_pulse	= dst_pulse_reg;
	assign dst_dout	= dst_latch;
	
		//
		// Destination Request Handler
		//
	always @(posedge dst_clk) begin
		//
		dst_pulse_reg <= flag_sync_pulse;					// generate pulse if flag change was detected
		//
		if (flag_sync_pulse) dst_latch <= src_latch;		// by the time destination side receives synchronized
		//																// flag value, data should be stable, we can safely
		//																// capture and store it in the destination buffer
		//
	end
		

endmodule