aboutsummaryrefslogblamecommitdiff
path: root/rtl/src/verilog/cdc_bus_pulse.v
blob: 631506c791c2553903f94165d3619431337a5b9d (plain) (tree)






























































                                                                                                                                                        
                                                                                                                 

                                                                                       
                              



                                          
                                                                                                            

                                                                                           
                              



                                          
                                                                                                                        

                                                                                                                                  
                              






























                                                                                                                                                                                                   
`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