Chapter 8. Standard Packages

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

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.

The Numeric_bit and Numeric_std Packages

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.

The Numeric Unsigned Packages

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.

The Fixed-Point Math Packages

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 Floating-Point Math Packages

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 Standard Package

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.

The Env Package

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.

Operator Overloading Summary

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

Operators

Left

Right

Result

Binaryand, or, nand, nor, xor, xnor

std_ulogic

std_ulogic

std_ulogic

 

LogicArrayType

LogicArrayType

LogicArrayType

 

LogicArrayType

std_ulogic

LogicArrayType

 

std_ulogic

LogicArrayType

LogicArrayType

 

not

std_ulogic

std_ulogic

  

LogicArrayType

LogicArrayType

Unary reduction and, or, nand, nor, xor, xnor

 

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

std_ulogic

NumericArrayType

 

std_ulogic

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 and, or, nand, nor, xor, xnor

ArrayOfBits

Result’length = L’length = R’length

  

Fixed, Float: Result’range = L’range

Array/scalar and, or, nand, nor, xor, xnor

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)

Conversion Function Summary

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.3. Conversions between bit and standard-logic types

Function

Return

Parameter 1

Parameter 2

Package

to_std_ulogic

std_ulogic

bit

 

1164

to-bit

bit

std_ulogic

 

1164

to_bv

bit_vector

sulv

 

1164

  

natural

size

nbu

to_sulv

sulv

bv

 

1164

  

slv

 

1164

  

natural

size

nsu

  

ufixed

 

fixed

  

sfixed

 

fixed

  

float

 

float

to-slv

slv

bv

 

1164

  

sulv

 

1164

  

natural

size

nsu

  

ufixed

 

fixed

  

sfixed

 

fixed

  

float

 

float

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

to_unsigned

unsigned

natural

size

  

ns/b

   

size_res

   
  

ufixed

size

overflow

round

fixed

   

size_res

overflow

round

 
  

float

size

round

chk_err

float

   

size_res

round

chk_err

 

to_signed

signed

integer

size

  

ns/b

   

size_res

   
  

sfixed

size

overflow

round

fixed

   

size_res

overflow

round

 
  

float

size

round

chk_err

float

   

size_res

round

chk_err

 

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

Function

Return

Param 1

Param 2

Param 3

Param 4

Param 5

Param 6

Param 7

Package

to_ufixed

ufixed

sulv

L_index

R_index

    

fixed

   

size_res

      
  

unsigned

       
   

L_index

R_index

overflow

round

   
   

size_res

overflow

round

    
  

natural

L_index

R_index

overflow

round

   
   

size_res

overflow

round

    
  

real

L_index

R_index

overflow

round

guard

  
   

size_res

overflow

round

guard

   
  

float

L_index

R_index

overflow

round

chk_err

denorm

float

   

size_res

overflow

round

chk_err

denorm

  

to_sfixed

sfixed

ufixed

      

fixed

  

sulv

L_index

R_index

     
   

size_res

      
  

signed

       
   

L_index

R_index

overflow

round

   
   

size_res

overflow

round

    
  

integer

L_index

R_index

overflow

round

   
   

size_res

overflow

round

    
  

real

L_index

R_index

overflow

round

guard

  
   

size_res

overflow

round

guard

   
  

float

L_index

R_index

overflow

round

chk_err

denorm

float

   

size_res

overflow

round

chk_err

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

to_float

float

sulv

exponent

fraction

  

float

   

size_res

    
  

unsigned

     
   

exponent

fraction

round

  
   

size_res

round

   
  

signed

exponent

fraction

round

  
   

size_res

round

   
  

ufixed

exponent

fraction

round

denorm

 
   

size_res

round

denorm

  
  

sfixed

exponent

fraction

round

denorm

 
   

size_res

round

denorm

  
  

integer

exponent

fraction

round

  
   

size_res

round

   
  

real

exponent

fraction

round

denorm

 
   

size_res

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

to_integer

natural

bv

   

nbu

 

natural

sulv

   

nsu

 

natural

unsigned

   

ns/b

 

integer

signed

    
 

natural

ufixed

overflow

round

 

fixed

 

integer

sfixed

overflow

round

  
 

integer

float

round

chk_err

 

float

to_real

real

ufixed

   

fixed

  

sfixed

    
  

float

round

chk_err

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

Function

Return

Param 1

Param 2

Param 3

Param 4

Param 5

Param 6

Param 7

Package

resize

bv

bv

new_size

     

nbu

   

size_res

      
 

sulv

sulv

new_size

     

nbu

   

size_res

      
 

unsigned

unsigned

new_size

     

ns/b

   

size_res

      
 

signed

signed

new_size

      
   

size_res

      
 

ufixed

ufixed

L_index

R_index

overflow

round

  

fixed

   

size_res

overflow

round

    
 

sfixed

sfixed

L_index

R_index

overflow

round

   
   

size_res

overflow

round

    
 

float

float

exponent

fraction

round

chk_err

den_in

den_out

float

   

size_res

round

chk_err

den_in

den_out

  

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.

Strength Reduction Function Summary

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’

‘—’

to_01

xmap

xmap

‘0’

‘1’

xmap

xmap

‘0’

‘1’

xmap

to_X01

‘X’

‘X’

‘0’

‘1’

‘X’

‘X’

‘0’

‘1’

‘X’

to_X01Z

‘X’

‘X’

‘0’

‘1’

‘Z’

‘X’

‘0’

‘1’

‘X’

to_UX01

‘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.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.213.235