aboutsummaryrefslogtreecommitdiffstats
path: root/sync.v
blob: c720b49905fd904117953714c9b67d612709a046 (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
// -----------------------------------------------------------------------
//
//   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_if_asynchronous; -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 tri1 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).
   generate
      if (stabilized)
	(* 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;
      else
	(* altera_attribute = "-name cut on -from * ; -name sdc_statement \"set_false_path -to [get_keepers {*|synchronizer:*|d_q[*]}]\"" *)
       reg [width-1:0] d_q;
   endgenerate

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

   assign q = stage[stages-1];

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

   genvar i;
   generate
      for (i = 1; i < stages; i = i + 1)
	begin: gen_stages
	   always @(posedge reset or posedge clk)
	     if (reset)
	       stage[i] <= {width{1'b0}};
	     else if (stage_enable[i])
	       stage[i] <= (i > 1) ? stage[i-1] : d_q;
	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.
//
// If bypass is true, then the result is available one cycle earlier
// (latency = 1 cycle) at the cost of extra combinatorial logic.
//
module stabilizer(reset, clk, enable, bypass, d, q);
   parameter width  = 1;	// Minimum useful = 2

   input reset;
   input clk;
   input enable;		// if 0, hold output value constant
   input bypass;		// Enable bypass network (probably a constant)
   input [width-1:0] d;
   output [width-1:0] q;

   reg [width-1:0]    d_q;		// One cycle delayed input
   reg [width-1:0]    q_q;		// Stored output

   wire		      d_stable = ~|(d ^ d_q);
   wire		      d_valid  = d_stable & enable;
   wire		      d_bypass = d_valid & bypass;

   assign q = d_bypass ? d_q : q_q;

   always @(posedge reset or posedge clk)
     if (reset)
       d_q <= {width{1'b0}};
     else
       d_q <= d;

   always @(posedge reset or 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
   localparam stabilized = stabilize || !(width > 1);
   localparam 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;

   synchronizer #(.width(width), .stages(stages), .stabilized(stabilized))
     synchronizer(.d(d), .q(sync_q), .enable(stabilize || enable),
		  .reset(reset), .clk(clk));

   generate
      if (stabilizer)
	stabilizer #(.width(width))
          stabilizer(.d(sync_q), .q(q), .enable(enable),
		     .bypass(bypass ? 1'b1 : 1'b0),
		     .reset(reset), .clk(clk));
      else
	assign q = sync_q;
   endgenerate
endmodule // synchronize