These are predefined attributes listed in the VHDL language reference manual.
An attribute in VHDL is a meta property that’s attached to a type or object. We can use them to get information about the item that goes beyond the value it carries. Some attributes are only for simulation, while others are also useful for avoiding hard-coded constants in synthesizable code.
Note: This list is still incomplete. I’m adding sections regularly and will remove this notice when finished.
Active
When applied to a signal s, the active attribute works like a function call, returning true
if s is active during the current simulation cycle and false
if not.
If s is a composite signal, the whole signal is considered active if one of the subelements are.
The term active means that a signal assignment, force
, or release
is scheduled for the current simulation cycle, even if it’s the same value as the signal already had.
signal s : std_logic := '0';
begin
process
begin
s <= '0';
wait;
end process;
process
begin
report "Active: " & boolean'image(s'active);
wait for 0 ns;
report "Active: " & boolean'image(s'active);
wait for 0 ns;
report "Active: " & boolean'image(s'active);
wait;
end process;
# ** Note: Active: false
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: Active: true
# Time: 0 ns Iteration: 1 Instance: /test_tb
# ** Note: Active: false
# Time: 0 ns Iteration: 2 Instance: /test_tb
Ascending
a‘ascending[(n)]
The ascending attributes can be applied to scalar types or objects of them, including subtypes and aliases. It returns a boolean
value that will be true
if p has ascending range or false
if it’s descending.
When called on an array a, the optional n parameter specifies which index range to check. It defaults to 1 when omitted, which is the only legal value for one-dimensional arrays anyway. But for multi-dimensional arrays, an n > 1 will check the direction of a subdimension.
signal s : std_logic_vector(7 downto 0);
type t1 is array (0 to 9) of bit;
type t2 is array (0 to 9, 7 downto 0) of bit;
begin
process
begin
report "s'ascending: " & boolean'image(s'ascending);
report "t1'ascending: " & boolean'image(t1'ascending);
report "t2'ascending: " & boolean'image(t2'ascending);
report "t2'ascending(1): " & boolean'image(t2'ascending(1));
report "t2'ascending(2): " & boolean'image(t2'ascending(2));
report "integer'ascending: " & boolean'image(integer'ascending);
report "std_logic'ascending: " & boolean'image(std_logic'ascending);
wait;
end process;
# ** Note: s'ascending: false
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: t1'ascending: true
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: t2'ascending: true
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: t2'ascending(1): true
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: t2'ascending(2): false
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: integer'ascending: true
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: std_logic'ascending: true
# Time: 0 ns Iteration: 0 Instance: /test_tb
Base
t‘base
When applied to an object, type, or subtype p, the base attribute returns the underlying type from which it originates. If there are multiple layers of subtypes, you get the root type that’s not a subtype.
You cannot use this attribute standalone. It must appear in conjunction with a second attribute, for example, p‘base‘right.
Language revisions before VHDL-2019 only support calling ‘base on types and subtypes (t).
subtype hex_type is integer range 0 to 15;
subtype dec_type is hex_type range 0 to 9;
signal p : dec_type;
begin
process
begin
report "hex_type'base'right: " & integer'image(hex_type'base'right);
report "dec_type'base'right: " & integer'image(dec_type'base'right);
report "p'subtype'base'right: " & integer'image(p'subtype'base'right);
-- (In VHDL-2019, you can do: p'base'right)
wait;
end process;
# ** Note: hex_type'base'right: 2147483647
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: dec_type'base'right: 2147483647
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: p'subtype'base'right: 2147483647
# Time: 0 ns Iteration: 0 Instance: /test_tb
Converse
VHDL-2019 adds mode views to interfaces and the converse attribute along with them. When called on a mode view m, converse returns a derived view with the modes from m transformed as follows:
in | → | out |
out | → | in |
inout | → | inout |
buffer | → | in |
mode view (m) | → | m‘converse |
Consider this record and corresponding mode view:
type spi_if is record
sclk : std_logic;
mosi : std_logic;
miso : std_logic;
end record;
view spi_master_if of spi_if is
sclk : out;
mosi : out;
miso : in;
end view spi_master_if;
We could create an identical view with reversed data directions like this:
view spi_slave_if of spi_if is
sclk : in;
mosi : in;
miso : out;
end view spi_slave_if;
Or we can achieve the same by using the converse attribute:
view spi_slave_if is spi_master_if'converse;
Delayed
When applied to a signal s, the delayed attribute produces a signal that’s a copy of s, but with the transitions delayed by t time units. The derived signal will have a delay of one delta cycle if the optional t argument is omitted.
This example creates a signal b
that’s delayed by one nanosecond from a
.
signal a : std_logic := '1';
signal b : std_logic;
begin
a <= not a after 5 ns;
b <= a'delayed(1 ns);
The code above produces the following waveform in the VHDL simulator:
This example shows what happens if we don’t specify a time unit t.
signal a : std_logic := '1';
signal b : std_logic;
begin
a <= not a after 5 ns;
b <= a'delayed;
As we can see from the waveform below, signal b
lags two delta cycles behind signal a
. That’s because the derived signal is one delta cycle behind a
, and when we copy it to b
, it adds an additional delta delay.
Designated_subtype
When called on an access type or file type, object p, the designated_subtype attribute returns the subtype that the object references.
subtype byte_type is integer range 0 to 255;
type ptr is access byte_type;
-- This signal's type will be byte_type
signal sig : ptr'designated_subtype;
Driving
The driving attribute acts as a function returning a boolean
value when applied to a signal s. You can only use this attribute from within a process or equivalent concurrent statement/subprogram. It will return true
if the process is driving the signal and false
otherwise.
If the s signal belongs to a port, it must have one of the following modes: inout
, out
, or buffer
.
When used in a regular signal, the driving attribute always returns true
. That’s because a process controlling a signal will always be driving it. However, that’s not always the case when it comes to guarded signals.
The demo below uses the driving attribute to print information about which process drives a value onto the common s
signal bus.
Thanks to Bert Molenkamp for submitting this example to VHDLwhiz!
architecture sim of test_tb is
signal i1, i2, en1, en2 : std_logic := '0';
signal s : std_logic bus; -- Guarded signal
begin
P1 : process(i1, en1)
begin
if en1 = '1' then s <= i1; else s <= null; end if;
if s'driving then
report "P1 is driving s <= " & std_logic'image(s'driving_value);
else
report "P1 is not driving s";
end if;
end process;
P2 : process(i2, en2)
begin
if en2 = '1' then s <= i2; else s <= null; end if;
if s'driving then
report "P2 is driving s <= " & std_logic'image(s'driving_value);
else
report "P2 is not driving s";
end if;
end process;
TEST_PROC : process
begin
i1 <= '1'; i2 <= '0';
wait for 10 ns; en1 <= '1'; en2 <= '0';
wait for 10 ns; en1 <= '0'; en2 <= '1';
wait for 10 ns; en1 <= '1'; en2 <= '1';
wait for 10 ns;
end process;
end architecture;
The listing below shows the Questa simulator’s console printout after we simulate. The value of s'driving
immediately reflects the latest signal assignment to s
. We don’t have to wait until the next delta cycle for it to update after we assign s <= i1
or s <= null
.
# ** Note: P2 is not driving s
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: P1 is not driving s
# Time: 0 ns Iteration: 0 Instance: /test_tb
# ** Note: P1 is not driving s
# Time: 0 ns Iteration: 1 Instance: /test_tb
# ** Note: P1 is driving s <= '1'
# Time: 10 ns Iteration: 1 Instance: /test_tb
# ** Note: P1 is not driving s
# Time: 20 ns Iteration: 1 Instance: /test_tb
# ** Note: P2 is driving s <= '0'
# Time: 20 ns Iteration: 1 Instance: /test_tb
# ** Note: P1 is driving s <= '1'
# Time: 30 ns Iteration: 1 Instance: /test_tb
The waveform below shows the signals during simulation. As expected, s
ends up in driver conflict when we enable both driving processes with conflicting values.
Driving_value
When applied to a signal, s, the driving_value attribute works like a function call, returning the value that the enclosing process is driving onto the signal.
You can only use this attribute within a process or subprogram.
Calling driving_value on a signal that the process isn’t driving produces a runtime error. You can use the driving attribute to check if a process currently drives a signal.
architecture sim of test_tb is
signal s : std_logic;
begin
PROC_A : process
begin
s <= '1';
wait for 0 ns;
report "PROC_A drives " & std_logic'image(s'driving_value) &
", but s is " & std_logic'image(s);
wait;
end process;
PROC_B : process
begin
s <= '0';
wait for 0 ns;
report "PROC_B drives " & std_logic'image(s'driving_value) &
", but s is " & std_logic'image(s);
wait;
end process;
end architecture;
As we can see from the output below, the driving value may differ from the actual value of a resolved or guarded signal.
# ** Note: PROC_B drives '0', but s is 'X'
# Time: 0 ns Iteration: 1 Instance: /test_tb
# ** Note: PROC_A drives '1', but s is 'X'
# Time: 0 ns Iteration: 1 Instance: /test_tb
Element
The element attribute can only be applied to an array type or a signal or variable of an array type. When applied to such an object, a, it returns the subtype of the array elements.
Thus, you can use 'element
to declare new objects, as shown in the example below.
-- The std_logic_vector type is an array of std_logic
signal vec : std_logic_vector(3 downto 0) := "0101";
-- sig1 becomes a std_logic type signal
signal sig1 : vec'element;
type arr_type is array (natural range <>) of integer;
-- sig2 becomes an integer type signal
signal sig2 : arr_type'element;
Event
When applied to a signal s, the event attribute works like a function call, returning true
if an event occurred on s during the current simulation cycle and false
if not.
The term event in the context of VHDL simply means that a signal changes value. A signal assignment only causes an event if the value changes. Assigning the same value again won’t cause an event.
architecture sim of events_tb is
signal sig1, sig2, sig3 : std_logic := '0';
begin
sig1 <= '1' after 10 ns;
sig2 <= sig1;
sig3 <= sig2;
process(sig1, sig2, sig3)
begin
if sig1'event then
report "sig1 changed";
end if;
if sig2'event then
report "sig2 changed";
end if;
if sig3'event then
report "sig3 changed";
end if;
end process;
end architecture;
The example code above has three signals that all change at 10 ns simulation time but in different delta cycles due to the cascading concurrent assignments. The process is sensitive to changes on either signal, and we’re using 'event
to determine which signal changed and print a message to the simulator’s transcript window.
As we can see from the printout below, the process prints a message every time a signal changes. All at 10 ns, but the iteration numbers are incrementing for each signal.
# ** Note: sig1 changed
# Time: 10 ns Iteration: 0 Instance: /events_tb
# ** Note: sig2 changed
# Time: 10 ns Iteration: 1 Instance: /events_tb
# ** Note: sig3 changed
# Time: 10 ns Iteration: 2 Instance: /events_tb
We can also observe this visually by expanding delta cycles at the 10 ns mark in the Questa simulator’s waveform viewer:
High
This attribute works like a function returning the upper bound (highest index) of the object (a) it’s attached to. It may be used on any object whose range is constrained.
If the object is a multi-dimensional array, you can specify the dimension to check using the optional n parameter. It defaults to n = 1, which is also the only option for one-dimensional vectors.
subtype int_t is integer range 0 to 15;
subtype real_t is real range 1.0 to 3.14;
signal slv_dt : std_logic_vector(7 downto 0);
signal slv_to : std_logic_vector(0 to 7);
type arr_2d_t is array (3 downto 0, 7 downto 0) of std_logic;
signal arr_2d : arr_2d_t;
begin
process
begin
report LF &
-- Unconstrained integer
"integer'high: " & integer'image(integer'high) & LF &
"int_t'high: " & integer'image(int_t'high) & LF &
"real_t'high: " & real'image(real_t'high) & LF &
"slv_dt'high: " & integer'image(slv_dt'high) & LF &
"slv_to'high: " & integer'image(slv_to'high) & LF &
"arr_2d(1)'high: " & integer'image(arr_2d'high(1)) & LF &
"arr_2d(2)'high: " & integer'image(arr_2d'high(2));
wait;
end process;
The output shows that the high attribute returns the index with the highest value regardless of ascending/descending range direction:
# integer'high: 2147483647
# int_t'high: 15
# real_t'high: 3.140000e+00
# slv_dt'high: 7
# slv_to'high: 7
# arr_2d(1)'high: 3
# arr_2d(2)'high: 7
Left
This attribute works like a function returning the left bound (leftmost index) of the object it’s attached to. It may be used on any object whose range is constrained.
If the object is a multi-dimensional array, you can specify the dimension to check using the optional n parameter. It defaults to n = 1, which is also the only option for one-dimensional vectors.
subtype int_t is integer range 0 to 15;
subtype real_t is real range 1.0 to 3.14;
signal slv_dt : std_logic_vector(7 downto 0);
signal slv_to : std_logic_vector(0 to 7);
type arr_2d_t is array (3 downto 0, 7 downto 0) of std_logic;
signal arr_2d : arr_2d_t;
begin
process
begin
report LF &
-- Unconstrained integer
"integer'left: " & integer'image(integer'left) & LF &
"int_t'left: " & integer'image(int_t'left) & LF &
"real_t'left: " & real'image(real_t'left) & LF &
"slv_dt'left: " & integer'image(slv_dt'left) & LF &
"slv_to'left: " & integer'image(slv_to'left) & LF &
"arr_2d(1)'left: " & integer'image(arr_2d'left(1)) & LF &
"arr_2d(2)'left: " & integer'image(arr_2d'left(2));
wait;
end process;
The output shows that the left attribute returns the leftmost value from the type/object’s definition regardless of ascending/descending range direction:
# integer'left: -2147483648
# int_t'left: 0
# real_t'left: 1.000000e+00
# slv_dt'left: 7
# slv_to'left: 0
# arr_2d(1)'left: 3
# arr_2d(2)'left: 7
Low
This attribute works like a function returning the lower bound (lowest index) of the object (a) it’s attached to. It may be used on any object whose range is constrained.
If the object is a multi-dimensional array, you can specify the dimension to check using the optional n parameter. It defaults to n = 1, which is also the only option for one-dimensional vectors.
subtype int_t is integer range 0 to 15;
subtype real_t is real range 1.0 to 3.14;
signal slv_dt : std_logic_vector(7 downto 0);
signal slv_to : std_logic_vector(0 to 7);
type arr_2d_t is array (3 downto 0, 7 downto 0) of std_logic;
signal arr_2d : arr_2d_t;
begin
process
begin
report LF &
-- Unconstrained integer
"integer'low: " & integer'image(integer'low) & LF &
"int_t'low: " & integer'image(int_t'low) & LF &
"real_t'low: " & real'image(real_t'low) & LF &
"slv_dt'low: " & integer'image(slv_dt'low) & LF &
"slv_to'low: " & integer'image(slv_to'low) & LF &
"arr_2d(1)'low: " & integer'image(arr_2d'low(1)) & LF &
"arr_2d(2)'low: " & integer'image(arr_2d'low(2));
wait;
end process;
The output shows that the low attribute returns the index with the lowest value regardless of ascending/descending range direction:
# integer'low: -2147483648
# int_t'low: 0
# real_t'low: 1.000000e+00
# slv_dt'low: 0
# slv_to'low: 0
# arr_2d(1)'low: 0
# arr_2d(2)'low: 0
Quiet
When applied to a signal s, the quiet attribute produces a derived signal of boolean
type. The resulting signal’s value is true
if s was quiet for t time before the current simulation time. Otherwise, it has the value false
.
The time value defaults to 0 ns
if you omit the optional t parameter.
A signal is quiet if no assignments happen within the given period. When a value is scheduled or forced onto the signal, it is no longer quiet, even if it’s the same value as the signal already had.
signal quiet_50_ns : boolean;
begin
quiet_50_ns <= sig'quiet(50 ns);
Right
This attribute works like a function returning the right bound (rightmost index) of the object it’s attached to. It may be used on any object whose range is constrained.
If the object is a multi-dimensional array, you can specify the dimension to check using the optional n parameter. It defaults to n = 1, which is also the only option for one-dimensional vectors.
subtype int_t is integer range 0 to 15;
subtype real_t is real range 1.0 to 3.14;
signal slv_dt : std_logic_vector(7 downto 0);
signal slv_to : std_logic_vector(0 to 7);
type arr_2d_t is array (3 downto 0, 7 downto 0) of std_logic;
signal arr_2d : arr_2d_t;
begin
process
begin
report LF &
-- Unconstrained integer
"integer'right: " & integer'image(integer'right) & LF &
"int_t'right: " & integer'image(int_t'right) & LF &
"real_t'right: " & real'image(real_t'right) & LF &
"slv_dt'right: " & integer'image(slv_dt'right) & LF &
"slv_to'right: " & integer'image(slv_to'right) & LF &
"arr_2d(1)'right: " & integer'image(arr_2d'right(1)) & LF &
"arr_2d(2)'right: " & integer'image(arr_2d'right(2));
wait;
end process;
The output shows that the right attribute returns the rightmost value from the type/object’s definition regardless of ascending/descending range direction:
# integer'right: 2147483647
# int_t'right: 15
# real_t'right: 3.140000e+00
# slv_dt'right: 0
# slv_to'right: 7
# arr_2d(1)'right: 0
# arr_2d(2)'right: 0
Stable
When applied to a signal s, the stable attribute produces a derived signal of boolean
type. The resulting signal’s value is false
if there were events on s for t time before the current simulation time. If there were no events, it is true
.
The time value defaults to 0 ns
if you omit the optional t parameter.
An event is when a signal’s value changes. Assigning the same value that the signal already has doesn’t trigger events.
signal stable_50_ns : boolean;
begin
stable_50_ns <= sig'stable(50 ns);
process
begin
wait until falling_edge(sclk);
assert cs'stable(10 ns)
report "Falling SCLK too close to falling Chip Select"
severity failure;
end process;