In VHDL, we model computation by writing expressions that involve application of operations (operators and functions) to operand values. Each operand is of some type, either predefined or user-defined. VHDL defines overloaded versions of operations to perform computation on values of various types. In VHDL-2008, a number of new operations are introduced, and the variety of types to which existing operations can be applied is expanded. We describe the new and changed operations in this chapter.
One point to note is that in earlier versions of VHDL, many of the operations were defined in separate standards. In particular, IEEE Std 1164 specified the package std_logic_l164
, which defined the types std_ulogic, stdjogic, std_ulogic_vector
, and std_logic_vector
and the operations on those types. Also, IEEE Std 1076.3 specified the packages numeric_bit
and numeric_std
, each of which defined the types unsigned
and signed
and operations on those types. All of these packages are now included as part of the VHDL-2008 standard. Other changes to the standard packages are described in Chapters 7 and 8.
VHDL provides logical operators, and, or, nand, nor, xor, and xnor, that each operate on a pair of values to produce a result. Earlier versions of VHDL provided predefined and standard overloaded definitions of these operators with the following signatures:
[ScalarType, ScalarType return ScalarType] [ArrayType, ArrayType return ArrayType]
ScalarType included the types bit, boolean
, and std_ulogic
, and ArrayType included arrays of bit, boolean
, and std_ulogic (std_ulogic_vector, std_logic_vector, unsigned
, and signed).
While this was sufficient for many purposes, there were some common modeling problems that required one operand to be an array and the other a scalar, yielding an array result. To meet this requirement, VHDL-2008 provides further overloaded definitions of the logical operators with the following signatures:
[ArrayType, ArrayElementType return ArrayType] [ArrayElementType ,ArrayType return ArrayType]
As before, ArrayType includes arrays of bit, boolean, and std_ulogic.
ArrayElementType is the scalar element type of the other operand. Thus, for example, we can apply an operator such as and to a bit_vector
and a bit operand. The bit value is ANDed with each element of the array to produce an array result.
Example 4.1. Select Logic
A common problem in coding register read logic is using select bits (each a scalar value) to select among several registers (array values). One possible coding that could be used in earlier versions of VHDL is:
genloop : for i in data_bus' range generate begin data_bus(i) <= (a(i) and a_sel) or (b(i) and b_sel) or (c(i) and c_sel); end generate;
Here, a_sel, b_sel
and c_sel
are the scalar select signals, and a, b, and c are the register values. Note that this coding requires the array indices for the register values and the data bus to be the same. An alternate solution that uses intermediate signals is:
signal va_sel, vb_sel, vc_sel : std_logic_vector(data_bus'range); . . . va_sel <= (others => a_sel); vb_sel <= (others => b_sel); vc_sel <= (others => c_sel); data_bus <= (a and va_sel) or (b and vb_sel) or (c and vc_sel);
Note that this solution does not require the array indices to be the same. The following third alternative is functionally correct if the select signals are mutually exclusive; however, for larger sized “AND-OR” logic, it results in an inefficient hardware implementation known as priority select logic.
data_bus <= a when a_sel = '1' else b when b_sel = '1' else c when c_sel = '1' else (others => '0'),
With the new overloaded definitions introduced in VHDL-2008, these alternatives can be replaced with the simple assignment:
data_bus <= (a and a_sel) or (b and b_sel) or (c and c_sel);
In each and term, the scalar value is applied to each bit of the array value. In effect, the scalar value is replicated in the same manner as the intermediate signal solution, but the statement is much more succinct.
The numeric_bit
and numeric_std
packages define overloaded addition (“+”) and subtraction (“-”) operators for unsigned and signed operands. Prior to VHDL-2008, if we wanted to code an addition with carry in, we had to convert the carry in to an array value, as follows:
signal c_in : std_logic; signal a, b : unsigned(7 downto 0); signal adder : unsigned(8 downto 0); . . . adder <= ('0' & a) + ('0' & b) + ("" & c_in);
Unfortunately, many synthesis tools saw the converted carry in as an additional array value and implemented two adders, where just a single adder with carry in would suffice.
In VHDL-2008, additional overloaded version of the “+” and “—” operators are added to allow the use of a scalar value (such as a std_logic
value) with an array value. Hence, we can rewrite the above code as follows:
adder <= ('0' & a) + ('0' & b) + c_in;
It is interesting to note that this same overloading is supported in one vendor’s nonstandard synthesis package and results in a single adder. The signatures for the new overloadings are:
[ArrayType, ArrayElementType return ArrayType] [ArrayElementType, ArrayType return ArrayType]
ArrayType includes unsigned and signed defined in the numeric_bit
and numeric_std
packages. ArrayElementType is bit (for the numeric_bit
operators) or std_ulogic
(for the numeric_std
operators). The same signatures are also defined for types ufixed and sfixed defined in the new fixed-point packages (see Section 8.4) and for the type float defined in the new floating-point packages (see Section 8.5).
Example 4.2. A conditional incrementer
The new overloading for the “+” operator allows us to use a scalar control signal as an operand in a conditional incrementer. If the control signal is ‘0’, an unsigned value is not incremented; if the control signal is ‘1’, the value is incremented. The declarations and process are:
signal inc_en : std_logic; signal inc_reg : unsigned(7 downto 0); . . . inc_reg_proc : process (clk) is begin if rising_edge(clk) then inc_reg <= inc_reg + inc_en; end if ; end process inc_reg_proc;
Prior VHDL-2008, the conditional incementer would have been coded as:
inc_reg_proc : process (clk) is begin if rising_edge(clk) then if inc_en = '1' then inc_reg <= inc_reg + 1; end if; end if; end process inc_reg_proc;
The use of the integer value 1 as an operand would have implied an adder, rather than just an incrementer.
In earlier versions of VHDL, the logical operators and, or, nand, nor, xor, and xnor were defined only as binary operators; that is, they each operated on two operands. The operands could be bit or boolean values, or they could be arrays of bit or boolean elements. In the case of array operands, the logical operator is applied to corresponding array elements to produce an array result. In some models, we need to apply a logical operator to all of the elements of an array to produce a single scalar result. To do this in earlier versions of VHDL, we had to write a loop to apply the operator to the elements.
VHDL-2008 extends the definition of logical operators to allow them to be used as unary operators. Each such logical reduction operator is applied to a single operand that is an array of bit or boolean elements and produces a bit or boolean result, respectively. The std_logic_l164
package also defines overloaded logical reduction operators for std_ulogic_vector
operands. Thus, the signature of each logical reduction operator is:
[ArrayType return ArrayElementType ]
The reduction and, or, and xor operators form the logical AND, OR, and exclusive OR, respectively of the array elements. Thus:
and "0110" = '0' and '1' and '1' and '0' = '0' or "0110" = '0' or '1' or '1' or '0' = '1' xor "0110" = '0' xor '1' xor '1' xor '0' = '0'
In each case, if the array has only one element, the result is the value of that element. If the array is a null array (that is, it has no elements), the result of the and operator is ‘I’, and the result of the or and xor operators is ‘0’.
The reduction nand, nor, and xnor operators are the negation of the reduction and, or, and xor operators, respectively. Thus:
nand "0110" = not ('0' and '1' and '1' and '0') = not '0' = '1' nor "0110" = not ('0' or '1' or '1' or '0') = not '1' = '0' xnor "0110" = not ('0' xor '1' xor '1' xor '0') = not '0' = '1'
In each case, application to a single-element array produces the negation of the element value. Application of nand to a null array produces ‘0’, and application of nor or xnor to a null array produces ‘1’.
The logical reduction operators have the same precedence as the unary not and abs operators. In the absence of parentheses, they are evaluated before binary operators. So the expression:
and A or B
involves applying the reduction and operator to A, then applying the binary or operator to the result and B. In some cases, we need to include parentheses to make an expression legal. For example, the expression:
and not X
is not legal without parentheses, since we cannot chain unary operators. Instead, we must write the expression as:
and (not X)
Example 4.3. Parity of a vector value
Without reduction operators, calculating parity requires the following:
parity <= data(7) xor data(6) xor data(5) xor data(4) xor data(3) xor data(2) xor data(1) xor data(0);
With reduction operators, calculating parity becomes
parity <= xor Data;
Since reduction operators have higher precedence than binary logical operators, the following two asignments produce the same value:
parity_error1 <= (xor data) and received_parity; parity_error2 <= xor data and received_parity;
However, for readability, parentheses are recommended.
VHDL provides numerous language constructs that use a condition to control what actions are performed. A condition is an expression that produces a boolean result, for example, through application of relational and logical operators. In earlier versions of VHDL, the fact that a condition had to produce a boolean value was a source of inconvenience, particularly in models that used bit or std_ulogic
values for control signals. We would typically write a condition using such a control signal as:
if control_sig = '1' then . . .
VHDL-2008 provides two new language features that allow us to treat an expression producing a bit or std_ulogic
value as a condition. The first of these features is a condition operator, “??”, that converts from a bit or std_ulogic
value to a boolean value. For bit, “??” converts ‘1’ to true and ‘0’ to false. For std_logic
, “??” converts both ‘1’ and ‘H’ to true and all other values to false. (We can also overload the operator for other user-defined types.) Thus, we could rewrite the if-statement condition shown above as:
if ?? control_sig = then . . .
Normally, we would not apply the condition operator explicitly like this, as the second of the new features involves implicit application of the operator in conditions. The operator is implicitly applied in a condition when the expression could otherwise not be interpreted as producing a boolean result and there is a unique interpretation using the condition operator that does produce a boolean result. These potential interpretations require use of the rules for resolving overloaded operators to determine the type of the expression. Note that an ambiguous boolean expression is considered a boolean interpretation and still results in an error.
As an example, assuming control_sig
is a std_ulogic
signal, we would rewrite the if-statement condition shown above as:
if control_sig then . . .
The condition operation is implicitly applied, since that is the one and only way of getting a boolean result from the expression.
The places where the condition operator is considered for application are:
after until in a wait statement
after assert in an assertion statement
after while in a while loop
after if or elsif in an if statement
after when in a next statement or exit statement
after when in a conditional signal or variable assignment statement
after if or elsif in an if-generate statement
in a Boolean expression in a PSL declaration or a PSL directive
This list includes all of the cases where an expression of type boolean was required in earlier versions of VHDL.
Example 4.4. Std_logic control conditions
With the condition operator implicitly applied, we can write the following in VHDL-2008:
signal cs1, ncs2, cs3 : std_logic; . . . if cs1 and not cs2 and cs3 then . . .
Backward compatibility is maintained, so we can still write:
if cs1 = '1' and ncs2 = '0' and cs3 = '1' then . . .
Note, however, that we cannot write a condition that mixes std_ulogic
and boolean operands for a logical operator, such as:
if cs1 and cs3 and ncs2 = '0' then -- illegal . . .
The “??” operator is only implicitly applied to the entire condition. There is no overloading for and that has a std_logic
left operand and a boolean right operand.
VHDL provides ordinary relational operators ("=", "/=", "<", "<=", ">", and ">=") that return a result of type boolean. We can use the result as a condition to control what actions are performed in a model. However, if we want to use the result to assign to a signal of type bit or std_ulogic
, we have to resort to a form such as:
control_sig >= '1' when X = Y else '0';
VHDL-2008 has a new set of predefined matching relational operators ("?=", "?/=", "?<", "?<=", "?>", and "?>=") that return bit
or std_ulogic results
. This allows us to rewrite the assignment as:
control_sig <= X ?= Y;
The matching relational operators are predefined with the following signatures for scalar operands:
[ScalarType, ScalarType return ScalarType]
ScalarType is one of bit or std_ulogic.
For bit operands, the results are the same as the ordinary relational operators, except that the matching relational versions return ‘0’ or ‘0’ instead of false or true. For std_ulogic
operands, the result values are shown in Tables 4.1, 4.2, and 4.3 VHDL-2008 lists the result values for the “?=” and “?<” operators, and then defines the results for the remaining operators using the not and or operators for std_ulogic. Note that for “?<”, “?<=”, “?>”, and “?>=”, an operand value of ‘-’ produces an assertion-violation error, so the seemingly anomalous results shown in Tables 4.2 and 4.3 are not a concern. They are defined for completeness in case we chose to ignore assertion violations during simulation.
Table 4.1. Result values for the “?=” and “?/=” operators on std_ulogic operands
?= | Right | ?/= | Right | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Left | ‘U’ | ‘X’ | ‘o’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘–’ | ‘Left’ | ‘U’ | ‘X’ | ‘o’ | ‘1’ | ’Z’ | ’W’ | ’L’ | ’H’ | ’– ‘ |
‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ’1‘ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘0’ |
‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ | ‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ |
‘0’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘1’ | ‘0’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘0’ |
‘1’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘1’ | ‘1’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘0’ |
‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ | ‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ |
‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ | ‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ |
‘L’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘1’ | ‘L’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘0’ |
‘H’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘1’ | ‘H’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘0’ |
‘—’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘—’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ |
Table 4.2. Result values for the “?<” and “?<= ” operators on std_ulogic operands
?< | Right | ?<= | Right | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Left | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘—’ | ‘Left’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘—’ |
‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘X’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘1’ |
‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ |
‘0’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘0’ | ‘U’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ | ‘X’ | ‘1’ | ‘1’ | ‘1’ |
‘1’ | ‘U’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘1’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘1’ |
‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ |
‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘1’ |
‘L’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘L’ | ‘U’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ | ‘X’ | ‘1’ | ‘1’ | ‘1’ |
‘H’ | ‘U’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘H’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘X’ | ‘X’ | ‘0’ | ‘1’ | ‘1’ |
‘—’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘—’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ | ‘1’ |
Table 4.3. Result values for the “?>” and “?>=” operators on std_ulogic operands
?> | Right | ?>= | Right | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Left | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘—’ | ‘Left’ | ‘U’ | ‘X’ | ‘0’ | ‘1’ | ‘Z’ | ‘W’ | ‘L’ | ‘H’ | ‘—’ |
‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘0’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘U’ | ‘X’ |
‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ | ‘X’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ |
‘0’ | ‘U’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘X’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ |
‘1’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘0’ | ‘1’ | ‘U’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ |
‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ | ‘Z’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ |
‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘0’ | ‘W’ | ‘U’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ |
‘L’ | ‘U’ | ‘X’ | ‘0’ | ‘0’ | ‘X’ | ‘X’ | ‘0’ | ‘0’ | ‘0’ | ‘L’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ |
‘H’ | ‘U’ | ‘X’ | ‘1’ | ‘0’ | ‘X’ | ‘X’ | ‘1’ | ‘0’ | ‘0’ | ‘H’ | ‘U’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ | ‘X’ | ‘1’ | ‘1’ | ‘X’ |
‘—’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘0’ | ‘—’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ | ‘X’ |
In addition, the “?=” and “?/=” operators are predefined with the signature:
[ArrayType, ArrayType return ArrayElementType ]
where ArrayType is any one dimensional array of bit or std_ulogic
elements. The array operands must be of the same length. The array “?=” operator applies the scalar “?=” operator to corresponding elements of the arrays, and then forms the logical AND of the resulting values. The array “?/=” operator does the same, but negates the final result.
We can also overload all of these operators. In particular, the “?<”, “?<=”, “?>”, and “?>=” operators are not predefined for arrays of bit or std_ulogic
elements; instead they are overloaded in the appropriate numeric packages. They are overloaded for signed and unsigned in numeric_bit
and numeric_std;
for ufixed and sfixed in fixed_generic_ pkg;
for float in float_generic_pkg;
for bit_vector
in numeric_bit_unsigned;
and for std_ulogic_vector
in numeric_std_unsigned.
(See Chapter 8 for more details about the standard packages.) Thus, we must include a use clause for the appropriate package if we want to apply the operators to bit_vector
or std_logic
vector operands.
Example 4.5. Assignment of a condition result for a select signal
We can write a Boolean equation for a std_ulogic
select signal that includes chip-select control signals and an address signal. In earlier versions of VHDL, we had to write an assignment in the following form, including a condition of type boolean:
dev_sell <= '1' when cs1 = '1' and ncs2 = '0' and addr = X"A5" else '0';
In VHDL-2008, we can use the "?="
operator, which returns a std_ulogic
result. We can combine that result with the std_ulogic
control signals to produce a std_ulogic
form of the Boolean equation:
dev_sel1 <= cs1 and not ncs2 and addr ?= X"A5";
We can also use this form of expression in a condition, since the condition operator, "??"
(see Section 4.4), is implicitly applied:
if cs1 and not ncs2 and addr ?= X"A5" then . . .
or similarly:
if cs1 and ncs2 ?= '0' and addr ?= X"A5" then . . .
Note that, in a condition, we still have backward compatibility. The following forms are still valid:
if cs1 = '1' and ncs2 = '0' and addr = X"A5" then . . .
and
if (cs1 and not ncs2) = '1' and addr = X"A5" then . . .
If we want to find the larger or the smaller of two values, we can write an if statement, such as:
if A > B then greater := A; else greater := B; end if;
However, in some cases, it would be more convenient to select the larger or smaller value as part of an expression. VHDL-2008 allows us to do so using new predefined maximum
and minimum
functions, with the following signatures:
[ScalarType, ScalarType return ScalarType ] [DiscreteArrayType, DiscreteArrayType return DiscreteArrayType]
Here, ScalarType is any scalar type, and DiscreteArrayType is any discrete array type (that is, an array type whose elements are of an integer or enumeration type). These are the types for which the operator "<"
is predefined, and the results of the maximum
and minimum
functions are defined in terms of the "<"
operator applied to the operands. For example:
maximum(3, 20) = 20 minimum(3, 20) = 3 maximum('a', 'z')= 'z' minimum('a', 'z') = 'a'
Note that for array types, the "<"
operator uses dictionary ordering to compare operands. The two arrays do not have to be of the same length. Corresponding elements are compared, from left to right, until a pair with differing values is encountered (in which case the lesser array is the one containing the lesser element of the pair) or until the end of one array is reached (in which case the lesser array is the shorter of the two). Since the maximum
and minimum
functions are defined in terms of the "<"
operator, they also use dictionary ordering for arrays, for example:
maximum(bit_vector' ("l01"), bit_vector' ("100100")) = "101" minimum(bit_vector' ("101"), bit_vector' ("100100")) = "100100"
As these examples show, for vectors representing binary-coded numeric values, the predefined maximum
and minimum
functions are not consistent with numerical ordering. (The same argument also applies to the predefined relational operators.) For this reason, the standard numeric packages define overloaded versions of maximum
and minimum
(and the relational operators) that do give results consistent with numerical ordering.
Example 4.6. Maximum Function Used In A Declaration
One use of the maximum
and minimum
is in expressions in declarations. For example, in the following function for saturating addition of unsigned numeric values, we use the maximum
function to determine the longer of the two operand values, and then declare the result variable to be of that size.
function saturating_add (A, B : unsigned) return unsigned is constant size : natural := maximum(A'length, B'length); variable result : unsigned(size - 1 downto 0); variable c_out : std_ulogic; begin (c_out, result) := ('0' & A) + ('0' & B); if c_out then result := (others => '1'), end if; return result; end function saturating_add;
VHDL-2008 also predefines the maximum
and minimum
functions as reduction operations on array values. The signature is:
[ArrayType return ArrayElementType ]
Here, ArrayType is an array of any scalar element type (not just a discrete type), and ArrayElementType is the element type. The maximum
function of this form returns the largest element in the array, and the minimum
function returns the smallest element in the array. Again, the comparisons are performed using the predefined "<"
operator for the element type. Thus,
maximum(string'("WYZ")) = 'Z' minimum(string'("WXyZ")) = 'W' maximum(time_vector'(10 ns, 50 ns, 20 ns)) = 50 ns minimum(time_vector'(10 ns, 50 ns, 20 ns)) = 10 ns
For a null array (one with no elements), the maximum
function returns the smallest value of the element type, and the minimum
functions returns the largest value of the element type.
Prior to VHDL-2008, the arithmetic operators "+", "-", "*"
and "/"
were predefined for physical types, including type time
, but the mod and rem operators were not predefined. VHDL-2008 adds predefined mod and rem functions for these types, with the following signature.:
[PhysicalType, PhysicalType return PhysicalType ]
For example, using type time:
5 ns rem 3 ns = 2 ns 5 ns mod 3 ns = 2 ns (-5 ns)rem 3 ns = -2 ns (-5 ns)mod 3 ns = 1 ns 1 ns mod 300 ps = 100 ps (-1 ns)mod 300 ps = 200 ps
Example 4.7. Generating a Periodic Waveform
We can use the mod operator to simplify generation of a periodic waveform. For example, the following process creates a triangle wave on the real signal triangle_wave. T_period_wave
defines the period of the output wave, t_offset
defines the offset within the triangle wave, and t_period_sample
defines how many points are in the waveform.
signal triangle_wave : real; . . . wave_proc : process is variable phase : time; begin phase := (now + t_offset) mod t_period_wave; if phase <= t_period_wave/2 then triangle_wave <= 4.0 * real(phase / t_period_wave) - 1.0; else triangle_wave <= 3.0 - 4.0 * real(phase / t_period_wave); end if; wait for tperiod_sample; end process wave_proc;
Prior to VHDL-2008, the shift operations (rol, ror, sll, srl, sla, and sra) were predefined only for arrays of bit
and boolean
elements. The operations can take a positive shift count, in which case they rotate or shift in the direction suggested by the operator name. They can also take a negative shift count, in which case they rotate or shift in the opposite direction. The rotate and logical-shift operations have the expected meanings. The arithmetic-shift operators also have the expected meaning when shifting right; that is, they replicate the leftmost bit. However, when shifting left, they replicate the rightmost bit, treating it as a sign bit. This seems anomalous to many designers, so the operation is rarely (if ever) used.
The numeric_bit
and numeric_std
packages used in earlier versions of VHDL defined overloaded versions of the rol, ror, sll, and srl operators with similar behavior to that of the predefined operators on bit_vector
values. The packages did not, however, overload sla and sra, preferring instead to define shift_left
and shift_right
functions that perform logical shifts on unsigned
values and arithmetic shifts on signed
values. A
shift left on a signed
value fills the vacated positions with ‘O’, rather than replicating the rightmost bit. This is generally more appropriate for arithmetic circuits.
VHDL-2008 extends the definitions of shift operations to include sla and sra in numeric_bit
and numeric_std.
It also defines all of the shift operations in the new arithmetic packages numeric_bit_unsigned, numeric_std_unsigned
, and in the fixed-point packages (see Chapter 8). In all cases, the overloaded sla and sra operators on signed values have the same numeric behavior as the shift_left
and shift_right
functions.
Prior to VHDL-2008, the strength reduction and ‘X’ detection functions were not uniformly implemented throughout the packages based on the std_ulogic
type. The package std_logic_1164
defined the detection function is_X
and the strength reduction functions to_X01, to_X01
Z, and to_UX01
for scalar and vector types. The numeric_std
package, however, did not define these functions for unsigned
or signed.
Instead, we had to convert values of those types to std_logic_vector
in order to use the functions. The package did, however, define the function to_01
that maps non-logic values to a value of our choice (the default being ‘0’).
In VHDL-2008, the inconsistency is rectified, and the functions are also defined in the new fixed-point and floating-point packages based on the std_ulogic
type (see Chapter 8). To summarize, the following functions are defined in the packages:
is_X [AType return boolean] to_X01 [AType return AType] to_X01Z [AType return AType] to_UX01 [AType return AType]
In std_logic_1164
, AType covers std_ulogic, std_logic, std_ulogic_vector
and std_logic_vector;
in numeric_std
, AType covers unsigned
and signed;
in the fixed-point packages, AType covers ufixed
and sfixed;
and in the floating-point packages, AType covers float
and its subtypes.
In addition, the function to_01
is defined with the following signature:
to_01 [AType, std_ulogic return AType ]
The second parameter is the value to which non-logic values such as ‘X’ are mapped. This function is defined in packages numeric_std, numeric_std_unsigned
, the fixed-point packages, and the floating-point packages, with the same types for AType as the other strength-reduction functions.
Example 4.8. Strength reduction and ‘X’ detection in models
We can use the to_X01
function in behavioral models and ASIC or FPGA input cells to promote a resistive strength to a driving level as follows:
ncs_x01 <= to_X01(ncs);
We can use the is_X
function to detect ‘X’ values in behavioral models and RTL code, for example, in the input to a state machine:
assert not is_X(ncs) report "ncs is X" severity error;