Filters an asynchronous reset signal so it can assert immediately and
asynchronously, but can only release synchronously with a receiving clock
(after 2 or 3 (plus EXTRA_DEPTH) cycles of latency), which avoids
metastability issues should the reset release too close to the receiving
clock edge.
Set the RESET_ACTIVE_STATE parameter to the value of your reset when
active (e.g.: 0 for active-low reset).
If you are running near the speed or temperature limits of your silicon,
you may have to add some extra synchronizer stages with the EXTRA_DEPTH
parameter. Consult your datasheets.
This design combines the internals of a CDC Bit
Synchronizer and of a Register with
Asynchronous Reset (please see those modules for
more background). We cannot instantiate those modules here since we have
to apply some attributes directly to reg values to make a good
synchronizer, and the CDC Synchronizer module does not have a reset.
Much like a CDC Synchronizer, you can only synchronize a given reset bit in
one place, as two synchronizers fed by the same input may have different
output latencies due to metastability. Also, feed the reset_in input
directly from a register, as combinational logic glitches could trigger
spurious resets. Finally, you cannot place any of the registers into I/O
registers: they are too far away from the main logic fabric to make a good
synchronizer.
Make sure no logic under asynchronous reset feeds logic which is not also under reset, else metastability may happen in the latter logic since the reset assertion is not synchronous to the clock. If you need reset assertion to be synchronous, use a CDC Synchronizer instead.
Also, note that introducing an asynchronous reset, even with synchronized release, may prevent any register retiming from ocurring in connected logic. Check your CAD tool results, and favour the plain CDC Synchronizer instead.
3 + EXTRA_DEPTH cycles before I/O transactions can begin.
`default_nettype none
module Reset_Synchronizer
#(
parameter EXTRA_DEPTH = 0,
parameter RESET_ACTIVE_STATE = 2 // Must be 0 (active-low) or 1 (active-high)
)
(
input wire receiving_clock,
// Necessary since the reset may be also used synchronously in the originating clock domain.
// verilator lint_off SYNCASYNCNET
input wire reset_in,
// verilator lint_on SYNCASYNCNET
output reg reset_out
);
initial begin
reset_out = ~RESET_ACTIVE_STATE [0];
end
The minimum valid synchronizer depth is 2. Add more stages if the design requires it. This usually happens near the highest operating frequencies. Consult your device datasheets.
localparam DEPTH = 2 + EXTRA_DEPTH;
For Vivado, we must specify that the synchronizer registers should be placed close together (see: UG912), and to show up as part of MTBF reports.
For Quartus, specify that these register must not be optimized (e.g. moved into the input register of a DSP or BRAM) and to mark them as composing a synchronizer (and so be placed close together).
In both cases, we also specify that the registers must not be placed in I/O register locations.
// Vivado
(* IOB = "false" *)
(* ASYNC_REG = "TRUE" *)
// Quartus
(* useioff = 0 *)
(* PRESERVE *)
(* altera_attribute = "-name SYNCHRONIZER_IDENTIFICATION \"FORCED IF ASYNCHRONOUS\"" *)
reg sync_reg [DEPTH-1:0];
integer i;
initial begin
for(i=0; i < DEPTH; i=i+1) begin
sync_reg [i] = ~RESET_ACTIVE_STATE [0];
end
end
Now, depending on the reset active state, we instantiate one of two cases, distinguished only by the active edge of the reset signal. (It's grotesque to have such code duplication, but it's the only way.)
When reset_in is asserted (as specified in RESET_ACTIVE_STATE),
asynchronously place all synchronizer registers in reset, which immediately
asserts reset_out. Then, when reset_in is released, the synchronizer
will synchronously release reset_out after DEPTH or DEPTH + 1 cycles,
depending on the metastability of the first sync_reg register.
Finally, if RESET_ACTIVE_STATE is given a value other than 0 or 1, try to
instantiate a non-existent module to force synthesis or simulation to fail
immediately, with the instance name as the error message. It's ugly, but
CAD tools usually ignore $display() and $finish() system functions
during synthesis.
We must have this failsafe, else an invalid RESET_ACTIVE_STATE parameter
could leave reset_out always inactive, causing hard-to-find, intermittent
bugs in the logic dependent on reset_out. Note the use of the identity
operator (===) instead of the equality operator (==), so a parameter
containing an X value does not implicitly match zero/false.
generate
if (RESET_ACTIVE_STATE === 0) begin
always @(posedge receiving_clock, negedge reset_in) begin
if (reset_in == RESET_ACTIVE_STATE [0]) begin
for(i = 0; i < DEPTH; i = i+1) begin
sync_reg [i] <= RESET_ACTIVE_STATE [0];
end
end
else begin
sync_reg [0] <= ~RESET_ACTIVE_STATE [0];
for(i = 1; i < DEPTH; i = i+1) begin
sync_reg [i] <= sync_reg [i-1];
end
end
end
end
else if (RESET_ACTIVE_STATE === 1) begin
always @(posedge receiving_clock, posedge reset_in) begin
if (reset_in == RESET_ACTIVE_STATE [0]) begin
for(i = 0; i < DEPTH; i = i+1) begin
sync_reg [i] <= RESET_ACTIVE_STATE [0];
end
end
else begin
sync_reg [0] <= ~RESET_ACTIVE_STATE [0];
for(i = 1; i < DEPTH; i = i+1) begin
sync_reg [i] <= sync_reg [i-1];
end
end
end
end
else begin
// verilator lint_off DECLFILENAME
NonExistentModuleForErrorChecking ERROR_RESET_ACTIVE_STATE_MUST_BE_0_OR_1 ();
// verilator lint_on DECLFILENAME
end
endgenerate
always @(*) begin
reset_out = sync_reg [DEPTH-1];
end
endmodule