Skip to content

Commit 0e19b10

Browse files
committed
Updated cdc_strobe. Used gray counter under the hood
1 parent 261e056 commit 0e19b10

File tree

2 files changed

+219
-65
lines changed

2 files changed

+219
-65
lines changed

cdc_strobe.sv

+76-65
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,105 @@
1-
//--------------------------------------------------------------------------------
1+
//------------------------------------------------------------------------------
22
// cdc_strobe.sv
33
// Konstantin Pavlov, [email protected]
4-
//--------------------------------------------------------------------------------
4+
//------------------------------------------------------------------------------
55

6-
// INFO --------------------------------------------------------------------------------
6+
// INFO ------------------------------------------------------------------------
77
// 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
99
//
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
1311
//
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
1727
//
1828
// For Quartus:
19-
// set_false_path -to [get_registers {*delay:*_SYNC_ATTR*|data[1]*}]
29+
// set_false_path -from [get_registers {*_FP_ATTR*}]
2030
//
2131
// 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*}]
2333
//
2434

2535

2636
/* --- INSTANTIATION TEMPLATE BEGIN ---
2737
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 ),
3244
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 )
3548
);
3649
3750
--- INSTANTIATION TEMPLATE END ---*/
3851

3952

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
4655

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
5059

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
6463
);
6564

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;
83102

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-
);
92103

93104
endmodule
94105

cdc_strobe_tb.sv

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//------------------------------------------------------------------------------
2+
// moving_average_tb.sv
3+
// Konstantin Pavlov, [email protected]
4+
//------------------------------------------------------------------------------
5+
6+
// INFO ------------------------------------------------------------------------
7+
// testbench for cdc_strobe.sv module
8+
//
9+
10+
`timescale 1ns / 1ps
11+
12+
module cdc_strobe_tb();
13+
14+
logic clk200;
15+
initial begin
16+
#0 clk200 = 1'b0;
17+
forever
18+
#2.5 clk200 = ~clk200;
19+
end
20+
21+
// external device "asynchronous" clock
22+
logic clk33a;
23+
initial begin
24+
#0 clk33a = 1'b0;
25+
forever
26+
#7 clk33a = ~clk33a;
27+
end
28+
29+
logic clk33;
30+
//assign clk33 = clk33a;
31+
always @(*) begin
32+
clk33 = #($urandom_range(0, 2000)*1ps) clk33a;
33+
end
34+
35+
logic rst;
36+
initial begin
37+
#0 rst = 1'b0;
38+
#10.2 rst = 1'b1;
39+
#5 rst = 1'b0;
40+
//#10000;
41+
forever begin
42+
#9985 rst = ~rst;
43+
#5 rst = ~rst;
44+
end
45+
end
46+
47+
logic nrst;
48+
assign nrst = ~rst;
49+
50+
logic rst_once;
51+
initial begin
52+
#0 rst_once = 1'b0;
53+
#10.2 rst_once = 1'b1;
54+
#5 rst_once = 1'b0;
55+
end
56+
57+
logic nrst_once;
58+
assign nrst_once = ~rst_once;
59+
60+
logic [31:0] DerivedClocks;
61+
clk_divider #(
62+
.WIDTH( 32 )
63+
) cd1 (
64+
.clk( clk200 ),
65+
.nrst( nrst_once ),
66+
.ena( 1'b1 ),
67+
.out( DerivedClocks[31:0] )
68+
);
69+
70+
logic [31:0] E_DerivedClocks;
71+
edge_detect ed1[31:0] (
72+
.clk( {32{clk200}} ),
73+
.nrst( {32{nrst_once}} ),
74+
.in( DerivedClocks[31:0] ),
75+
.rising( E_DerivedClocks[31:0] ),
76+
.falling( ),
77+
.both( )
78+
);
79+
80+
logic [15:0] RandomNumber1;
81+
c_rand rng1 (
82+
.clk(clk200),
83+
.rst(rst_once),
84+
.reseed(1'b0),
85+
.seed_val(DerivedClocks[31:0]),
86+
.out( RandomNumber1[15:0] )
87+
);
88+
89+
logic start;
90+
initial begin
91+
#0 start = 1'b0;
92+
#100 start = 1'b1;
93+
#20 start = 1'b0;
94+
end
95+
96+
// Module under test ==========================================================
97+
98+
logic strb1s;
99+
assign strb1s = |RandomNumber1[2:1];
100+
101+
/*logic strb1s = 1'b0;
102+
always_ff @(posedge clk200) begin
103+
strb1s <= ~strb1s;
104+
end
105+
*/
106+
logic strb1;
107+
edge_detect ed_strb1 (
108+
.clk( clk200 ),
109+
.nrst( nrst_once ),
110+
.in( strb1s ),
111+
.rising( strb1 ),
112+
.falling( ),
113+
.both( )
114+
);
115+
116+
logic strb2;
117+
cdc_strobe M (
118+
.arst( 1'b0 ),
119+
120+
.clk1( clk200 ),
121+
.nrst1( nrst_once ),
122+
.strb1( strb1 ),
123+
124+
.clk2( clk33 ),
125+
.nrst2( 1'b1 ),
126+
.strb2( strb2 )
127+
);
128+
129+
logic [7:0] strb1_cntr = '0;
130+
always_ff @(posedge clk200) begin
131+
if( strb1 ) begin
132+
strb1_cntr[7:0] <= strb1_cntr[7:0] + 1'b1;
133+
end
134+
end
135+
136+
logic [7:0] strb2_cntr = '0;
137+
always_ff @(posedge clk33) begin
138+
if( strb2 ) begin
139+
strb2_cntr[7:0] <= strb2_cntr[7:0] + 1'b1;
140+
end
141+
end
142+
143+
endmodule

0 commit comments

Comments
 (0)