Previous

Operands

Operands determine the data that the operator uses to compute its value. An operand is said to return its value to the operator.

There are many categories of operands. The simplest operand is a literal, such as the number 7, or an identifier, such as a variable or signal name. An operand itself can be an expression. You create expression operands by surrounding an expression with parentheses.

The operand categories follow.

The next two sections discuss operand bit widths and explain computable operands. Subsequent sections describe the operand types listed above.

Operand Bit Width

Foundation Express uses the bit width of the largest operand to determine the bit width needed to implement an operator in hardware. For example, an INTEGER operand is 32 bits wide by default. An addition of two INTEGER operands causes Foundation Express to build a 32-bit adder.

To use hardware resources efficiently, always indicate the bit width of numeric operands. For example, use a subrange of INTEGER when declaring types, variables, or signals.

   type     ENOUGH:  INTEGER range 0 to 255; 
   variable WIDE:    INTEGER range -1024 to 1023; 
   signal   NARROW:  INTEGER range 0 to 7; 

NOTE

During optimization, Foundation Express removes hardware for unused bits.


Computable Operands

Some operators, such as the division operator, restrict their operands to be computable. A computable operand is one whose value can be determined by Foundation Express. Computability is important because noncomputable expressions can require logic gates to determine their value.

Examples of computable operands follow.

Additionally, a variable is given a computable value if it is an OUT or INOUT parameter of a procedure that assigns it a computable value.

Examples of noncomputable operands follow.

The example below shows some definitions and declarations, followed by several computable and noncomputable expressions.

   signal S: BIT;
   . . .
   function MUX(A, B, C: BIT) return BIT is
   begin
        if (C = '1') then 
          return(A);
        else 
              return(B);
        end if;
   end;
   
   procedure COMP(A: BIT; B: out BIT) is
   begin
        B := not A;
   end;
   
   process(S)
           variable V0, V1, V2: BIT;
        variable V_INT:      INTEGER;
   
        subtype MY_ARRAY is BIT_VECTOR(0 to 3);
        variable V_ARRAY:    MY_ARRAY;
   begin
        V0 := '1';             -- Computable (value is '1')
        V1 := V0;              -- Computable (value is '1')
        V2 := not V1;          -- Computable (value is '0')
   
     for I in 0 to 3 loop
          V_INT := I;          -- Computable (value depends
     end loop;              --   on iteration)
   
     V_ARRAY := MY_ARRAY'(V1, V2, '0', '0');
                            -- Computable ("1000")
     V1 := MUX(V0, V1, V2); -- Computable (value is '1')
     COMP(V1, V2);
     V1 := V2;              -- Computable (value is '0')
     V0 := S and '0';       -- Computable (value is '0')
     V1 := MUX(S, '1', '0');-- Computable (value is '1')
     V1 := MUX('1', '1', S);-- Computable (value is '1')
   
     if (S = '1') then
          V2 := '0';           -- Computable (value is '0')
     else
          V2 := '1';           -- Computable (value is '1')
     end if;
     V0 := V2;            -- Noncomputable; V2 depends
                                                 --   on S
     V1 := S;             -- Noncomputable; S is signal 
     V2 := V1;            -- Noncomputable; V1 is no
                                                  --   longer computable
   end process;

Literals

A literal (constant) operand can be a numeric literal, a character literal, an enumeration literal, or a string literal. The following sections describe these four kinds of literals.

Numeric Literals

Numeric literals are constant integer values. The two kinds of numeric literals are decimal and based. A decimal literal is written in base 10. A based literal can be written in a base from 2 to 16 and is composed of the base number, an octothorpe (#), the value in the given base, and another octothorpe (#); for example, 2#101# is decimal 5.

The digits in either kind of numeric literal can be separated by an underscore ( _ ) character. The example below shows several different numeric literals, all representing the same value.

   170
   1_7_0
   10#170#
   2#1010_1010#
   16#AA#

Character Literals

Character literals are single characters enclosed in single quotation marks, for example, `A'. Character literals can be used as values for operators and to define enumerated types, such as CHARACTER and BIT. See the “Data Types” chapter for more information about the legal character types.

Enumeration Literals

Enumeration literals are values of enumerated types. The two kinds of enumeration literals are character literals and identifiers. Character literals were described previously. Enumeration identifiers are those literals listed in an enumeration type definition. The following example shows an enumeration type definition,

type SOME_ENUM is ( ENUM_ID_1, ENUM_ID_2, ENUM_ID_3);

If two enumerated types use the same literals, those literals are said to be overloaded. You must qualify overloaded enumeration literals (see the “Qualified Expressions” section of this chapter) when you use them in an expression unless their type can be determined from context. See the “Data Types” chapter for more information.

The example below defines two enumerated types and shows some enumeration literal values.

   type ENUM_1 is (AAA, BBB, 'A', 'B', ZZZ);
   type ENUM_2 is (CCC, DDD, 'C', 'D', ZZZ);
   
   AAA           -- Enumeration identifier of type ENUM_1
   'B'           -- Character literal of type ENUM_1
   CCC           -- Enumeration identifier of type ENUM_2
   'D'           -- Character literal of type ENUM_2
   ENUM_1'(ZZZ)  -- Qualified because overloaded

String Literals

String literals are one-dimensional arrays of characters, enclosed in double quotes (" "). The two kinds of string literals are character strings and bit strings. Character strings are sequences of characters in double quotes; for example, "ABCD." Bit strings are similar to character strings, but represent binary, octal, or hexadecimal values; for example, B"1101", O"15", and X"D" all represent decimal value 13.

A string value's type is a one-dimensional array of an enumerated type. Each of the characters in the string represents one element of the array. The following example shows character-string literals.

   "10101"
   "ABCDEF"

NOTE

Null string literals ("") are not supported.


Bit strings, like based numeric literals, are composed of a base specific character, a double quotation mark, a sequence of numbers in the given base, and another double quotation mark. For example, B"0101" represents the bit vector 0101. A bit-string literal consists of the base specifier B, O, or X, followed by a string literal. The bit-string literal is interpreted as a bit vector, a one-dimensional array of the predefined type BIT. The base specifier determines the interpretation of the bit string as follows.

The value is in hexadecimal digits (0 to 9 and A to F). Each hexadecimal digit in the string represents four BITs in the generated bit vector (array).

You can separate the digits in a bit-string literal value with underscores ( _ ) for readability. The example below shows several bit-string literals that represent the same value.

    X"AAA"
   B"1010_1010_1010"
   
   O"5252"
   B"101_010_101_010"

Identifiers

Identifiers are probably the most common operand. An identifier is the name of a constant, variable, signal, entity, port, subprogram, or parameter and returns the object's value to an operand.

The example below shows several kinds of identifiers and their usage. All identifiers are shown in boldface.

   entity EXAMPLE is
        port (INT_PORT:   in INTEGER;
                 BIT_PORT:  out BIT);
   end;
   . . .
   signal   BIT_SIG: BIT;
   signal   INT_SIG: INTEGER;
   . . .
   INT_SIG  <= INT_PORT;   -- Signal assignment from port
   BIT_PORT <= BIT_SIG;    -- Signal assignment to port
   
   function FUNC(INT_PARAM:  INTEGER)
          return INTEGER;
   end function;
   . . .
   constant CONST:   INTEGER := 2;
   variable VAR:     INTEGER;
   . . .
   VAR := FUNC(INT_PARAM => CONST);  -- Function call

Indexed Names

An indexed name identifies one element of an array variable or signal. Slice names identify a sequence of elements in an array variable or signal; aggregates create array literals by giving a value to each element of an instance of an array type. Slice names and aggregates are described in the next two sections.

The syntax of an indexed name follows.

identifier ( expression )

Identifier must name a signal or variable of an array type. The expression must return a value within the array's index range. The value returned to an operator is the specified array element.

If the expression is computable (see the “Computable Operands” section of this chapter), the operand is synthesized directly. If the expression is not computable, hardware that extracts the specified element from the array is synthesized.

The example below shows two indexed names - one computable and one not computable. The figure for the resulting circuit follows the example.

   signal A, B: BIT_VECTOR(0 to 3);
   signal I:    INTEGER range 0 to 3;
   signal Y, Z: BIT;
   
   Y <= A(I);  -- Noncomputable index expression
   Z <= B(3);  -- Computable index expression

Figure 5.8 Circuit for Indexed Name Operands

You can also use indexed names as assignment targets; see the “Indexed Name Targets” section of the “Sequential Statements” chapter.

Slice Names

Slice names return a sequence of elements in an array. The syntax follows.

identifier ( expression direction expression )

Identifier must name a signal or variable of an array type. Each expression must return a value within the array's index range and must be computable. See the “Computable Operands” section of this chapter.

The direction must be either to or downto. The direction of a slice must be the same as the direction of the identifier array type. If the left and right expressions are equal, define a single element.

The value returned to an operator is a subarray containing the specified array elements.

The example below uses slices to assign an eight-bit input to an eight-bit output, exchanging the lower and upper four bits. The figure for the resulting circuit follows the example.

   signal A, Z: BIT_VECTOR(0 to 7);
   
   Z(0 to 3) <= A(4 to 7);
   Z(4 to 7) <= A(0 to 3);

Figure 5.9 Circuit for Slice Name Operands

In the slice name operands example, slices are also used as assignment targets. This usage is described in the “Slice Targets” section of the “Sequential Statements” chapter.

Limitations on Null Slices

Foundation Express does not support null slices. A null slice is indicated by a null range, such as (4 to 3), or a range with the wrong direction, such as UP_VAR(3 downto 2) when the declared range of UP_VAR is ascending. The following example shows three null slices and one noncomputable slice.

   subtype DOWN is BIT_VECTOR(4 downto 0); 
   subtype UP   is BIT_VECTOR(0 to 7);
   . . .
   variable UP_VAR:   UP;
   variable DOWN_VAR: DOWN;
   . . .
   UP_VAR(4 to 3)       -- Null slice (null range)
   
   UP_VAR(4 downto 0)   -- Null slice (wrong direction)
   DOWN_VAR(0 to 1)     -- Null slice (wrong direction)
   . . .
   
   variable I: INTEGER range 0 to 7;
   . . .
   UP_VAR(I to I+1)     -- Noncomputable slice

Limitations on Noncomputable Slices

IEEE VHDL does not allow noncomputable slices - slices whose range contains a noncomputable expression.

Records and Fields

Records are composed of named fields of any type. For more information, see the “Record Types” section of the “Data Types” chapter.

In an expression, you can refer to a record as a whole, or you can refer to a single field. The syntax of field names follows.

record_name.field_name

record_name is the name of the record variable or signal, and field_name is the name of a field in that record type. A field_name is separated from the record name by a period (.). Note that a record_ name is different for each variable or signal of that record type. A field_name is the field name defined for that record type.

The example below shows a record type definition and record and field access.

   type BYTE_AND_IX is 
        record
            BYTE: BIT_VECTOR(7 downto 0);
            IX:   INTEGER range 0 to 7;
        end record;
    
   signal X: BYTE_AND_IX;
   . . .
   X           -- record
   X.BYTE      -- field: 8-bit array
   X.IX        -- field: integer

A field can be any type - including an array, record, or aggregate type. Refer to a field element with that type's notation as in the following example.

   X.BYTE(2)           -- one element from array field BYTE
   X.BYTE(3 downto 0)  -- 4-element slice of array field BYTE

Aggregates

Aggregates are considered array literals, because they specify an array type and the value of each array element. The syntax follows.

   type_name' ([choice=>] expression 
             {, [choice =>] expression})

The syntax is more restrictive than the syntax in the library reference manual. type_name must be a constrained array type. The optional choice specifies an element index, a sequence of indexes, or others. Each expression provides a value for the chosen elements and must evaluate to a value of the element's type.

The example below shows an array type definition and an aggregate representing a literal of that array type. The two sets of assignments have the same result.

   subtype MY_VECTOR is BIT_VECTOR(1 to 4);
   signal X:      MY_VECTOR;
   variable A, B: BIT;
   
   X <= MY_VECTOR'('1', A nand B, '1', A or B)  -- Aggregate
                                                -- assignment
   ...
   X(1) <= '1';                                 -- Element
   X(2) <= A nand B;                            -- assignment
   X(3) <= '1';
   X(4) <= A or B;

You can specify an element's index with either positional or named notation. With positional notation, each element is given the value of its expression in order, as shown in the example above.

With named notation, the choice => construct specifies one or more elements of the array. The choice can contain an expression (such as (I mod 2) =>) to indicate a single element index or a range (such as 3 to 5 => or 7 downto 0 =>) to indicate a sequence of element indexes.

An aggregate can use both positional and named notation, but positional expressions must appear before named (choice) expressions.

It is not necessary to specify all element indexes in an aggregate. All unassigned values are given a value by including others => expression as the last element of the list.

The example below shows several aggregates representing the same value.

   subtype MY_VECTOR is BIT_VECTOR(1 to 4);
   
   MY_VECTOR'('1', '1', '0', '0');
   MY_VECTOR'(2 => '1', 3 => '0', 1 => '1', 4 => '0');
   MY_VECTOR'('1', '1', others => '0');
   MY_VECTOR'(3 => '0', 4 => '0', others => '1');
   MY_VECTOR'(3 to 4 => '0', 2 downto 1 => '1');

The others expression must be the only expression in the aggregate. The following example shows two equivalent aggregates.

   MY_VECTOR'(others => '1');
   MY_VECTOR'('1', '1', '1', '1');

To use an aggregate as the target of an assignment statement, see the “Aggregate Targets” section of the “Sequential Statements” chapter.

Attributes

VHDL defines attributes for various types. A VHDL attribute takes a variable or signal of a given type and returns a value. The syntax of an attribute follows.

object'attribute

Foundation Express supports the following predefined VHDL attributes for use with arrays, as described under “Array Types” section of the “Data Types” chapter.

Foundation Express also supports the following predefined VHDL attributes to use with wait and if statements, as described in the “Register and Three-State Inference” chapter.

In addition to supporting the predefined VHDL attributes listed above, Foundation Express has a defined set of synthesis-related attributes. These Foundation Express-specific attributes can be placed in your VHDL design description to direct optimization.

Function Calls

A function call executes a named function with the given parameter values. The value returned to an operator is the function's return value. The syntax of a function call follows.

function_name ( [parameter_name =>] expression
{, [parameter_name =>] expression }

function_name is the name of a defined function. The optional parameter_name is an expression of formal parameters, as defined by the function. Each expression provides a value for its parameter and must evaluate to a type appropriate for that parameter.

You can specify parameters in positional or named notation, like aggregate values.

In positional notation, the parameter_name => construct is omitted. The first expression provides a value for the function's first parameter, the second expression provides a value for the second parameter, and so on.

In named notation, parameter_name => is specified before an expression; the named parameter gets the value of that expression.

You can mix positional and named expressions in the same function call, as long as all positional expressions appear before the named parameter expressions.

Function calls are implemented by logic unless you use the map_to_entity compiler directive. For more information, see the “Mapping Subprograms to Components (Entities)” section of the “Sequential Statements” chapter and the “Component Implication Directives” section of the “Foundation Express Compiler Directives” chapter.

The following example shows a function declaration and several equivalent function calls.

   function FUNC(A, B, C: INTEGER) return BIT;
   . . .
   FUNC(1, 2, 3)
   FUNC(B => 2, A => 1, C => 7 mod 4)
   FUNC(1, 2, C => -3+6)

Qualified Expressions

Qualified expressions state the type of an operand to resolve ambiguities in an operand's type. You cannot use qualified expressions for type conversion. (See the “Type Conversions” section of this chapter.)

The syntax of a qualified expression follows.

type_name'(expression)

type_name is the name of a defined type. Expression must evaluate to a value of an appropriate type.


NOTE

A single quote, or tick, must appear between type_name and (expression). If the single quote is omitted, the construction is interpreted as a type conversion. (See the “Type Conversions” section of this chapter.)


The following example shows a qualified expression that resolves an overloaded function by qualifying the type of a decimal literal parameter.

   type R_1 is range 0 to 10;  -- Integer 0 to 10
   type R_2 is range 0 to 20;  -- Integer 0 to 20
   
   function FUNC(A: R_1) return BIT;
   function FUNC(A: R_2) return BIT;
   
   FUNC(5)         -- Ambiguous; could be of type R_1, 
                   --   R_2, or INTEGER
   
   FUNC(R_1'(5))   -- Unambiguous

The following example shows how qualified expressions resolve ambiguities in aggregates and enumeration literals.

   type ARR_1 is array(0 to 10) of BIT;
   type ARR_2 is array(0 to 20) of BIT;
   . . .
   (others => '0')        -- Ambiguous; could be of
                          -- type ARR_1 or ARR_2
   
   ARR_1'(others => '0')  -- Qualified; unambiguous
   ------------------------------------------------------
   type ENUM_1 is (A, B);
   type ENUM_2 is (B, C);
   . . .
   B                      -- Ambiguous; could be of 
                          -- type ENUM_1 or ENUM_2
   
   ENUM_1'(B)             -- Qualified; unambiguous

Type Conversions

Type conversions change an expression's type. Type conversions are different from qualified expressions, because they change the type of their expression; whereas qualified expressions simply resolve the type of an expression.

The syntax of a type conversion follows.

type_name (expression)

type_name is the name of a defined type. The expression must evaluate to a value of a type that can be converted into type type_name. The following conditions apply to type conversions.

The following example shows some type definitions and associated signal declarations, followed by legal and illegal type conversions.

   type INT_1 is range 0 to 10;
   type INT_2 is range 0 to 20;
   
   type ARRAY_1 is array(1 to 10) of INT_1;
   type ARRAY_2 is array(11 to 20) of INT_2;
   
   subtype MY_BIT_VECTOR is BIT_VECTOR(1 to 10);
   type BIT_ARRAY_10 is array(11 to 20) of BIT;
   type BIT_ARRAY_20 is array(0 to 20) of BIT;
   
   signal S_INT:      INT_1;
   signal S_ARRAY:    ARRAY_1;
   signal S_BIT_VEC:  MY_BIT_VECTOR;
   signal S_BIT:      BIT;
   
                      -- Legal type conversions
   
   INT_2(S_INT)   
                 -- Integer type conversion
   
   BIT_ARRAY_10(S_BIT_VEC)
        -- Similar array type conversion
                      -- Illegal type conversions
   
   BOOLEAN(S_BIT);  
     -- Can't convert between enumerated types
   
   INT_1(S_BIT);
        -- Can't convert enumerated types to other types
   
   BIT_ARRAY_20(S_BIT_VEC); 
     -- Array lengths not equal
   
   ARRAY_1(S_BIT_VEC);  
     -- Element types cannot be converted 
Next