In earlier versions of VHDL, the predefined types were declared in the package standard
, specified in the VHDL Standard Language Reference Manual (LRM). Other standard types were specified in packages defined by separate IEEE standards. They included the packages math-real, math-complex, std_logic_1164, numeric_bit
, and numeric_std.
In VHDL-2008, all of these packages are included as part of the VHDL LRM, and so are now considered to be part of VHDL. VHDL also adds a number of new packages, including packages for fixed-point and floating-point numbers represented as vectors of std_ulogic
elements, and a package providing access to the simulation environment. Furthermore, VHDL-2008 adds operations to the standard packages to provide a consistent feature set across the suite. This includes a consistent set of conversion functions and the I/O operations to_string, read, write
, and so on. In this chapter, we summarize the contents of the packages.
The std_logic_1164
package defines the types std_ulogic, std_logic, std_ulogic_vector
and std_logic_vector
, as well as operations on these types. VHDL-2008 makes the following enhancements to the package:
In earlier versions, std_ulogic-vector
and std_logic_vector
were declared as separate types. That meant many of the operations had to be declared in two overloaded forms, one for each type. It also made it difficult for us to mix the two types in designs where some signals had multiple resolved sources and others had only a single source. In VHDL-2008, the std_logic_1164
package is revised to take advantage of the new features for resolving elements of composite types (see Section 3.2). The type std_logic_vector
is now a subtype of std_ulogic_vector.
Each of the array operations in the package is defined just for the std_ulogic_vector
type and can be applied to std_logic_vector
values.
The VHDL-2008 version of the package defines array/scalar logic operations for std_ulogic
and std_ulogic_vector
values, mirroring those that are predefined for bit/ bit_vector
and boolean/boolean_vector
(see Section 4.1).
The package adds logical reduction operations for std_ulogic_vector
values (see Section 4.3).
The matching relational operators (see Section 4.5) are predefined: "?=", "?/="
, "?>="., "?<"
, and "?<="
for std_ulogic
, and "?=
“and for std_ulogic_vector.
The maximum
and minimum
functions are predefined for std_ulogic
and std_ulogic_vector
(see Section 4.6).
The package defines overloaded shift operations (sll, srl, rol, ror) for std_ulogic_vector.
It does not add sra or sla, since those operations assume a numeric interpretation for a vector. Overloaded version of the arithmetic shift operations are added to numeric_std_unsigned
instead (see Section 8.3).
The condition operator is defined for std_ulogic
, allowing logical expressions that yield std_ulogic
values to be used as Boolean conditions (see Section 4.4).
The package defines a complete set of string conversion functions and text I/O procedures (see Chapter 7). Many of these operations provide the same functionality as operations in the non-standard std_logic_textio
package provided by some tool vendors. To ease the transition from that package to the standard packages, VHDL-2008 provides an empty version of std_logic_textio
package. Legacy code that included a use clause referring to std_logic_textio
to gain access to the I/O operations can continue to do so. The difference is that the operations will actually be provided by the std_logic_1164
package instead.
The package defines the strength reduction function to_01
(see Section 8.10), to be consistent with other standard packages.
All assertion messages produced by the package now start with the name of the package and the operation producing the message.
In addition to the new operations provided in std_logic_1164
, the package defines a number of aliases:
To_std_logic_vector
and to_slv
are defined as aliases for the conversion function to_stdlogicvector.
Many designers have been puzzled by the absence of underscores in to_stdlogicvector
, compared to the type name std_logic_vector
, and find the inconsistency to be annoying. The first alias name rectifies this. The second alias name, to-slv
, satisfies those who prefer shorter names, for example, to reduce typing.
Similarly, to_std_ulogic_vector
and to_sulv
are defined as aliases for the conversion function to_stdulogicvector
, and to_bit_vector
and to-bv
are defined as aliases for the conversion function to-bitvector.
Each of the numeric_bit
and numeric_std
packages defines the types unsigned
and signed
, representing binary-coded integers as vectors of bit
or std_ulogic
elements, as well as operations on these types. VHDL-2008 makes the following enhancements to the packages:
The previous version of the numeric_std
package defined unsigned
and signed
as arrays of the resolved element type std_logic.
There was no provision for unresolved elements. Since VHDL-2008
provides for resolution information to be added to elements when declaring subtypes (see Section 3.2)
, the numeric_std
package revises the way the types are defined. The package defines two array types with unresolved std_ulogic
elements, unresolved-unsigned
and unresolved-signed
, for use where a signal has only one source. The types unsigned
and signed
are now declared as subtypes with the same resolution function applied to elements as that used for std_logic.
All of the operations in numeric_std
are defined for the unresolved types, but can also be used with the resolved subtypes.
In order to reduce the amount of typing required for the unresolved types, the package defines two aliases, u_unsigned
and u_signed
, for unresolved_unsigned
and unresolved_signed
, respectively.
Both the numeric_bit
and numeric_std
packages define array/scalar addition and subtraction operations (see Section 4.2).
The packages define array/scalar logic operations (see Section 4.1)
and logical reduction operations (see Section 4.3).
The packages define maximum
and minimum
functions, which compare the numeric values represented by the operands to determine the result. Overloaded versions are also defined with one of the parameters being of type integer
or natural.
The packages define overloaded sla and sra operators (see Section 4.8).
In the case of numeric_bit
, these operators differ from the behavior of the predefined operators on arrays of bit
elements. Their behavior is more appropriate for vectors representing binary-coded numbers.
The packages define two functions, find_leftmost
and find_rightmost
, that return the index of the leftmost and rightmost element, respectively, that has a nominated value. Comparison of elements is done using the matching equality operator. Thus, for example, find_leftmost
(V, '1 '
) returns the index of the leftmost occurrence of '1'
or 'HI
in the unsigned
value V.
If there is no such element, the function returns -1.
The functions are declared as follows:
function find_leftmost ( arg : ArrayType; Y : std_ulogic) return integer; function find_rightmost ( arg : ArrayType ; Y : std_ulogic) return integer;
where ArrayType is unsigned
or signed.
The packages define overloaded matching relational operations, "?="
, "?/="
, "?>" "?<="
, and "?<="
These operations compare the numeric values represented by the operands. The numeric_std
version return an 'X'
result if any of the operand elements is a metalogical value (a value other than '0', 'I'
, 'L'
, or 'H'
).
The numeric_std
package defines a complete set of strength reduction operations, in addition to the to_01
operation that was defined in the earlier version. The package also defines the is-X
function. These operations are all summarized in Section 8.10.
The packages define a complete set of string conversion functions and text I/O procedures (see Chapter 7).
All assertion messages produced by the packages now start with the name of the package and the operation producing the message.
While VHDL provides the numeric_std
package defining the unsigned
type and associated operations, there are occasions when we would like to interpret a std_ulogic_vector
value as representing a binary-coded number. Having to convert explicitly between that type and unsigned
is inconvenient and clouds the intent of a model. VHDL-2008 alleviates this problem by providing the package numeric_std_unsigned.
It provides the same set of operations on std_ulogic_vector
values as are provided by numeric_std
for unsigned
values. Thus, we can perform arithmetic operations on std_ulogic_vector
values without including type conversions.
VHDL-2008 also provides the numeric_bit_unsigned
package. It performs an analogous purpose for bit_vector
values, providing the same operations as are provided by numeric_bit
for unsigned
values.
Many digital-signal processing applications involve mathematical operations on non-integral data. While we could use floating-point representation and hardware, that would be excessively resource-intensive in many cases. Instead, we can use a fixed-point representation, in which the radix point (analogous to the base-10 decimal point) is assumed to have a fixed position. VHDL-2008 defines a number of packages for fixed-point math that we describe in this section. The packages are all defined in the library IEEE.
For simple cases, fixed-point math amounts to integer math with scaling by a power of 2. More generally, we need to take account of rounding and overflow. The main VHDL-2008 fixed-point package, fixed_generic_pkg
, has formal generic constants so that we can choose the rounding and overflow behaviors that are most appropriate for our application. The package is defined as follows:
package fixed_generic_pkg is generic ( fixed_round_style : fixed_round_style_type := fixed_round; fixed_overflow_style : fixed_overflow_style_type := fixed_saturate; fixed_guard_bits : natural := 3; no_warning : boolean := false ); . . .
The types fixed_round_style_type
and fixed_overlow_style_type
are enumeration types defined in the package fixed_float_types.
The fixed_round_style
generic determines the rounding behavior for operations in the package: either fixed_round
, if results are to be rounded to the nearest representable value, or fixed_truncate
, if results are to be truncated toward zero to the next smallest representable value. The fixed_overflow_style
generic determines the behavior on overflow: either fixed_saturate
, if an overflowing result is to remain at the largest representable value, or fixed_wrap
, if modulo-based behavior is required. The fixed_guard_bits
generic specifies the number of extra bits of precision to use for division operations. Finally, the no_warning
generic allows suppression of warning messages on conditions such as non-matching operand lengths and occurrence of metalogical values.
Since the package has generics, we must instantiate it in order to make use of it (see Section 1.2). The IEEE
library includes an instance that has the default values for all of the generics. It is defined as:
package fixed_pkg is new IEEE.fixed_generic_pkg generic map ( fixed_round_style => IEEE.fixed_float_types.fixed_round, fixed_overflow_style => IEEE.fixed_float_types.fixed_saturate, fixed_guard_bits => 3, no_warning => false );
The package fixed_generic_pkg
(and any instance of it) defines types for unsigned and signed fixed-point representation in the form of vectors of std_ulogic
elements. The base type for unsigned representation is unresolved_ufixed
, declared as:
type unresolved_ufixed is array (integer range <>) of std_ulogic;
The name u_ufixed
is defined, for convenience, as an alias to unresolved_ufixed.
For signals with multiple sources, the type ufixed
is defined as a subtype of unresolved_ufixed
with resolved elements (see Section 3.2):
subtype ufixed is (resolved) unresolved_ufixed;
Objects of these types must have a descending (downto) index range. The whole-number part of the value is on the left of the vector, down to index 0, and the fractional part is on the right, starting at index -1. For example, given the following declaration of a fixed-point signal A:
signal A : ufixed(3 downto -3) := "0110100";
the whole-number part is A(3
downto O), and the fractional part is A(-1
downto -3).
The range of values represented is 0
to just less than 16 in steps of 0.125 (one eighth). The value represented by the default initial value is 0110.1002 = 6.510.
This example shows a number with both whole-number and fractional parts. In general, we can declare number with just a whole-number part (the right index being 0) or just a fraction part (the left index being -1). Indeed, we can declare numbers in which the radix point is completely outside the index range of the vector. For example, in the following:
variable X : ufixed(9 downto 2); variable Y : ufixed(-5 downto -14);
X is an 8-bit vector representing values in the range 0 to 1020 in steps of 4, and Y
is a 10-bit vector representing values in the range 0 to just less than 0.0625 (one sixteenth) in steps of 2-14.
The base type defined in the package for signed representation is unresolved_sfixed
, declared as:
type unresolved_sfixed is array (integer range <>) of std_ulogic;
As for the unsigned representation, there is an alias, u_sfixed
, and a subtype with resolved elements, sfixed.
Likewise, the index range for a signed value must be descending (downto), with the radix point being assumed between index 0 and index -1. The difference is that the signed type and subtypes use 2s-complement binary representation, with the leftmost bit being the sign bit. Thus, for example, the signal:
signal S : sfixed(3 downto -3);
represents values from -8 to just less than 8 in steps of 0.125.
The fixed-point math packages perform operations with full precision. This is illustrated in the following example:
signal A4_2 : ufixed(3 downto -2); signal B3_3 : ufixed(2 downto -3); signal Y5_3 : ufixed(4 downto -3); . . . Y5_3 <= A4_2 + B3_3;
The whole-number part of the addition result is one bit larger than the larger of the two operand whole-number parts. In this example, the operand whole-number parts are 4 bits and 3 bits, respectively, so the result’s whole-number part is 5 bits. The fractional part of the result is the larger fractional part of the operands. In this example, the operands’ fractional parts are 2 bits and 3 bits, respectively, so the result has a 3-bit fractional part. We summarize the operations provided by the packages and the sizes of the operation results in Section 8.8.
If we want to assign a fixed-point value to an object, one way is to use a string literal, for example:
signal A4 : ufixed(3 downto -3); . . . A4 <= "0110100"; -- string literal for 6.5
Alternatively, we can apply a conversion function, to_ufixed
or to_sfixed
, to an integer or real value. In this case, we need to specify the index range for the conversion result. There are two forms of conversion function. For the first form, we specify the left and right indices for the result, for example:
A4 <= to_ufixed(6.5, 3, -3); -- pass indices
For the second form, we provide an object whose index range is used:
A4 <= to_ufixed(6.5, A4); -- sized by A4
In this example, the only use of A4
by the to_ufixed
function is to read its left and right indices to determine the index range of the result. If A4
were an out-mode signal or variable, reading would be legal in VHDL-2008; reading of out-mode objects is a change introduced in this revision of the language (see Section 6.3).
The use of a string literal in an arithmetic expression is problematic, since the index range of such a literal is ascending (to) and starts with integer'low.
Fixed-point numbers must have descending index ranges. Instead we can use integer literals, real literals, and qualified string literals, as shown in the following examples:
subtype ufixed4_3 is ufixed(3 downto -3); signal A4, B4 : ufixed4_3; signal Y5 : ufixed (4 downto -3); -- Y5 <= A4 + "0110100"; -- illegal, Y5 <= A4 + ufixed4_3'("0110100"); Y5 <= A4 + 6.5; -- overloading with real Y5 <= A4 + 6; -- overloading with integer
In the assignment marked “illegal,” the index range of the string literal would be integer'low
to integer'low +
6. The type qualification in the next assignment avoids this problem and results in a bit-string value with index bounds taken from the subtype ufixed4_3.
We can safely apply the addition operator to this value and the operand A4
, giving a result with index range 4 down to -3.
If we need to change the size of an expression result, we can use a resize
function. As for the conversion functions, there are two forms, one in which we specify the left and right index values and the other in which we provide an object whose index range is used. For example, in the following accumulator assignment, since the addition result is one bit larger than the accumulator, we need to resize the result:
signal A4_3 : ufixed(3 downto -3); signal Y7_3 : ufixed(6 downto -3); . . . -- Y7_3 <= Y7_3 + A4_3; -- illegal, result too big Y7_3 <= resize(arg => Y7_3 + A4_3, size_res => Y7_3, overflow_style => fixed_wrap, round_style => fixed_truncate);
The overflow_style
and round_style
parameters allow us to control the way the value is processed if it cannot be represented exactly. The default values for these parameters are taken from the generics of the package. If those values are satisfactory, we can omit them in the resize
call. This is shown in the following example, which uses the form of the function specifying left and right index values for the result:
Y7_3 <= resize (arg => Y7_3 + A4_3, left_index => 7, right_index => -3);
Full-precision arithmetic can lead to some unexpected results in expressions involving multiple operators. Consider, as an example, the following declarations and assignment:
signal A4, B4, C4, D4 : ufixed(3 downto 0); signal Y6 : ufixed(5 downto 0); signal Y7A, Y7B : ufixed(6 downto 0); . . . Y6 <= (A4 + B4) + (C4 + D4);
The expression in the assignment is built as a balanced tree. Each of the additions A4 + B4
and C4 + D4
yields a 5-bit result, so the final result size is 6 bits. However, if we build the expression in a cascaded fashion, the result size is 7 bits. We can see this most clearly by explicitly parenthesizing the expression:
Y7A <= ((A4 + B4) + C4) + D4;
The addition A4 + B4
yields a 5-bit result. This added to C4
yields a 6-bit result, and the 6-bit result added to D4
yields a 7-bit result. Since addition is associative, the following unparenthesized expression yields the same 7-bit result:
Y7B <= A4 + B4 + C4 + D4;
The fixed-point math packages described in the previous section allow us to represent non-integral values with constant absolute precision over a given range. In some applications, however, we would prefer to use a floating-point representation, in which we can represent a greater dynamic range with a given number of bits, and have constant relative precision over the range. VHDL provides abstract floating-point types, including the type real
, built into the language. However, they are defined to use IEEE 64-bit double- precision representation. That may not be the best choice for all applications. VHDL-2008 provides a set of packages for binary-coded floating-point representation and operations in which we can control the range and precision and many aspects of the way arithmetic operations are performed. Floating-point values are represented using the same principles as IEEE-standard floating-point, specified in IEEE Std 743 and IEEE Std 854, with a sign bit, an exponent field, and a fraction field. However, we can choose the field widths that are appropriate for our application.
The main floating-point math package, float_generic_pkg
, is defined as:
package float_generic_pkg is generic ( float_exponent_width : natural := 8; float_fraction_width : natural := 23; float_round_style : round_type := round_nearest; float_denormalize : boolean := true; float_check_error : boolean := true; float_guard_bits : natural := 3; no_warning : boolean := false; package fixed_pkg is new IEEE.fixed_generic_pkg generic map (<>) );
The package uses the generics to govern the behavior of operations. The generics float_exponent_width
and float_fraction_width
are used to determine the default size of results from the to_float
conversion functions. The rounding mode for operations is specified by the generic float_round_style: round_nearest, round_zero
(truncation), round_inf
(round up toward infinity) and round_neginf
(round down toward negative infinity). The enumeration type round_type
is defined in fixed_float_types.
Denormalized numbers are a form of floating-point numbers that represent very small values near zero. If the generic float_denormalized
is true, operations in the package deal with denormalized values; otherwise, all numbers are treated as normalized. The generic float_check_error
controls detection of invalid numbers and overflow, float_guard_bit
specifies the number of extra bits of precision used within operations before the result is rounded, and no_warning
allows suppression of warning messages. Finally, the generic fixed_pkg
allows us to specify an instance of the fixed-point package (see Section 8.4) whose types are to be used for the conversion functions between fixed-point and floating-point types.
As for the fixed-point package, the IEEE
library includes an instance of float_generic_pkg
with default values for the generics. The package float_pkg
is defined as follows:
package float_pkg is new IEEE.float_generic_pkg generic map ( float_exponent_width => 8, float_fraction_width => 23, float_round_style => IEEE.fixed_float_types.round_nearest, float_denormalize => true, float_check_error => true, float_guard_bits => 3, no_warning => false, fixed_pkg => IEEE. fixed_pkg );
If we are using a combination of fixed-point and floating-point numbers in an application and need to instantiate the packages ourselves, we should instantiate the fixed-point package first, and then use the instance as the actual for the fixed_pkg
generic in our instance of the floating-point package. For example, we might instantiate the fixed-point package as follows:
package my_fixed_pkg is new IEEE.fixed_generic_pkg generic map ( fixed_round_style => IEEE.fixed_float_types.fixed_round, fixed_overflow_style => IEEE.fixed_float_types.fixed_wrap, fixed_guard_bits => 2, no_warning => true );
and then instantiate the floating-point package:
package my_float_pkg is new IEEE.float_generic_pkg generic map ( float_exponent_width => 6, float_fraction_width => 18, float_round_style => round_zero, float_denormalize => false, float_check_error => true, float_guard_bits => 2, no_warning => true, fixed_pkg => my_fixed_pkg );
The package float_generic_pkg
(and each instance of the package) defines the base type for floating point numbers, unresolved_float.
The alias u_float
is a convenient shorthand for this type. There is also a subtype, float
, which has resolved elements, for signals that have multiple sources. The declarations are:
type unresolved_float is array (integer range <>) of std_ulogic; alias u_float is unresolved_float; subtype float is (resolved) unresolved_float;
Objects of these types must have descending (downto) index ranges, for example:
signal A : float(8 downto -23) := "01000000110100000000000000000000";
The sign bit is at index A'left
(bit 8 in this example), the exponent is indexed from A'left -
1 down to 0 (7 down to 0 in the example), and the fraction is indexed from -1 down to A'right
(-1 down to -23 in the example). Unlike fixed-point numbers, floating-point numbers must have the sign, exponent, and fraction all present. The smallest floating-point representation supported by the package has a range of 3 down to -3. In practice, we would expect representations to be 16 bits or more, with at least 6 bits for the exponent and at least 10 bits for the fraction. For the sign bit, 0 is positive, and 1 is negative. The exponent field is an unsigned binary value representing the actual exponent biased by 2e-1 – 1 (where e is the width of the exponent field). Thus, for the signal A declared above, the bias is 127. The actual fraction is normalized to the range of 1.0 to just less than 2.0. Since the bit to the left of the radix point would always be 1, it is not explicitly represented. Instead, the fraction field of a floating-point number just contains the bits to the right of the radix point, with a 1 bit implied to the left of the radix point.
We can use these properties of the representation to analyze the bit string used as the default initial value for the signal A above. The leftmost bit is 0, so the number is positive. The next 8 bits, A(7 downto
O), are 10000001. As an unsigned number, this is 129. We subtract the bias, 127, to give an actual exponent of 2. The fraction field is 101000000000000000000000. We include the implied 1 bit to give an actual fraction of 1.101. Thus, the value represented is +1.1012 × 22 = 1.625 × 4 = 6.5.
The packages declare a number of subtypes and aliases for IEEE standard floating-point representations. For IEEE Std 754 single-precision numbers, the declarations are:
subtype unresolved_float32 is unresolved_float(8 downto -23); alias u_float32 is unresolved_float32; subtype float32 is float(8 downto -23);
For IEEE Std 754 double-precision numbers (corresponding to double float
in C, float"8
in Fortran, and real
in VHDL), the declarations are:
subtype unresolved_float64 is unresolved_float(11 downto -52); alias u_float64 is unresolved_float64; subtype float64 is float(l1 downto -52);
For IEEE Std 854 extended-precision numbers (corresponding to long double
in C
and float*l6
in Fortran), the declarations are:
subtype unresolved_floatl28 is unresolved_float (15 downto -112); alias u_float128 is unresolved_floatl28; subtype float128 is float(l5 downto -112);
The IEEE floating-point number standards reserve a number of representations for special purposes. In particular, numbers with all 0 or all 1 bits in the exponent field have the following meanings:
• Positive zero: 0 00000000 00000000000000000000000 • Negative zero: 1 00000000 00000000000000000000000 • Positive infinity: 0 11111111 00000000000000000000000 • Negative infinity: 1 11111111 00000000000000000000000
Note that there are two representations of 0, one positive and the other negative. Operations on floating-point values generally treat them as equivalent. In addition to these representations, a number with all 1 bits in the exponent field and at least one 1 bit in the fraction field (such as 1 11111111 00000000000000000000001) is called Not-a-Number, or NaN. Such values can result from otherwise illegal operations, such as division of zero by zero, or square root of -1.
Here are some further examples of floating-point numbers. First, the following is a large float32
value (though not largest, as that is just less than 2**128).
0 11111110 000000000000000000000000 = +1 × 2254 − 127 × (1.0 + 0.0) = 2127 = 1.70141 × 1038
Next, the following is the smallest float32
value, without using denormals:
0 00000001 00000000000000000000000 = +1 × 21 − 127 × (1.0 + 0.0) = 2− 126 = 1.17549 × 10−38
Finally, the following is a small float32
value using denormals (though not the smallest):
0 00000000 10000000000000000000000 = +1 × 21 − 127 × (0.0 + 0.5) = +1 × 2− 126 × 0.5 = 2−127 = 5.87747 × 10−39
For floating-point math operations, the result always has the largest of the exponent sizes and fraction sizes of the operands. Most often, the numbers are all of the same size, as in the following example:
signal A32, B32, Y32 : float(8 downto -23); . . . Y32 <= A32 + B32
Further details of overloaded operations and result sizes are provided in the tables in later sections of this chapter.
If we want to assign a value to a floating-point object, we can either use a string literal or we can apply a to_float
conversion function to an integer or real number. This is similar to the way in which we assign values to fixed-point objects (see Section 8.4). In the case of conversion functions, we can specify the result size either by specifying the exponent and fraction size, or by providing an object whose index range is used. These approaches are shown in the following example:
signal A_fp32 : float32;
. . .
A_fp32 <= "01000000110100000000000000000000";
A_fp32 <= to_float(6.5, 8, -32); -- pass sizes
A_fp32 <= to_float(6.5, A_fp32); -- size using A_fp32
As with fixed-point math, use of string literals in an expression is problematic, since their index ranges are ascending (to) and start with integer'low.
The solution is the same, namely, using type-qualified string literals or using overloaded operations that accept integer or real operands. These are shown in the following example:
signal A, Y : float32;
. . .
-- Y <= A + "01000000110100000000000000000000"; -- illegal
Y <= A + float32'("01000000110100000000000000000000");
Y <= A + 6.5; -- overloading with real
Y <= A + 6; -- overloading with integer
The predefined types and association operations in VHDL are defined in the package standard
, residing in the library std.
In practice, most tools have built-in implementations of the package, rather than interpreting the VHDL source code directly. VHDL-2008 enhances package standard
by adding a
number of new types and by extending the set of operations association with predefined types.
The types boolean_vector, integer_vector, real_vector
, and time_vector
are now predefined. Each is an unconstrained type with natural
as the index type, much like the predefined type bit_vector
in earlier versions. The predefined operations on boolean_vector
are the same as those defined for bit_vector.
The predefined operations on integer_vector
include the relational operators ("=", "/=", "<", ">", "<="
, and and the concatenation operator ("&").
The predefined operations on real_vector
and time_vector
include the equality and inequality operators ("="
and "/=")
and the concatenation operator ("&").
The array/scalar logic operations and logical reduction operation (see Sections 4.1 and 4.3) are predefined for bit_vector
and boolean_vector
, since they are arrays with bit
and boolean
elements, respectively.
The matching relational operators "?=", "?/=", "?>", "?>=", "?<"
, and "?<="
are predefined for bit
and boolean.
Further, the operators "?="
and "?/="
are predefined for bit_vector
and boolean_vector.
(See Section 4.5.)
The condition operator "??"
is predefined for bit
(see Section 4.4).
The operators mod and rem are predefined for time
, since it is a physical type (see Section 4.7).
The maximum
and minimum
operations are predefined for all of the predefined types (see Section 4.6).
The functions rising_edge
and falling_edge
are predefined for bit
and boolean.
Prior to VHDL-2008, the bit
versions of these functions were declared in the package numeric_bit.
However, that was mainly to provide consistency with the std_logic
versions defined in the std_logic_1164
package. They rightly belong with the definition of the type on which they operate; hence, VHDL-2008 includes them in the package standard.
The VHDL-2008 revision of the numeric_bit
package redefines the operations there as aliases for the predefined versions.
The to_string
operations are predefined for all scalar types and for bit_vector
(see Section 7.1).
Further, the to_bstring, to_ostring
, and to_hstring
operations and associated aliases are predefined for bit_vector.
Previous version of the VHDL standard defined the language, but did not specify any means of accessing the simulation environment. VHDL-2008, as well as including the VHPI procedural interface (see Section 2.6), also includes a new environment package called env
, resident in the library std.
The package defines the following procedures:
procedure stop (status: integer); procedure stop; procedure finish (status: integer); procedure finish;
When the procedure stop
is called, the simulator stops and accepts further input from the user interface (if interactive) or command file (if running in batch mode). When the procedure finish
is called, the simulator terminates; simulation cannot continue. The versions of the procedures that have the status
parameter use the parameter value in an implementation-defined way. They might, for example, provide the value to a control script so that the script can determine what action to take next.
The env
package also defines a function to access the resolution limit for the simulation:
function resolution_limit return delay_length;
One way in which we might use this function is to wait for simulation time to advance by one time step, as follows:
wait for env.resolution_limit;
Since the resolution limit, and hence the minimum time by which simulation advances, can vary from one simulation run to another, we cannot write a literal time value in such a wait statement. The use of the resolution_limit
function allows us to write models that adapt to the resolution limit used in each simulation. We need to take care in using this function, however. It might be tempting to compare the return value with a given time unit, for example:
if env.resolution_limit > ns then -- potentially illegal! . . . -- do coarse-resolution actions else . . . -- do fine-resolution actions end if ;
The problem is that we are not allowed to write a time unit smaller than the resolution limit used in a simulation. If this code were simulated with a resolution limit greater than ns
, the use of the unit name ns
would cause an error. So the code can only succeed if the resolution limit is less than or equal to ns.
We can avoid this problem by rewriting the example as:
if env.resolution_limit > 1.OE-9 sec then . . . -- do coarse-resolution actions else . . . -- do fine-resolution actions end if ;
For resolution limits less than or equal to ns
, the test returns false, so the “else” alternative is taken. For resolution limits greater than ns
, the time literal 1 .OE-9 sec
is truncated to zero, and so the test returns true. Thus, even though the calculation is not quite what appears, it produces the result we want.
In this section, we summarize the operations defined in the standard packages. Table 8.1 summarizes the operand and result types for overloaded operations defined in the packages std_logic_1164, numeric_std, numeric_bit, numeric_std_unsigned, numeric_bit_unsigned, fixed_generic_pkg
, and float_generic_pkg.
The table does not include the predefined operators on the various types. In the table, the notation use is as follows:
Table 8.1. Operand and result types
Left | Right | Result | |
---|---|---|---|
Binaryand, |
|
|
|
LogicArrayType | LogicArrayType | LogicArrayType | |
LogicArrayType |
| LogicArrayType | |
std_ulogic | LogicArrayType | LogicArrayType | |
| std_ulogic | std_ulogic | |
LogicArrayType | LogicArrayType | ||
Unary reduction | LogicArrayType | LogicArrayType | |
=, /=, <, <=, >, >= | NumericArrayType | NumericArrayType | boolean |
NumericArrayType | integer | boolean | |
integer | NumericArrayType | boolean | |
RealArrayType | real | boolean | |
real | RealArrayType | boolean | |
?=, ?/=, ?<=, ?<=, ?>, ?>= | NumericArrayType | NumericArrayType | ArrayElementType |
NumericArrayType | integer | ArrayElementType | |
integer | NumericArrayType | ArrayElementType | |
RealArrayType | real | ArrayElementType | |
real | RealArrayType | ArrayElementType | |
rol, ror, sll, srl | LogicArrayType | integer | LogicArrayType |
sla, sra | NumericArrayType | integer | NumericArrayType |
Binary +, -, *, /, mod, rem | NumericArrayType | NumericArrayType | NumericArrayType |
NumericArrayType | integer | NumericArrayType | |
RealArrayType | real | RealArrayType | |
real | RealArrayType | RealArrayType | |
Binary +, − | NumericArrayType |
| NumericArrayType |
| NumericArrayType | NumericArrayType | |
Unary -, abs | signed, sfixed, float | signed, sfixed, float | |
maximum, minimum | NumericArrayType | NumericArrayType | NumericArrayType |
NumericArrayType | integer | NumericArrayType | |
integer | NumericArrayType | NumericArrayType | |
RealArrayType | real | RealArrayType | |
real | RealArrayType | RealArrayType |
LogicArrayType: arrays of std_ulogic
elements
NumericArrayType: signed, unsigned, ufixed, sfixed, float, bit_vector
with operations in numeric_bit_unsigned
visible, or std_ulogic_vector
with operations in numeric_std_unsigned
visible
RealArrayType: ufixed, sfixed
, or float
ArrayElementType: the element type of the operand array or arrays
Table 8.2 summarizes the result size and/or index range for operations with array results. For arrays representing unsigned or signed integer values, only the size is relevant, as the leftmost bit is the most significant bit and the rightmost bit is the least significant bit. For fixed-point and floating-point values, the specific index bounds are relevant, as described in Sections 8.4 and 8.5. The notation for types is the same as that used in Table 8.1. In addition, L represents the left operand, R represents the right operand, A represents the array operand in the case where the other operand is scalar, and Result represents the result of the operation.
Table 8.2. Result sizes and index ranges
Operator | Result Type | Result Size and/or Range |
---|---|---|
Array/array | ArrayOfBits | Result’length = L’length = R’length |
Fixed, Float: Result’range = L’range | ||
Array/scalar | ArrayOfBits | Result’length = L’length = A’length |
Fixed, Float: Result’range = A’range | ||
not | ArrayOfBits | Result’length = R’length |
Fixed, Float: Result’range = R’range | ||
rol, ror, sll, srl, sla, sra | ArrayOfBits | Result’length = A’length |
Fixed, Float: Result’range = A’range | ||
+, -, *, /, rem, mod | float | maximum(L’left, R’left) down to minimum(L’right, R’right) |
Binary +, - | unsigned, signed | maximum(L’length, R’length) - 1 down to 0 |
ufixed, sfixed | maximum(L’left, R’left) + 1 down to minimum(L’right, R’right) | |
unsigned, signed | L’length + R’length - 1 down to 0 | |
ufixed, sfixed | L’left + R’left + 1 down to L’right + R’right | |
unsigned, signed | L’length − 1 down to 0 | |
ufixed | L’left − R’right down to L’right − R’left - 1 | |
sfixed | L’left − R’right + 1 down to L’right − R’left | |
rem | unsigned, signed | R’length − 1 down to 0 |
ufixed, sfixed | minimum(L’left, R’left) down to minimum(L’right, R’right) | |
mod | unsigned, signed | R’length − 1 down to 0 |
ufixed | minimurn(L’left, R’left) down to minimum(L’right, R’right) | |
sfixed | R’left down to minimum(L’right, R’right) | |
Unary -, abs | signed | R’length − 1 down to 0 |
sfixed | R’left + 1 down to R’right | |
minimum, maximum | DiscreteArrayType | Result’length = A’length |
Fixed, Float: Result’range = A’range | ||
unsigned, signed | maximum(L’length, R’length) − 1 down to 0 | |
ufixed, sfixed, float | minimum(L’left, R’left) down to minimum(L’right, R’right) |
In this section, we summarize the conversion functions defined in the standard-logic and numeric packages. In order to present the information in more compact form, we have used some abbreviations for types and the packages in which the functions are defined: bv = bit_vector
, slv = std_logic_vector
, sulv = std_ulogic_vector
, 1164 = std_logic_1164
, nbu = numeric_bit_unsigned
, nsu = numeric_std_unsigned
, ns/b = numeric_std
and numeric_bit
, fixed = fixed_generic_pkg
, and float = float_generic_pkg.
Table 8.3 shows the functions that convert between bit
and std_ulogic
scalar types, and between vectors of these types. The first parameter is the value to be converted. Those functions that convert from an abstract numeric value to a vector representation have a second parameter, size
, to specify the result size.
Table 8.4 shows the functions that convert from the various numeric types to the unsigned
and signed
types defined in numeric_std
and numeric_bit
. The first parameter is the value to be converted, and the second parameter is either the size of the result (size
) or a value of the result type whose size is used for the result (size_res
).
Table 8.4. Conversion functions yielding unsigned and signed values
Function | Return | Param 1 | Param 2 | Param 3 | Param 4 | Package |
---|---|---|---|---|---|---|
| unsigned | natural | size | ns/b | ||
| ||||||
ufixed | size | overflow | round | fixed | ||
| overflow | round | ||||
float | size | round |
| float | ||
| round |
| ||||
| signed | integer | size | ns/b | ||
| ||||||
sfixed | size | overflow | round | fixed | ||
| overflow | round | ||||
float | size | round |
| float | ||
| round |
|
The conversions from fixed-point representation have a third parameter, overflow_style
(abbreviated to overflow in the table), of type fixed_overflow_style_type
. The default value is the value of the generic fixed_overflow_style
. The fourth parameter, round_style
(abbreviated to round), is of type fixed_round_style_type
and defaults to the value of the generic fixed_round_style
.
The conversions from float
have a third parameter, round_style
(abbreviated to round), of type round_type
, with the default being the value of the package generic float_round_style
. The fourth parameter is check_error
(abbreviated to chk-err), of type boolean
, for controlling error checking during the conversion. The default is the value of the package generic float_check_error
.
Table 8.5 shows the functions that convert from numeric types to the ufixed
and sfixed
types defined in fixed_generic_pkg
and instances of that package. In the case of conversion functions defined in the floating-point packages, the definitions of ufixed
and sfixed
come from the instance of fixed_generic_pkg
supplies as an actual generic package to the instance of float_generic_pkg. The first parameter of each function is the value to be converted. Following this are either two parameters, left_index
and right_index
(abbreviated to L_index and R_index in the table), to specify the index bounds of the result, or a single parameter, size_res
, for a value whose index range is used for the result. For the conversions from natural
or unsigned
to ufixed
, and for the conversions to integer
or signed
to sfixed
, the default for right_index
is 0. Additional parameters specify overflow and rounding modes (overflow_style
and round_style
), the number of guard bits to use (guard_bits
), whether error checking is required (check_error
), and whether operands of type float use denormalized representation (denormalize
). The default values for the overflow_style, round_style
, and guard_bits
parameters come from the generics of the fixed_generic_pkg
package; the default values for the check_error
and denormalize
parameters come from the generics of the float_generic_pkg package. Note that there are also versions of to_ufixed
and to_sfixed
with no parameters beyond the first unsigned
or signed
parameter. (This is not an error in the table layout!) These versions simply return the value of the parameter as a fixed-point value with no fractional part (that is, indexed from one less than the length down to 0).
Table 8.5. Conversion functions yielding ufixed
and sfixed
values
Return | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6 | Param 7 | Package | |
---|---|---|---|---|---|---|---|---|---|
| ufixed | sulv |
|
| fixed | ||||
| |||||||||
unsigned | |||||||||
| R_index | overflow | round | ||||||
| overflow | round | |||||||
natural |
|
| overflow | round | |||||
| overflow | round | |||||||
real |
|
| overflow | round | guard | ||||
| overflow | round | guard | ||||||
float |
|
| overflow | round |
| denorm | float | ||
| overflow | round |
| denorm | |||||
sfixed | ufixed | fixed | |||||||
sulv |
|
| |||||||
| |||||||||
signed | |||||||||
|
| overflow | round | ||||||
| overflow | round | |||||||
integer |
|
| overflow | round | |||||
| overflow | round | |||||||
real |
|
| overflow | round | guard | ||||
| overflow | round | guard | ||||||
float |
|
| overflow | round |
| denorm | float | ||
| overflow | round |
| denorm |
Table 8.6 shows the functions that convert from numeric types to the float type defined in fixed_generic_pkg and instances of that package. Again, the definitions of ufixed and sfixed come from the instance of fixed_generic_pkg
supplied as an actual generic package to the instance of float_generic_pkg
. The first parameter of each function is the value to be converted. Following this are either two parameters, exponent_width
and fraction_width
(abbreviated to exponent and fraction in the table), to specify the sizes of the corresponding fields in the result, or a single parameter, size_res
, for a value whose index range is used for the result. Additional parameters specify the rounding mode (round_style
) and whether denormalized representation is used (denormalize
). The default values for the field size, round_style
and denormalize
parameters come from the generics of the package.
Table 8.6. Conversion functions yielding float values
Function | Return | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Package |
---|---|---|---|---|---|---|---|
| float | sulv | exponent | fraction | float | ||
| |||||||
unsigned | |||||||
exponent | fraction | round | |||||
| round | ||||||
signed | exponent | fraction | round | ||||
| round | ||||||
ufixed | exponent | fraction | round | denorm | |||
| round | denorm | |||||
sfixed | exponent | fraction | round | denorm | |||
| round | denorm | |||||
integer | exponent | fraction | round | ||||
| round | ||||||
real | exponent | fraction | round | denorm | |||
| round | denorm |
The final group of conversion functions is shown in Table 8.7. These function convert from binary-coded vectors to abstract integer or real types. As in the preceding tables, the first parameter is the value to be converted, and subsequent parameters specify overflow and rounding modes (overflow_style
and round_style
), whether error checking is required (check_error
), and whether operands of type float use denormalized representation (denormalize).
The default values for these subsequent parameters come from the generics of the respective packages.
Table 8.7. Conversion functions yielding integer and real values
Function | Return | Param 1 | Param 2 | Param 3 | Param 4 | Package |
---|---|---|---|---|---|---|
| natural | bv | nbu | |||
natural | sulv | nsu | ||||
natural | unsigned | ns/b | ||||
integer | signed | |||||
natural | ufixed | overflow | round | fixed | ||
integer | sfixed | overflow | round | |||
integer | float | round |
| float | ||
| real | ufixed | fixed | |||
sfixed | ||||||
float | round |
| denorm | float |
In addition to the declarations of the conversion functions, there are aliases for convenience and enhanced readability: the function to_bv
has aliases to-bitvector
and to_bit_vector;
the function to_sulv
has aliases to-stdulogicvector
and to-std_ulogic_vector;
and the function to_slv
has aliases to_stdlogicvector
and to_std_logic_vector.
For each binary-coded numeric type, there is a resize
function, shown in Table 8.8. The versions yielding bit_vector, std_ulogic_vector, unsigned
, and signed
results have a parameter new-size
to specify the result size, or a parameter size_res
for an object whose index range is used for that of the result. The versions that yield fixed-point results have either two parameters (left_index
and right_index
) to specify the index bounds of the result, or a parameter (size_res
) for an object whose index range is used for that of the result. They also have parameters to specify overflow and rounding modes (overflow_style
and round_style)
, with default values coming from the package generics. Similarly, the versions that yield floating-point results have either two parameters to specify the field sizes for the result (exponent_width
and fraction_width)
, and subsequent parameters specify rounding modes (round_style)
, whether error checking is required (check_error)
, and whether the operand and result use denormalized representation (denormalize_in
and denormalize_out
, respectively). The default values for these subsequent parameters come from the package generics.
Table 8.8. Resizing functions
Return | Param 1 | Param 2 | Param 3 | Param 4 | Param 5 | Param 6 | Param 7 | Package | |
---|---|---|---|---|---|---|---|---|---|
resize | bv | bv |
| nbu | |||||
| |||||||||
sulv | sulv |
| nbu | ||||||
| |||||||||
unsigned | unsigned |
| ns/b | ||||||
| |||||||||
signed | signed |
| |||||||
| |||||||||
ufixed | ufixed |
|
|
| round | fixed | |||
| overflow | round | |||||||
sfixed | sfixed |
|
|
| round | ||||
| overflow | round | |||||||
float | float | exponent | fraction | round |
|
|
| float | |
| round |
|
|
|
Resizing an unsigned vector of type bit_vector, std_ulogic_vector
or unsigned
to produce a larger vector involves filling leftmost bits with ‘0’. Resizing these types to produce a smaller vector involves truncating the leftmost bits. For type signed
, producing a larger vector involves filling the leftmost bits with copies of the operand’s sign bit, and producing a smaller vector involves truncating the leftmost bits while retaining the sign bit.
Resizing a fixed-point value is similar. A ufixed vector is extended on the left or right by filling bits with ‘0’. An sfixed vector is extended on the left by replicating the sign bit and extended on the right by filling bits with ‘0’. Reducing the size of a fixed-point vector is more complicated, and depends on the overflow and rounding modes. If the vector is to be truncated on the right, a rounding mode of fixed_truncate
causes the truncated bits to be discarded and the rightmost result bit to be unchanged, whereas a rounding mode of fixed-round
causes the result to be rounded based on the values of the discarded bits and the rightmost result bit. If the vector is to be truncated to the left and the operand value is out of the representable range for the result, the value returned depends on the overflow style. For fixed_saturate
, the largest representable value (for ufixed
or for positive sfixed
values) or the most negative representable value (for negative sfixed
values) is returned. For fixed_wrap
, the leftmost bits are simply truncated, which, in the case of sfixed
values, may result in a change of sign.
Resizing a floating-point value is much more involved than resizing integral and fixed-point values. It involves determining the class of value represented by the operand (normal, denormal, zero, infinity, or NaN), resizing the exponent and fractional parts, rounding according to the round_style
parameter, renormalizing or representing as a denormal if required, checking for errors, and transforming overflow to infinity.
VHDL-2008 expands the definition of the strength reduction functions so that they are defined for the entire family of types based on std_ulogic.
Functions of the following form are defined:
function to_01 (S : uType ; XMAP : std_ulogic := '0') return uType ; function to_X01 (S : uType ) return uType ; function to_X01Z (S : uType ) return uType ; function to_UX01 (S : uType ) return uType ;
The type uType includes std_ulogic, std_ulogic_vector, unresolved_unsigned, unresolved_signed, unresolved_ufixed, unresolved_sfixed
, and unresolved_float.
The value returned by each function for each operand element value is shown in Table 8.9. The functions to-XO
1 , to-XO 1 Z
, and to-UXO 1
, when applied to vector operands, convert each operand element according to the table to yield the corresponding result element. The to_01
function, however, behaves differently. Provided all of the elements are 'O'
, ‘I1, ‘L’, or ‘HI, they are converted according to the table. However, if any element is a metalogical value (a value other than ‘O’, ‘l’, ‘L’, or ‘HI), all elements of the result are set to the value of the xmap
parameter. Thus, we can test any element of the result to determine whether there were any metalogical elements in the operand.
Table 8.9. Strength reduction mappings
Function | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘—’ |
| xmap | xmap | ‘0’ | ‘1’ | xmap | xmap | ‘0’ | ‘1’ | xmap |
| ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ |
| ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ |
| ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ |
VHDL-2008 also expands the definition of the ‘XI detection functions so that they are defined for the entire family of types based on std_ulogic.
The function definitions are of the form:
function is_X (S : uType ) return boolean;
The version for std_ulogic
returns true if the operand is a metalogical value, or false otherwise. The versions for vector types return true if any element of the operand is a metalogical value, or false otherwise.