The 'stable and 'quiet attributes are predefined in the VHDL standard and work on signal objects. We can use these special features in simulation to check that a signal’s value remains untouched for a given period.

Here’s the syntax for using ‘stable and ‘quiet:

signal_name'stable(time_value)

signal_name'quiet(time_value)

Both attributes require a time type value as an argument and return a boolean value.

They will return a derived signal with the value true if the base signal has remained quiet or stable for the given period before the current simulation time. Otherwise, the attributes return false.

The difference between the two is that stable only considers changes to the signal’s value, while quiet, on the other hand, senses all assignments, also when it doesn’t result in a new signal value.

When called sequentially, the attributes work like functions. We can, for example, print the instantaneous evaluation of 'stable by using the report statement like this:

report "stable: " & boolean'image(sig'stable(50 ns));

If you are using ModelSim, the output to the simulator console will be:

# ** Note: stable: true
#    Time: 0 ps  Iteration: 0  Instance: /stable_quiet_tb

Stable and quiet as derived signals

As we have seen, you can use these attributes like regular impure function calls, and they will appear to return a boolean value. But in reality, they are producing a derived signal and not an instantaneous value.

When we type sig'stable(50 ns), this whole expression is like a new signal of boolean type, continuously updated with the result of the called attribute.

To demonstrate this, I’ve created a testbench with a std_logic signal named sig and two boolean signals:

architecture sim of stable_quiet_tb is

  signal sig : std_logic := '0';

  signal stable_50_ns : boolean;
  signal quiet_50_ns : boolean;

begin

  stable_50_ns <= sig'stable(50 ns);
  quiet_50_ns <= sig'quiet(50 ns);

Then, using two concurrent processes (shown above), we assign to the two boolean signals using the 'stable and 'quiet attributes. For both attributes, we use 50 nanoseconds as the stable/quiet time.

Furthermore, we’ll use a regular process to create some events on sig to trigger changes in the two derived signals named stable_50_ns and quiet_50_ns.

The initial value of sig is '0', but after 100 nanoseconds, we change it to '1', as shown below. Then, after 100 more, we set it to '1' again. But the signal’s value won’t change because it’s already '1' at this point. And finally, we change sig back to '0' before pausing the process indefinitely.

  process
  begin
    
    wait for 100 ns;
    sig <= '1';
    wait for 100 ns;
    sig <= '1'; -- Assign the same value again
    wait for 100 ns;
    sig <= '0';
    wait for 100 ns;

    wait;
  end process;

When we simulate the code above in ModelSim, it will produce this waveform:

ModelSim waveform showing VHDL 'stable and 'quiet attributes working

The first event on sig happens at 100 ns when it changes from '0' to '1'. The two derived signals change from true to false on the rising edge of the base signal because it’s no longer stable nor quiet. As we can see from the marked waveform section below, they remain false for 50 ns before transitioning to true again.

ModelSim waveform showing VHDL 'stable and 'quiet attributes at 100 ns

When the value of the base signal changes, 'stable and 'quiet behave identically, but 'quiet can sense assignments that we cannot observe in the waveform.

For example, at the 200 ns mark shown below. We can’t see any change on sig, but quiet_50_ns goes to false. That’s because, in the code, we assigned the same value of '1' once again to the signal. While 'stable ignores that, 'quiet senses the assignment and becomes false for 50 ns.

ModelSim waveform showing VHDL 'stable and 'quiet attributes at 200 ns

Finally, we make sig change from '1' back to '0', and we can see both attributes reacting:

ModelSim waveform showing VHDL 'stable and 'quiet attributes at 300 ns

Using the attributes within assert statements

Another way to use 'quiet and 'stable is within VHDL assert statements. Typical use cases would be to verify pulse widths or setup and hold times. Check out my tutorial video below to see how we can use 'stable to check some assumptions about the timing on asynchronous interfaces!

In the tutorial video, I used a simplified verification component to spy on the device under test (DUT). You can see the two signals of interest coming in through the entity of this simulation module below.

entity spi_verification is
  port (
    cs : in std_logic;
    sclk : in std_logic
  );
end spi_verification;

The first thing I checked was that the falling edge of sclk wasn’t too close to the prior falling edge of cs. This timing requirement comes from the datasheet of the analog-to-digital converter (ADC) I used in the SPI master VHDLwhiz Membership tutorial mentioned in the video.

It’s easy to check if a signal remained stable before a different one changed. Simply wait for the edge of the triggering signal and then run the assert on the other one, as shown in the process below.

  process
  begin
    wait falling_edge(sclk);

    assert cs'stable(10 ns)
      report "Falling SCLK too close to falling Chip Select"
      severity failure;
    
  end process;

But we can’t use this method to check the pulse width of a discrete signal. That’s because if we wait for an event on signal A before checking, A'stable(T) will always return false. The signal is changing at that delta cycle and, thus, has remained stable for zero time.

A workaround is to create an additional helper signal delayed by one delta cycle from the signal of interest, as shown below. The concurrent assignment from cs to cs_delta copies the value in zero simulation time but creates an additional delta cycle delay on the copy. That’s just what we need!

architecture sim of spi_verification is

  signal cs_delta : std_logic;

begin

  cs_delta <= cs;

Finally, we can create the pulse width checker process, which triggers on the falling edge of cs. But instead of calling 'stable on cs itself, we use cs_delta to check that the pulse width requirement is honored:

  process
  begin
    wait until falling_edge(cs);

    assert cs_delta'stable(10 ns)
      report "Chip Select pulse too short"
      severity failure;
    
  end process;

The two signals are identical concerning simulation time, but cs_delta lags cs by one delta cycle. Therefore, it will still not have changed when the program executes the assertion line, and we can use it to verify the stability of the original cs signal.

Final thoughts

I consider 'stable to be one of the most helpful signal attributes for testbenches, and I often use it to verify interfaces’ timing. When dealing with complex hardware protocols, I usually move the checking code to a separate module like the verification component in this article.

On the other hand, I hardly ever use the 'quiet attribute. I feel that I’m making too many assumptions about the implementation of the DUT by using it. It doesn’t matter how many times you assign the same value to a signal in an RTL module. After synthesis, there are no signal assignments anyway, just logic.

Feel free to comment below if you know a good use case for 'quiet! 🙂

Author: Jonas Julian Jensen

I’m from Norway, but I live in Bangkok, Thailand. Before I started VHDLwhiz, I worked as an FPGA engineer in the defense industry. I earned my master’s degree in informatics at the University of Oslo.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.