// ----------------------------------------------------------------------- // // 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