summaryrefslogtreecommitdiffstats
path: root/sync.v
blob: a4060c19b589c1936296dfb8be79e3a6b5947dbb (plain)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// -----------------------------------------------------------------------
//
//   Copyright 2016 H. Peter Anvin - All Rights Reserved
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//   Boston MA 02110-1301, USA; either version 2 of the License, or
//   (at your option) any later version; incorporated herein by reference.
//
// -----------------------------------------------------------------------

//
// Synchronize data from different clock domains.  This is pretty trivial
// stuff in theory; most of the complexity comes from giving hints
// to the synthesizer.  Good reason to make this a parameterized module.
//

(* altera_attribute = "-name synchronizer_identification FORCED; -name auto_shift_register_recognition OFF; -name global_signal OFF" *)
module synchronizer(reset, clk, enable, d, q);
   parameter width  = 1;	// Minimum 1
   parameter stages = 2;	// Minimum 2
   parameter stabilized = 0;	// Used to disable D104 warning

   input reset;
   input clk;
   input enable;		// If 0, hold the output constant
   input [width-1:0] d;
   output [width-1:0] q;

   // Inputs to these modules are inherently asynchronous, that is the
   // whole point.  Let the timing analyzer know that is okay.
   // We define stage[0] separately as d_q to enable separate attributes.
   // If the stabilizer is enabled, also there is no need to warn about
   // bus skew (design rule D104).
   // I used a generate if else statement here, but it seems to have
   // led to the completely wrong result...
   (* altera_attribute = "-name disable_da_rule \"D104\" ; -name cut on -from * ; -name sdc_statement \"set_false_path -to [get_keepers {*|synchronizer:*|d_q[*]}]\"" *)
     reg [width-1:0] d_q = {width{1'b0}};

   reg [width-1:0] stage[1:stages-1];
   wire [stages-1:0] stage_enable = {enable, {(stages-1){1'b1}}};

   assign q = stage[stages-1];

   integer s;
   initial
     for (s = 1; s < stages; s = s + 1)
       stage[s] = {width{1'b0}};
   
   always @(posedge clk)
     if (reset)
       d_q <= {width{1'b0}};
     else if (stage_enable[0])
       d_q <= d;

   always @(posedge clk)
     if (reset)
       stage[1] <= {width{1'b0}};
     else if (stage_enable[1])
       stage[1] <= d_q;

   genvar i;
   generate
      for (i = 2; i < stages; i = i + 1)
	begin: gen_stages
	   always @(posedge clk)
	     if (reset)
	       stage[i] <= {width{1'b0}};
	     else if (stage_enable[i])
		 stage[i] <= stage[i-1];
	end
   endgenerate
endmodule

//
// Stabilize the result of crossing a bus across asynchronous clock domains.
// This is an inherently unsafe operation, because there is no guarantee
// that even when synchronized their values will be latched in the same
// cycle.  However, if the outputs change slowly *and* an extra 2
// target cycle of latency is acceptable, then we can compare the outputs
// with the previous ones and save the result only if doing so is safe.
//
// This makes no sense to use with width = 1, so the bus width should
// always be specified.  This is intended to be connected to the output
// of a synchronizer, it does not itself include the synchronizer chain.
//
module stabilizer(reset, clk, enable, d, q);
   parameter width  = 1;	// Minimum useful = 2

   input reset;
   input clk;
   input enable;		// if 0, hold output value constant
   input [width-1:0] d;
   output [width-1:0] q;

   reg [width-1:0] d_q = {width{1'b0}}; // One cycle delayed input
   reg [width-1:0] q_q = {width{1'b0}}; // Latched output

   assign q = q_q;
   
   wire	 d_stable = ~|(d ^ d_q);
   wire	 d_valid  = d_stable & enable;
   
   always @(posedge clk)
     if (reset)
       d_q <= {width{1'b0}};
     else
       d_q <= d;

   always @(posedge clk)
     if (reset)
       q_q <= {width{1'b0}};
     else
       begin
	  if (d_valid)
	    q_q <= d_q;
       end
endmodule

//
// General synchronizer with optional stabilizer
//
module synchronize(reset, clk, enable, d, q);
   parameter width  = 1;	// Minimum 1
   parameter stages = 2;	// Minimum 2
   parameter stabilize = 1;	// Add stabilizer if necessary
   parameter bypass = 0;	// Add stabilizer bypass

   // "stabilized" indicates if our output is stabilized one way or another.
   // "stabilizer" indicates if we need to instantiate a physical stabilizer.
   parameter stabilized = stabilize || (width <= 1);
   parameter stabilizer = stabilize && (width > 1);

   input tri0 reset;
   input clk;
   input tri1 enable;		// If 0, hold the output constant
   input [width-1:0] d;
   output [width-1:0] q;

   wire [width-1:0] sync_q;
   wire sync_enable = stabilizer ? 1'b1 : enable;
   
   reg s_reset = 1'b1;
   always @(posedge clk)
     s_reset <= reset;
   
   synchronizer #(.width(width), .stages(stages), .stabilized(stabilized))
     synchronizer(.d(d), .q(sync_q),
		.enable(sync_enable),
		.reset(s_reset), .clk(clk));

   generate
      if (stabilizer)
	stabilizer #(.width(width))
          stabilizer(.d(sync_q), .q(q), .enable(enable),
		     .reset(s_reset), .clk(clk));
      else
	assign q = sync_q;
   endgenerate
endmodule // synchronize