Search code examples
vhdl

no function declarations for operator


I get this error message:

testbench.vhd:16:22: no function declarations for operator "+"

at this line:

    Z <= unsigned(X) + resize(unsigned(Y),X'length);

with this code:

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity MCVE is
end entity MCVE;

architecture MCVE of MCVE is
  signal X, Z : std_logic_vector(15 downto 0);
  signal Y    : std_logic_vector(7 downto 0);
begin

  process    
  begin
    Z <= unsigned(X) + resize(unsigned(Y),X'length);
  end process;

end architecture MCVE;

https://www.edaplayground.com/x/2LBg

I don't understand why.


Solution

  • While Giwrgos Rizeakos original question was on resizing, there's a long history of questions and answers on operator and function overload visibility (there no function declaration visible here for operator "+").

    After a search through those questions and answers this answer attempts to provide a question and answer pair that can be used to eliminated future duplicate questions based on providing authoritative references in an explanatory flow.

    The problem starts with visibility:

    12.3 Visibility

    The meaning of the occurrence of an identifier at a given place in the text is defined by the visibility rules and also, in the case of overloaded declarations, by the overloading rules. The identifiers considered in this subclause include any identifier other than a reserved word or an attribute designator that denotes a predefined attribute. The places considered in this subclause are those where a lexical element (such as an identifier) occurs. The overloaded declarations considered in this subclause are those for subprograms and enumeration literals.

    For each identifier and at each place in the text, the visibility rules determine a set of declarations (with this identifier) that define the possible meanings of an occurrence of the identifier. A declaration is said to be visible at a given place in the text when, according to the visibility rules, the declaration defines a possible meaning of this occurrence. The following two cases arise in determining the meaning of such a declaration:

    — The visibility rules determine at most one possible meaning. In such a case, the visibility rules are sufficient to determine the declaration defining the meaning of the occurrence of the identifier, or in the absence of such a declaration, to determine that the occurrence is not legal at the given point.
    — The visibility rules determine more than one possible meaning. In such a case, the occurrence of the identifier is legal at this point if and only if exactly one visible declaration is acceptable for the overloading rules in the given context or all visible declarations denote the same named entity.

    It involves overloaded subprograms:

    4.5.2 Operator overloading

    The declaration of a function whose designator is an operator symbol is used to overload an operator. The sequence of characters of the operator symbol shall be one of the operators in the operator classes defined in 9.2.

    The subprogram specification of a unary operator shall have a single parameter, unless the subprogram specification is a method (see 5.6.2) of a protected type. In this latter case, the subprogram specification shall have no parameters. The subprogram specification of a binary operator shall have two parameters, unless the subprogram specification is a method of a protected type, in which case, the subprogram specification shall have a single parameter. If the subprogram specification of a binary operator has two parameters, for each use of this operator, the first parameter is associated with the left operand, and the second parameter is associated with the right operand.

    Operator overloads are defined as subprograms and use subprogram overloading rules:

    4.5 Subprogram overloading

    4.5.1 General

    Two formal parameter lists are said to have the same parameter type profile if and only if they have the same number of parameters, and if at each parameter position the corresponding parameters have the same base type. Two subprograms are said to have the same parameter and result type profile if and only if both have the same parameter type profile, and if either both are functions with the same result base type or neither of the two is a function.

    As a segue subprograms can be described in terms of signatures as a shorthand:

    4.5.3 Signatures

    A signature distinguishes between overloaded subprograms and overloaded enumeration literals based on their parameter and result type profiles. A signature can be used in a subprogram instantiation declaration, attribute name, entity designator, or alias declaration.

    signature ::= [ [ type_mark { , type_mark } ] [ return type_mark ] ]

    (Note that the initial and terminal brackets are part of the syntax of signatures and do not indicate that the entire right-hand side of the production is optional.) A signature is said to match the parameter and the result type profile of a given subprogram if, and only if, all of the following conditions hold:

    — The number of type marks prior to the reserved word return, if any, matches the number of formal parameters of the subprogram.
    — At each parameter position, the base type denoted by the type mark of the signature is the same as the base type of the corresponding formal parameter of the subprogram.
    — If the reserved word return is present, the subprogram is a function and the base type of the type mark following the reserved word in the signature is the same as the base type of the return type of the function, or the reserved word return is absent and the subprogram is a procedure.

    And here the example:

    Z <= unsigned(X) + resize(unsigned(Y),X'length);
    

    can be described in terms of a signature.

    Z is declared as type std_logic_vector. The left operand to "+" is X which is subject type conversion to unsigned (rules given in 9.3.6, both array types, same element type). The right operand is the result of resize with a signature of [unsigned, natural, return unsigned] found in package numeric_std by overload resolution:

    12.5 The context of overload resolution

    Overloading is defined for names, subprograms, and enumeration literals.

    ...

    When considering possible interpretations of a complete context, the only rules considered are the syntax rules, the scope and visibility rules, and the rules of the form as follows:

    a) Any rule that requires a name or expression to have a certain type or to have the same type as another name or expression.
    b) Any rule that requires the type of a name or expression to be a type of a certain class; similarly, any rule that requires a certain type to be a discrete, integer, floating-point, physical, universal, or character type.
    ...
    e) The rules given for the resolution of overloaded subprogram calls; for the implicit conversions of universal expressions; for the interpretation of discrete ranges with bounds having a universal type; for the interpretation of an expanded name whose prefix denotes a subprogram; and for a subprogram named in a subprogram instantiation declaration to denote an uninstantiated subprogram.
    ...

    And the function made visible through the use of use clause making the declarations in numeric_std visible:

    12.4 Use clauses

    A use clause achieves direct visibility of declarations that are visible by selection.

    The same overload resolution is performed on the example where no function declaration is found. No function declaration is found for "+" [unsigned, unsigned return std_logic_vector] where the return value must match type std_logic_vector (the a) rule above).

    And as Matthew Taylor points out you can alter the assignment right hand expression by type conversion to std_logic_vector:

    Z <= std_logic_vector(unsigned(X) + resize(unsigned(Y),X'length));
    

    And why the resize function call is not needed can authoritatively be shown by referencing the function declaration for "+" `[unsigned, unsigned return unsigned] found in IEEE package numeric_std, (found in numeric_std-body.vhdl, in the 1076-2008 downloads.zip which is part of the -2008 standard):

      -- Id: A.3R
      function "+" (L : UNRESOLVED_UNSIGNED; R : STD_ULOGIC)
        return UNRESOLVED_UNSIGNED
      is
        variable XR : UNRESOLVED_UNSIGNED(L'length-1 downto 0) := (others => '0');
      begin
        XR(0) := R;
        return (L + XR);
      end function "+";
    

    Interpreting that authoritatively requires the the declaration for unsigned in numeric_std.vhdl (also found in the same above zip file):

       type UNRESOLVED_UNSIGNED is array (NATURAL range <>) of STD_ULOGIC;
    
    
        subtype UNSIGNED is (resolved) UNRESOLVED_UNSIGNED;
    

    which is a subtype of unresolved_unsigned providing an element resolution function name. Resolution functions are explained in the standard:

    4.6 resolution functions

    A resolution function is a function that defines how the values of multiple sources of a given signal are to be resolved into a single value for that signal. Resolution functions are associated with signals that require resolution by including the name of the resolution function in the declaration of the signal or in the declaration of the subtype of the signal. A signal with an associated resolution function is called a resolved signal (see 6.4.2.3).

    and the subtype declaration syntax can require a resolution function be found:

    6.3 Subtype declarations

    subtype_declaration ::=
        subtype identifier is subtype_indication ;

    subtype_indication ::=
        [ resolution_indication ] type_mark [ constraint ]

    resolution_indication ::=
        resolution_function_name | ( element_resolution )

    element_resolution ::= array_element_resolution | record_resolution

    and function resolved is found in the the std_logic_1164 package declaration (in the same above zip file, provided as part of the standard).

    Resolution is a matter for simulation, see 14.7 Execution of a model, particularly 14.7.3 Propagation of signal values and it's sub-clauses where resolution is applied to signals.

    (And about here you see why there can be a lot of incomplete answers that don't preclude future questions. Finding answers in the standard requires subject matter understanding and it's primary audiences are tool implementors and advanced users where VHDL syntax and semantics are defined concisely allowing it's use as a formal notation.)