External IO and Metastability

Pages
Contributors: Alchitry, Ell C
Favorited Favorite 3

Metastability

Now that you know how to define the pinout for your design, we should get into the dangers of using external signals.

For most of your design, a signal starts and ends inside the FPGA. When this happens the tools can accurately manage the timing of this to make sure everything works as expected. However, external signals aren’t controllable and without special care can cause your entire design to behave unexpectedly.

This happens due to a phenomenon called metastability.

Metastability is when your nice digital system stops working in 0s and 1s and gets stuck somewhere in between or even oscillating between values.

This can happen when you violate specific assumptions made by the DFFs (we covered these back in the Programming an FPGA tutorial).

The main assumption is that the D input of any DFF will be stable for a certain amount of time before and after the rising edge of a clock. The time requirement before is called the setup time and the time after is called the hold time.

If the D input changes in this window, the DFF can fail to capture the value and may become unstable.

Here is a diagram illustrating this:

Image showing metastability

The first two edges of the clock have no D transitions around them so the value of D is correctly saved and output on Q. However, the last one has a transition near the rising edge so the output becomes unpredictable.

The easiest solution to this problem is to simply respect the setup and hold windows. This can be done when you use a clocked bus like SPI. However, for anything that isn’t clocked, you can’t predict or control when the signal will change.

Imagine trying to get a user to avoid 100,000,000 tiny windows per second when pushing a button. It’s impossible.

The workaround for this is chaining two or more DFFs together:

metastability with dffs


This significantly reduces the likelihood of the output of the second DFF being unstable. It does introduce a small amount of latency into the input though. It’s important to know this doesn’t solve the problem of metastability. It merely drastically reduces the chance of it being an issue. Adding more DFFs to the chain further reduces the probability of it becoming an issue but the amount quickly diminishes and two is usually plenty.

There is a component in the Component Library called pipeline that can be used to synchronize inputs. It is under the Miscellaneous category. It simply pipes an input through a parameterized number of DFFs before outputting it.

module pipeline #(
    DEPTH = 1 : DEPTH > 0 // number of stages
  )(
    input clk,  // clock
    input in,   // input
    output out  // output
  ) {

  dff pipe[DEPTH] (.clk(clk));
  var i;

  always {
    // in goes into the start of the pipe
    pipe.d[0] = in; 

    // out is the end of the pipe
    out = pipe.q[pipe.WIDTH-1]; 

    // for each intermediate state
    for (i = 1; i < DEPTH; i++)
      pipe.d[i] = pipe.q[i-1]; // copy from previous
  }
}

You may have noticed in the previous tutorials that a component called the reset_conditioner is used to condition the reset signal (who would’ve guessed?!).

This component takes care of two things. First, it synchronizes the reset button to the clock. Second, it ensures the reset signal stays high for a minimum amount of time. These two requirements are important for ensuring that the signal is clean and the entire FPGA comes out of reset at the same time.

module reset_conditioner #(
    STAGES = 4 : STAGES > 1 // number of stages
  )(
    input clk,  // clock
    input in,   // async reset
    output out  // snyc reset
  ) {

  dff stage[STAGES] (.clk(clk), .rst(in), #INIT(STAGESx{1}));

  always {
    stage.d = c{stage.q[STAGES-2:0],0};
    out = stage.q[STAGES-1];
  }
}

The way this works is clever. It uses four DFFs in a chain with the first one getting a constant 0 input on D. The raw reset signal is used to reset these four DFFs to 1. When they aren’t being forced to 1 by the reset, they will output 1 for the first four clock cycles then drop the output low to allow normal operation of the reset of the design. This means that the reset signal will be at least four clock cycles long and will end synchronized to the clock.

This design is from Xilinx’s whitepaper entitled Get Smart About Reset: Think Local, Not Global. This is a great read - in addition to discussing the design of the reset, it also talks about the cost of using a reset. Basically, if you don’t need a DFF to be reset, don’t connect the reset signal to it. It just requires extra routing resources and complicates your design. Many DFF values don’t matter when a design is reset as they will get assigned a known value almost immediately.

Multiple Clocks

Another area you can run into metastability issues is crossing clock domains. This is when your design has multiple clocks of different frequencies. You can’t simply connect the output of a DFF being clocked at 33MHz to one being clocked at 100MHz. There will be times when timing is violated and bad things happen.

To cross clock domains you have a few options. The first is to use a chain like before. This is the easiest way but isn’t suitable for multi-bit or rapidly changing signals. It is best used for single bit flags.

The more robust and complicated solution is to use an asynchronous FIFO. These are FIFOs that have read and write ports with independent clocks. You can write values to the FIFO from one clock domain and read them out from the other.

This is in the component library under Memory/Asynchronous FIFO.

These work great for getting data across clock domains but you need to ensure that you are reading data from them as fast as you are writing it so they don’t overflow.

The best way, if possible, is to just avoid multiple clocks. You can often create slower parts of your design by simply using a counter to tick when you should do something. This allows the tools to still manage all the timing requirements for you.

This isn’t always possible and when you need to cross clock domains you simply need to take a bit of extra care.