summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2016-11-04 23:10:01 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2016-11-04 23:10:01 (GMT)
commitde07bf4664cc397d67cd3433a5f88b37db5bad77 (patch)
treeb3af192a6c3fc98431afa9850467f5c06ea6c767
parentb092a9e69fabfaf7303330655a4948382d528d62 (diff)
downloadabc80-de07bf4664cc397d67cd3433a5f88b37db5bad77.zip
abc80-de07bf4664cc397d67cd3433a5f88b37db5bad77.tar.gz
abc80-de07bf4664cc397d67cd3433a5f88b37db5bad77.tar.bz2
abc80-de07bf4664cc397d67cd3433a5f88b37db5bad77.tar.xz
sync.v: include both synchronizer and stabilizer support
Add appropriate modules for both synchronizer and stabilizer, with the appropriate attribute magic for both. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--sync.v118
1 files changed, 104 insertions, 14 deletions
diff --git a/sync.v b/sync.v
index b1f3077..e4590b7 100644
--- a/sync.v
+++ b/sync.v
@@ -17,45 +17,135 @@
//
(* altera_attribute = "-name synchronizer_identification forced_if_asynchronous; -name auto_shift_register_recognition off; -name global_signal off" *)
-module synchronize(reset, clk, d, q);
+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
- (* altera_attribute = "-name cut on -from * ; -name sdc_statement \"set_false_path -to [get_keepers {synchronize:*|d_q[*] *|synchronize:*|d_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[*] *|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[*] *|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
+ else if (stage_enable[0])
d_q <= d;
- always @(posedge reset or posedge clk)
- if (reset)
- stage[1] <= {width{1'b0}};
- else
- stage[1] <= d_q;
-
genvar i;
-
generate
- for (i = 2; i < stages; i = i + 1)
+ 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
- stage[i] <= stage[i-1];
+ 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