Check Timing With VHDL Attributes

An essential part of any VHDL model written for simulation is checking for timing violations, for example, a micropocessor model should check its acknowledge input for setup and hold violations or a memory model should check the active pulse width of its chip select, output enable and write enable inputs. VHDL has several predefined signal attributes that allow you to gather extra information about a signal or variable and these can be used during simulation to perform timing checks such as pulse width violations, setup or hold violations, etc.

Attribute Returns Description
signalname'EVENT Boolean value Returns the value TRUE if there is an event on signalname in the current delta period, else FALSE.
signalname'ACTIVE Boolean value Returns the value TRUE if there is a transaction on signalname in the current delta period, else FALSE.
signalname'QUIET(t) Boolean signal Returns a signal whose value is TRUE when an activity has not occurred on signalname within the last t time units, else FALSE. If t is not specified, then a signal whose value is TRUE when an activity has not occurred on signalname within the current delta period is returned.
signalname'STABLE(t) Boolean signal Returns a signal whose value is TRUE when an event has not occurred on signalname within the last t time units, else FALSE. If t is not specified, then a signal whose value is TRUE when an event has not occurred on signalname within the current delta period is returned.
signalname'TRANSACTION Bit signal Returns a signal of type BIT which toggles whenever an event occurs on signalname.
signalname'DELAYED(t) Signal Returns a signal whose value is that of signalname delayed by t time units. If t is not specified, then a signal whose value is that of signalname delayed by 1 delta period is returned.
signalname'LAST_ACTIVE Time Returns the amount of time since the last activity on signalname.
signalname'LAST_EVENT Time Returns the amount of time since the last event on signalname.
signalname'LAST_VALUE Signal Returns the value that signalname had before the the last event onsignalname.

Now that we have defined the attributes, lets see how to use them to perform timing checks. The common thread running through all these checks is this:

  1. Wait until an event (or activity) occurs.
  2. Look back in time to see if the timing requirement was met.

Flip-flop data setup

A clocked flip-flop can be described by the following code snippet:

  1. process (clk)
  2. begin
  3.   if (clk'event and clk='1') then    -- on clock rising edge..
  4.     q <= d;              -- ..set q output..
  5.   end if;
  6. end process flip_flop;

The first use of the predefined signal attributes is seen in the IF condition which detects the rising edge of the clk signal. Applying the 'EVENT attribute to clk will return the value TRUE if there has been an event on the clk signal in the current delta time period. If the value of clk is '1' and it has changed value (the definition of an event) then this is the rising edge.

Suppose we now wish to add a check for minimum data setup violation. The data input must not vary (i.e. change state) for a certain time before the clock rising edge - the minimum setup time.

  1. process (clk)
  2. begin
  3.   if (clk'event and clk='1') then
  4.     -- check d has been stable for time tsu
  5.     if (d'stable(tsu)) then
  6.       q <= d;
  7.     else
  8.       q <= 'x';
  9.       assert false report "ff: data setup violation"
  10.       severity error;
  11.     end if;
  12.   end if;
  13. end process;

When a rising edge occurs, the second IF condition checks that the data input has been stable, i.e. no event has occurred for at least Tsu time units (Tsu would normally be defined as a constant of type TIME). If that condition is satisfied then q is assigned the value of d. If the condition is not satisfied, then q is assigned the value 'X' to indicate that its value is uncertain and an error message is generated.

Flip-flop data hold

The data input must not vary (i.e. change state) for a certain time after the clock rising edge - the minimum hold time. The technique for checking the hold time is slightly different from the check of the setup time as we have to wait until a rising edge on the clock, then wait for the minimum hold time and then check to see if d has been stable for that time. The easiest way to do this is with WAIT statements, which means that we can't have a sensitivity list.

  1. process
  2. begin
  3.   -- wait until a clock rising edge
  4.   wait until (clk'event and clk='1');
  5.   wait for (thld);
  6.   -- check d has been stable for time thld
  7.   if (d'stable(thld)) then
  8.     q <= d;
  9.   else
  10.     q <= 'x';
  11.     assert false report "ff: data hold violation"
  12.     severity error;
  13.   end if;
  14. end process;

This process will wait until the rising edge on the clk signal, then wait for the minimum hold time Thld (again, this would normally be defined as a constant). If d has been stable for time Thld, then the output q is assigned the value of d, otherwise it is assigned the value 'X' (undefined) and an error message is asserted. Basically, the above process waits a certain time (Thld) after the clk rising edge and then "looks back" to see if the data input has remained stable during that time. Another way to achieve that same thing is to delay the clk signal by time Thld and then "look back" to see if d has been stable during that time.

  1. process
  2. begin
  3.   wait until (clk'delayed(thld)'event and clk'delayed(thld)='1');
  4.   -- check d has been stable for time thld
  5.   if (d'stable(thld)) then
  6.     q <= d;
  7.   else
  8.     q <= 'x';
  9.     assert false report "ff: data hold violation"
  10.     severity error;
  11.   end if;
  12. end process;

The first WAIT statement will wait for a rising edge on a signal which is equivalent to the clk signal, but delayed by time Thld, the data is then checked to see if it has been stable for the required time. The 'DELAYED attribute returns a signal, so other attributes can be applied to it.

Pulse width

Checking the width of a pulse is quite simple - just wait until the falling edge for active high pulses or the rising edge for active low pulses and then "look back" at the pulse to check that it has been stable for the required time. The following code snippet checks that an active low static ram write strobe (ramwe) pulse meets the minimum width requirements and will report any violation:

  1. process
  2. begin
  3.   -- wait until end of write pulse
  4.   wait until (ramwe'event and ramwe='1');
  5.   -- check ramwe stable for time twe
  6.   if (not(ramwe'delayed'stable(twe))) then
  7.     assert false report "ram: min write strobe width violation"
  8.     severity error;
  9.   end if;
  10. end process;

Note that the ramwe signal is not directly tested for stability, instead the 'DELAYED attribute is used to delay it by 1 delta period, otherwise the 'STABLE attribute always returns false since there is an event on ramwe in the current delta period.