Module resync

This document contains technical documentation for the resync module. To browse the source code, please visit the repository on GitHub.

This module contains a large set of VHDL entities for clock domain crossing (CDC) of data in an FPGA. Everything in this module is properly and carefully constrained, and has a thought-out structure to ensure stable operation.

resync_counter.vhd

View source code on GitHub.

component resync_counter is
  generic (
    width : positive;
    default_value   : u_unsigned;
    pipeline_output : boolean;
    assert_false_on_counter_jumps : boolean
  );
  port (
    clk_in : in std_ulogic;
    counter_in : in u_unsigned;
    --# {{}}
    clk_out : in std_ulogic;
    counter_out : out u_unsigned
  );
end component;

Synchronize a counter value between two domains using Gray-coded values. Converts the binary input counter word to Gray code, resynchronizes it to the output clock domain with an async_reg chain, and converts it back to a binary number.

../../_images/resync_counter_transparent.png

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.

Note

This entity has a scoped constraint file that must be used. See the scoped_constraints folder for the file with the same name.

Warning

This entity assumes that the input counter value only increments and decrements in steps of one. Erroneous values can appear on the output if this is not followed.

See the corresponding constraint file and this article for information about timing constraints and how this CDC topology is made reliable.

Resource utilization

This entity has netlist builds set up with automatic size checkers in module_resync.py. The following table lists the resource utilization for the entity, depending on generic configuration.

Resource utilization for resync_counter netlist builds.

Generics

Total LUTs

FFs

width = 16

23

48

resync_cycles.vhd

View source code on GitHub.

component resync_cycles is
  generic (
    counter_width : positive;
    active_level : std_ulogic
  );
  port (
    clk_in : in std_ulogic;
    data_in : in std_ulogic;
    --# {{}}
    clk_out : in std_ulogic;
    data_out : out std_ulogic
  );
end component;

Resynchronizes a bit, so that the output bit is asserted as many clock cycles as the input bit.

Note

This entity instantiates resync_counter.vhd which has a scoped constraint file that must be used.

This module counts each clk_in cycle the input bit is asserted. The counter is resynchronized to clk_out, and used as a reference to know how many clk_out cycles the output bit should be asserted. The module may fail when clk_out is slower than clk_in and the input is asserted many cycles in a row. An assertion is made to check for this case.

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.

resync_level.vhd

View source code on GitHub.

component resync_level is
  generic (
    enable_input_register : boolean;
    default_value : std_ulogic
  );
  port (
    clk_in : in std_ulogic;
    data_in : in std_ulogic;
    --# {{}}
    clk_out : in std_ulogic;
    data_out : out std_ulogic
  );
end component;

Resync a single bit from one clock domain to another, using two async_reg registers.

../../_images/resync_level_transparent.png

The two registers will be placed in the same slice, in order to maximize metastability recovery, which minimizes mean time between failure (MTBF). This enables proper resynchronization of semi-static “level”-type signals without meta stability on rising/falling edges.

Note

This entity has a scoped constraint file that must be used. See the scoped_constraints folder for the file with the same name.

Warning

This entity works only for semi-static “level”-type input signals. This entity can not handle “pulse”-type signals. Pulses can be missed and single-cycle pulse behavior will not work.

See the corresponding constraint file and this article for information about timing constraints and how this CDC topology is made reliable.

Deterministic latency

If you want a deterministic latency through this resync block, via a set_max_delay constraint, the clk_in port must be assigned to the clock that drives the input data. If it is not, a simple set_false_path constraint will be used and the latency can be arbitrary, depending on the placer/router.

Input register

There is an option to include a register on the input side before the async_reg flip-flop chain. This option is to prevent sampling of data when the input is in a transient “glitch” state, which can occur if it is driven by a LUT as opposed to a flip-flop. If the input is already driven by a flip-flop, you can safely set the generic to false in order to save resources. Note that this is a separate issue from meta-stability; they can happen independently of each other. When this option is enabled, the clk_in port must be driven with the correct clock.

resync_level_on_signal.vhd

View source code on GitHub.

component resync_level_on_signal is
  generic (
    default_value : std_ulogic
  );
  port (
    data_in : in std_ulogic;
   --# {{}}
   clk_out : in std_ulogic;
   sample_value : in std_ulogic;
   data_out : out std_ulogic
  );
end component;

Sample a bit from one clock domain to another.

Note

This entity has a scoped constraint file that must be used. See the scoped_constraints folder for the file with the same name.

This entity does not utilize any meta stability protection. It is up to the user to ensure that data_in is stable when sample_value is asserted.

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.

resync_pulse.vhd

View source code on GitHub.

component resync_pulse is
  generic (
    assert_false_on_pulse_overload : boolean
  );
  port (
    clk_in : in std_ulogic;
    pulse_in : in std_ulogic;
    --# {{}}
    clk_out : in std_ulogic;
    pulse_out : out std_ulogic
  );
end component;

A robust way of resyncing a pulse signal from one clock domain to another.

Note

This entity instantiates resync_level.vhd which has a scoped constraint file that must be used.

This entity features a feedback input gating which makes it robust in all configurations. Without input gating, if multiple pulses arrive close to each other, pulse overload will occur and some, or even all of them, can be missed and not arrive on the output. With input gating, if multiple pulses arrive one and only one will arrive on the output.

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.

resync_slv_level.vhd

View source code on GitHub.

component resync_slv_level is
  generic (
    width : positive;
    enable_input_register : boolean;
    default_value : std_ulogic_vector
  );
  port (
    clk_in : in std_ulogic;
    data_in : in std_ulogic_vector;
    --# {{}}
    clk_out : in std_ulogic;
    data_out : out std_ulogic_vector
  );
end component;

Resync a vector from one clock domain to another.

Note

This entity instantiates resync_level.vhd which has a scoped constraint file that must be used.

This simple vector resync mechanism does not guarantee any coherency between the bits. There might be a large skew between different bits. It will also not be able to handle pulses in the input data, but can instead only handle semi-static “level”-type signals.

It does however have meta-stability protection. See resync_level.vhd for details about constraining and usage of the enable_input_register generic.

resync_slv_level_coherent.vhd

View source code on GitHub.

component resync_slv_level_coherent is
  generic (
    width : positive;
    default_value : std_ulogic_vector
  );
  port (
    clk_in : in std_ulogic;
    data_in : in std_ulogic_vector;
    --# {{}}
    clk_out : in std_ulogic;
    data_out : out std_ulogic_vector
  );
end component;

Resynchronize a data vector from one clock domain to another. Unlike e.g. resync_slv_level.vhd, this entity contains a mechanism that guarantees bit coherency.

Note

This entity has a scoped constraint file that must be used. See the scoped_constraints folder for the file with the same name.

This entity is great for resynchronizing e.g. a control/status register or a counter value, which are scenarios where bit coherency is crucial. It will not be able to handle pulses in the input data, it is very likely that pulses will be missed. Hence the “level” in the name.

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.

A level signal is rotated around between input and output side, with three registers in each direction. The level toggles for each roundtrip, and data is sampled on each side upon a level transition. This ensures that data is sampled on the output side only when we know that the sampled input data is stable. Conversely, input data is only sampled when we know that data has been sampled on the output in a stable fashion.

Latency and resource utilization

The latency is less than or equal to

3 * period(clk_in) + 3 * period(clk_out)

This is also the sampling period of the signal. As such this resync is not suitable for signals that change quickly. It is instead typically used for e.g. monotonic counters, slow moving status words, and other data where the different bits are correlated.

The LUT utilization is always 3. The FF utilization increases linearly at a rate of 2 * width.

Compared to resync_counter.vhd this entity has lower LUT and FF usage in all scenarios. It does however have higher latency.

Another way of achieving the same functionality is to use a shallow asynchronous_fifo.vhd with write_valid and read_ready statically set to 1. This entity will however have lower LUT usage. FF usage is lower up to around width 32 where this entity will consume more FF. Latency is about the same for both.

Resource utilization

This entity has netlist builds set up with automatic size checkers in module_resync.py. The following table lists the resource utilization for the entity, depending on generic configuration.

Resource utilization for resync_slv_level_coherent netlist builds.

Generics

Total LUTs

FFs

width = 16

3

38

resync_slv_level_on_signal.vhd

View source code on GitHub.

component resync_slv_level_on_signal is
  generic (
    width : positive;
    default_value : std_ulogic_vector
  );
  port (
    data_in : in std_ulogic_vector;
   --# {{}}
   clk_out : in std_ulogic;
   sample_value : in std_ulogic;
   data_out : out std_ulogic_vector
  );
end component;

Sample a vector from one clock domain to another.

Note

This entity instantiates resync_level_on_signal.vhd which has a scoped constraint file that must be used.

This modules does not utilize any meta stability protection. It is up to the user to ensure that data_in is stable when sample_value is asserted. It will not be able to handle pulses in the data and does not feature any bit coherency. Hence it can only be used with semi-static “level”-type signals.

Note that unlike e.g. resync_level.vhd, it is safe to drive the input of this entity with LUTs as well as FFs.