|
1 |
| -//-------------------------------------------------------------------------------- |
| 1 | +//------------------------------------------------------------------------------ |
2 | 2 | // cdc_strobe.sv
|
3 | 3 | // Konstantin Pavlov, [email protected]
|
4 |
| -//-------------------------------------------------------------------------------- |
| 4 | +//------------------------------------------------------------------------------ |
5 | 5 |
|
6 |
| -// INFO -------------------------------------------------------------------------------- |
| 6 | +// INFO ------------------------------------------------------------------------ |
7 | 7 | // Clock crossing setup for single-pulse strobes
|
8 |
| -// CDC stands for "clock data crossing" |
| 8 | +// Strobes could trigger transfers of almost-static data between clock doamins |
9 | 9 | //
|
10 |
| -// This is a simplest form of strobe CDC circuit. Good enough for rare single |
11 |
| -// strobe events. This module does NOT support close-standing strobes, |
12 |
| -// placed in adjacent lock cycles |
| 10 | +// - Maximum input strobe rate is every second clk1 clock cycle |
13 | 11 | //
|
14 |
| -// Don`t forget to write false_path constraints for all your synchronizers |
15 |
| -// The best way to do it - is to mark all synchonizer delay.sv instances |
16 |
| -// with "_SYNC_ATTR" suffix. After that, just one constraint is required: |
| 12 | +// - Input strobe may span several clock cycles, but it will be considered one |
| 13 | +// event and only one single-cycle strobe will be generated to the output |
| 14 | +// |
| 15 | +// - When clk2 is essentially less than clk1 it is possible that strb2 will |
| 16 | +// remain HIGH for several consecutive clk2 cycles. On the output every |
| 17 | +// HIGH cycle should be considered as a separate strobe event |
| 18 | +// |
| 19 | +// - When clk2 is essentially less than clk1 - output strobes could even |
| 20 | +// "overlap" or miss. In this case, please restrict input strobe event rate |
| 21 | +// |
| 22 | +// - cdc_strobe module features a 2 clock cycles propagation delay |
| 23 | +// |
| 24 | +// |
| 25 | +// |
| 26 | +// False_path constraint is required from all nodes with "_FP_ATTR" suffix |
17 | 27 | //
|
18 | 28 | // For Quartus:
|
19 |
| -// set_false_path -to [get_registers {*delay:*_SYNC_ATTR*|data[1]*}] |
| 29 | +// set_false_path -from [get_registers {*_FP_ATTR*}] |
20 | 30 | //
|
21 | 31 | // For Vivado:
|
22 |
| -// set_false_path -to [get_cells -hier -filter {NAME =~ *_SYNC_ATTR/data_reg[1]*}] |
| 32 | +// set_false_path -from [get_cells -hier -filter {NAME =~ *_FP_ATTR*}] |
23 | 33 | //
|
24 | 34 |
|
25 | 35 |
|
26 | 36 | /* --- INSTANTIATION TEMPLATE BEGIN ---
|
27 | 37 |
|
28 |
| -cdc_strobe CS [7:0] ( |
29 |
| - .clk1_i( {8{clk1}} ), |
30 |
| - .nrst1_i( {8{1'b1}} ), |
31 |
| - .strb1_i( input_strobes[7:0] ), |
| 38 | +cdc_strobe_v2 cdc_wr_req ( |
| 39 | + .arst( 1'b0 ), |
| 40 | +
|
| 41 | + .clk1( clk1 ), |
| 42 | + .nrst1( 1'b1 ), |
| 43 | + .strb1( wr_req_clk1 ), |
32 | 44 |
|
33 |
| - .clk2_i( {8{clk2}} ), |
34 |
| - .strb2_o( output_strobes[7:0] ) |
| 45 | + .clk2( {8{clk2}} ), |
| 46 | + .nrst2( 1'b1 ), |
| 47 | + .strb2( wr_req_clk2 ) |
35 | 48 | );
|
36 | 49 |
|
37 | 50 | --- INSTANTIATION TEMPLATE END ---*/
|
38 | 51 |
|
39 | 52 |
|
40 |
| -module cdc_strobe #( parameter |
41 |
| - PRE_STRETCH( 2 ) // number of cycles to stretch input strobe |
42 |
| -)( |
43 |
| - input clk1_i, // clock domain 1 clock |
44 |
| - input nrst1_i, // clock domain 1 reset (inversed) |
45 |
| - input strb1_i, // clock domain 1 strobe |
| 53 | +module cdc_strobe ( |
| 54 | + input arst, // async reset |
46 | 55 |
|
47 |
| - input clk2_i, // clock domain 2 clock |
48 |
| - output strb2_o // clock domain 2 strobe |
49 |
| -); |
| 56 | + input clk1, // clock domain 1 clock |
| 57 | + input nrst1, // clock domain 1 reset (inversed) |
| 58 | + input strb1, // clock domain 1 strobe |
50 | 59 |
|
51 |
| -// This signal should be at_least(!!!) one clk2_i period long |
52 |
| -// Preliminary stretching is usually nessesary, unless you are crossing |
53 |
| -// to essentialy high-frequency clock clk2_i, that is > 2*clk1_i |
54 |
| -logic strb1_stretched; |
55 |
| - |
56 |
| -pulse_stretch #( |
57 |
| - .WIDTH( PRE_STRETCH ), |
58 |
| - .USE_CNTR( 0 ) |
59 |
| -) stretch_strb1 ( |
60 |
| - .clk( clk1_i ), |
61 |
| - .nrst( nrst1_i ), |
62 |
| - .in( strb1_i ), |
63 |
| - .out( strb1_stretched ) |
| 60 | + input clk2, // clock domain 2 clock |
| 61 | + input nrst2, // clock domain 2 reset (inversed) |
| 62 | + output strb2 // clock domain 2 strobe |
64 | 63 | );
|
65 | 64 |
|
66 |
| -// This is a synchronized signal in clk2_i clock domain, |
67 |
| -// but no guarantee, that it is one-cycle-high |
68 |
| -logic strb2_stretched; |
69 |
| - |
70 |
| -delay #( |
71 |
| - .LENGTH( 2 ), |
72 |
| - .WIDTH( 1 ), |
73 |
| - .TYPE( "CELLS" ), |
74 |
| - .REGISTER_OUTPUTS( "FALSE" ) |
75 |
| -) delay_strb1_SYNC_ATTR ( |
76 |
| - .clk( clk2_i ), |
77 |
| - .nrst( 1'b1 ), |
78 |
| - .ena( 1'b1 ), |
79 |
| - |
80 |
| - .in( strb1_stretched ), |
81 |
| - .out( strb2_stretched ) |
82 |
| -); |
| 65 | + // buffering strb1 |
| 66 | + logic strb1_b = 1'b0; |
| 67 | + always @(posedge clk1 or posedge arst) begin |
| 68 | + if( arst || ~nrst1 ) begin |
| 69 | + strb1_b <= '0; |
| 70 | + end else begin |
| 71 | + strb1_b <= strb1; |
| 72 | + end |
| 73 | + end |
| 74 | + |
| 75 | + // strb1 edge detector |
| 76 | + // prevents secondary strobe generation in case strb1 is not one-cycle-high |
| 77 | + logic strb1_ed; |
| 78 | + assign strb1_ed = (~strb1_b && strb1) && ~arst; |
| 79 | + |
| 80 | + |
| 81 | + // 2 bit gray counter, it must NEVER be reset |
| 82 | + logic [1:0] gc_FP_ATTR = '0; |
| 83 | + always @(posedge clk1) begin |
| 84 | + if( strb1_ed ) begin |
| 85 | + gc_FP_ATTR[1:0] <= {gc_FP_ATTR[0],~gc_FP_ATTR[1]}; // incrementing counter |
| 86 | + end |
| 87 | + end |
| 88 | + |
| 89 | + // buffering counter value on clk2 |
| 90 | + // gray counter does not need a synchronizer |
| 91 | + logic [1:0][1:0] gc_b = '0; |
| 92 | + always @(posedge clk2 or posedge arst) begin |
| 93 | + if( arst || ~nrst2 ) begin |
| 94 | + gc_b[1:0] <= {2{gc_FP_ATTR[1:0]}}; |
| 95 | + end else begin |
| 96 | + gc_b[1:0] <= {gc_b[0],gc_FP_ATTR[1:0]}; // shifting left |
| 97 | + end |
| 98 | + end |
| 99 | + |
| 100 | + // gray_bit_b edge detector |
| 101 | + assign strb2 = (gc_b[1][1:0] != gc_b[0][1:0] ) && ~arst; |
83 | 102 |
|
84 |
| -edge_detect ed_strb2 ( |
85 |
| - .clk( clk2_i ), |
86 |
| - .nrst( 1'b1 ), |
87 |
| - .in( strb2_stretched ), |
88 |
| - .rising( strb2_o ), // and now the signal is definitely one-cycle-high |
89 |
| - .falling( ), |
90 |
| - .both( ) |
91 |
| -); |
92 | 103 |
|
93 | 104 | endmodule
|
94 | 105 |
|
0 commit comments