0
\$\begingroup\$

Timing, especially in sequential logic, confuses me a lot. I devised an example and drew a timing diagram and I would like to ask some question regarding it; all of them are about the same issue.

Here is the VHDL code I wrote. It does not make sense in terms of functionality, but what I focus on is the timing of it. I have a black box component (no implementation of it, I just made it up and it is only sequential, no combinational part and it only uses the rising edge of the clock in its process part) and in a dummy module I use that module. It takes 2 inputs and when enabled, one clock cycle later it sets the output and pulls high its output valid pin for one cycle.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity dummy is
Port ( clk                          : in std_logic;
       dummy_enable                 : in std_logic;
       dummy_done                   : out std_logic
       );
end dummy;

architecture Behavioral of dummy is

COMPONENT b_box
  PORT (
    clk             : IN STD_LOGIC;
    enable_b_box    : IN STD_LOGIC;
    input_1         : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
    input_2         : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
    output_valid    : IN STD_LOGIC;
    output          : OUT STD_LOGIC_VECTOR(31 DOWNTO 0)
  );
END COMPONENT;

signal enable_bb            : std_logic := '0';
signal input_1_bb           : std_logic_vector(31 downto 0); 
signal input_2_bb           : std_logic_vector(31 downto 0); 
signal output_valid_bb      : std_logic := '0';
signal output_bb            : std_logic_vector(31 downto 0);

begin

bb : b_box
  PORT MAP (
    clk => clk,
    enable_b_box => enable_bb,
    input_1 => input_1_bb,
    input_2 => input_2_bb,
    output_valid => output_valid_bb,
    output => output_bb
  );
  
process(clk)
begin
    if rising_edge(clk) then
        if dummy_enable = '1' then 
            enable_bb <= '1';
            input_1_bb <= some random number_1;
            input_2_bb <= some random number_2;
        else    
            enable_bb <= '0';
        end if;
        
        if output_valid_bb = '1' then 
            dummy_done <= '1';
        else
            dummy_done <= '0';
        end if;
    end if;
end process;
end Behavioral;

The timing diagram :

My questions:

  1. At t=1, b_box enable goes high, at the same time inputs are provided to it. Is there a risk of metastability for b_box component? Would it be better if b_box enable is kept high for 2 clock cycles as it can miss the enable signal in its process code, I think?

  2. When output valid goes high for one clock cycle, dummy done is pulled high one cycle later. Is there a risk for metastability as well for dummy done because output valid is only high between 2 rising edges (from t=2 to t=3). I believe set up time and hold time problems must occur for dummy done.

I mean one signal goes low at the same time one goes up according to the first signal. This feels wrong because at the clock rising edge when sampled, it could be sampled 1 or 0.

\$\endgroup\$

3 Answers 3

1
\$\begingroup\$

It looks like your real concern is "the output is changing the same time as it's being used as an input to somewhere else; doesn't that cause metastability?" The answer is no; certainly not if the two processes reside on the same FPGA.

To avoid metastability, you must satisfy the setup and hold time requirement of the input. Let's consider setup time a solved problem (I presume you've given thought to making sure the signal isn't transitioning right before the clock edge). As for the hold time requirement:

  • On modern FPGA the hold time requirements are extremely small; often zero.
  • More importantly, all FPGA vendors assure that the required hold time is less than the time it takes an output to switch to its new value -- if you connect two registers that are running on the same clock, they can safely be assumed to satisfy the hold time requirement.

Personally I like to draw my timing diagrams to always show that each output transitions slightly after its clock edge (i.e. nothing is truly instantaneous), and when it's drawn this way I think it makes it much more intuitive that there is no problem:

enter image description here

\$\endgroup\$
2
\$\begingroup\$

You're asking for an explanation on the fundamentals of synchronous logic. Such a tutorial is beyond the scope of this site. There is plenty of pre-written and detailed information on the internet that will explain how synchronous logic works and why it doesn't have the problems you're describing.

This is only to give you the basic understanding to then find and learn for yourself...

A rising-edge triggered D-type Flip-Flop (DFF) will latch the level on its D data input on the rising edge of its CK clock input. Shortly afterwards, that level will appear at its Q output.

The D input must be steady for a specified set-up time before the CK rising edge. The D input must be kept steady for a specified hold time after the CK rising edge.

Otherwise, the DFF may go metastable, producing an illegal Q voltage level. The DFF is a positive feedback amplifier with a very high gain so it will then quickly head up or down to a true logic voltage and resolve itself.

Since in theory, all DFFs in the circuit see their CK rising edge simultaneously, all DFFs will use the existing D levels. All DFFs will then produce a new Q level after the CK rising edge.

The mistake you're making is imagining that the DFFs produce a new Q output on the CK rising edge. They don't. They sample their D input on the CK rising edge and Q comes later. If you look into the internal design of a dual-mux FPGA/CPLD DFF, you'll see how that's possible.

Incidentally, in a real logic IC like an FPGA, CPLD or ASIC, the clock cannot reach all destinations (DFFs/registers, memories etc) simultaneously because there are routing delays. The fitter software that 'lays out' and routes their logic circuit factors the delayed clock into its timing calculations to place them correctly. Again, you can look into plenty of existing documentation on this.

\$\endgroup\$
1
\$\begingroup\$

Because of the way clocked sequential logic works, all clocked inputs driven from clocked outputs will 'just work', as long as the clock rate is low enough for the logic propagation delay, and the setup and hold times are met. If it's not, the layout/timing tool will tell you. That's why clocked logic takes nearly 100% of the logic designs in existence, it's easy to design with.

Your asynchronous 'random_number' inputs do not appear to be from outputs clocked by the same clock, and so can violate the setup and hold times. As a result, they can cause metastability in the receiving latch.

It's important to recognise what aspects of metastability are a problem, and what are not.

I mean one signal goes low at the same time one goes up according to the first signal. This feels wrong because at the clock rising edge when sampled, it could be sampled 1 or 0.

It feels wrong because it is wrong, if you're expecting a deterministic result. If you want to guarantee whether an edge is sampled as 0 or 1, then you have to ensure it changes respecting the setup and hold times of the receiving latch. That means it needs to be a synchronous signal, generated with respect to that clock.

If it's an input that changes at the same time as the clock, it doesn't matter whether it's seen as a 0 or a 1, either would be valid. An infinitesimal change in the timing would change the received value. In most cases, the 0 or 1 result would settle fairly quickly. In a few cases, metastability could occur, and the final settling time could exceed that allowed for the clocked logic to 'just work'.

When does this matter?

It matters when the received signal is read by more than one logic system, either in different places, or at different times.

For instance, let's say the random input is a processor interrupt. It really doesn't matter whether the interrupt is recognised this cycle or the next, as long as it's one or the other. But if the program counter part of the address multiplexer thinks it's an interrupt this cycle, so doesn't drive the address bus, and the interrupt vector controller thinks it is not, so also doesn't drive the address bus, then you have a crash.

There is no 100% cure for metastability, but we can get 99.999(+ a lot more 9s)% of the way there by waiting long enough for the metastability to settle. This needs at least one extra latch simply pipelined with the first. If we need to wait more time than one clock cycle, then we can add more latches in a longer pipeline. Eventually we'll have a long enough wait that our probability of failure from metastability would be once in the age if the universe, or some other sufficiently small number that we cease to worry about it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.