From cb224284b10c3500950248cfe43e7a57ce538b2c Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov Date: Fri, 11 Jun 2021 17:39:06 +0300 Subject: [PATCH] Added synchronizer modules --- cdc_data.sv | 58 +++++++++++++++++++++++++++++++ cdc_strobe.sv | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 cdc_data.sv create mode 100644 cdc_strobe.sv diff --git a/cdc_data.sv b/cdc_data.sv new file mode 100644 index 0000000..075fa4b --- /dev/null +++ b/cdc_data.sv @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------------- +// cdc_data.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//-------------------------------------------------------------------------------- + +// INFO -------------------------------------------------------------------------------- +// Standard two-stage synchronizer +// CDC stands for "clock data crossing" +// +// In fact, this madule is just a wrapper for dalay.sv +// +// Don`t forget to write false_path constraints for all your synchronizers +// The best way to do it - is to mark all synchonizer delay.sv instances +// with "_SYNC_ATTR" suffix. After that, just one constraint is required: +// +// For Quartus: +// set_false_path -to [get_registers {*delay:*_SYNC_ATTR*|data[1]*}] +// +// For Vivado: +// set_false_path -to [get_cells -hier -filter {NAME =~ *_SYNC_ATTR/data_reg[1]*}] +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +cdc_data CD [31:0] ( + .clk( {32{clk}} ), + .nrst( {32{1'b1}} ), + .d( ext_data[31:0] ), + .q( synchronized_data[31:0] ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module cdc_data( + input clk, + input nrst, + input d, + output q +); + +delay #( + .LENGTH( 2 ), + .WIDTH( 1 ), + .TYPE( "CELLS" ), + .REGISTER_OUTPUTS( "FALSE" ) +) data_SYNC_ATTR ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + + .in( d ), + .out( q ) +); + +endmodule + diff --git a/cdc_strobe.sv b/cdc_strobe.sv new file mode 100644 index 0000000..bcfafb3 --- /dev/null +++ b/cdc_strobe.sv @@ -0,0 +1,94 @@ +//-------------------------------------------------------------------------------- +// cdc_strobe.sv +// Konstantin Pavlov, pavlovconst@gmail.com +//-------------------------------------------------------------------------------- + +// INFO -------------------------------------------------------------------------------- +// Clock crossing setup for single-pulse strobes +// CDC stands for "clock data crossing" +// +// This is a simplest form of strobe CDC circuit. Good enough for rare single +// strobe events. This module does NOT support close-standing strobes, +// placed in adjacent lock cycles +// +// Don`t forget to write false_path constraints for all your synchronizers +// The best way to do it - is to mark all synchonizer delay.sv instances +// with "_SYNC_ATTR" suffix. After that, just one constraint is required: +// +// For Quartus: +// set_false_path -to [get_registers {*delay:*_SYNC_ATTR*|data[1]*}] +// +// For Vivado: +// set_false_path -to [get_cells -hier -filter {NAME =~ *_SYNC_ATTR/data_reg[1]*}] +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +cdc_strobe CS [7:0] ( + .clk1_i( {8{clk1}} ), + .nrst1_i( {8{1'b1}} ), + .strb1_i( input_strobes[7:0] ), + + .clk2_i( {8{clk2}} ), + .strb2_o( output_strobes[7:0] ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module cdc_strobe #( parameter + PRE_STRETCH( 2 ) // number of cycles to stretch input strobe +)( + input clk1_i, // clock domain 1 clock + input nrst1_i, // clock domain 1 reset (inversed) + input strb1_i, // clock domain 1 strobe + + input clk2_i, // clock domain 2 clock + output strb2_o // clock domain 2 strobe +); + +// This signal should be at_least(!!!) one clk2_i period long +// Preliminary stretching is usually nessesary, unless you are crossing +// to essentialy high-frequency clock clk2_i, that is > 2*clk1_i +logic strb1_stretched; + +pulse_stretch #( + .WIDTH( PRE_STRETCH ), + .USE_CNTR( 0 ) +) stretch_strb1 ( + .clk( clk1_i ), + .nrst( nrst1_i ), + .in( strb1_i ), + .out( strb1_stretched ) +); + +// This is a synchronized signal in clk2_i clock domain, +// but no guarantee, that it is one-cycle-high +logic strb2_stretched; + +delay #( + .LENGTH( 2 ), + .WIDTH( 1 ), + .TYPE( "CELLS" ), + .REGISTER_OUTPUTS( "FALSE" ) +) delay_strb1_SYNC_ATTR ( + .clk( clk2_i ), + .nrst( 1'b1 ), + .ena( 1'b1 ), + + .in( strb1_stretched ), + .out( strb2_stretched ) +); + +edge_detect ed_strb2 ( + .clk( clk2_i ), + .nrst( 1'b1 ), + .in( strb2_stretched ), + .rising( strb2_o ), // and now the signal is definitely one-cycle-high + .falling( ), + .both( ) +); + +endmodule +