Operator overloading

There are various kinds of arithmetic operators that can be useful: saturating, arbitrary size floating point, carry save, etc.

It is convenient to use the normal arithmetic operators for readability, rather than relying on function calls.

overload_declaration ::=

bind overload_operator function data_type function_identifier ( overload_proto_formals ) ;

overload_operator ::= + | ++ | | – – | * | ** | / | % | == | != | < | <= | > | >= | =

overload_proto_formals ::= data_type {, data_type}

The overload declaration allows the arithmetic operators to be applied to data types that are normally illegal for them, such as unpacked structures. It does not change the meaning of the operators for those types where it is legal to apply them. This means that such code does not change behavior when operator overloading is used.

The overload declaration links an operator to a function prototype. The arguments are matched, and the type of the result is then checked. Multiple functions can have the same arguments and different return types. If no expected type exists because the operator is in a self-determined context, then a cast must be used to select the correct function. Similarly, if more than one expected type is possible, due to nested operators, and could match more than one function, a cast must be used to select the correct function. An expected result type exists in any of the following contexts:

  • The right-hand side of an assignment or assignment expression
  • Actual input argument of a task or function call
  • Input port connection of a module, interface, or program
  • Actual parameter to a module, interface, program, or class
  • Relational operator with unambiguous comparison
  • Inside a cast

For example, suppose there is a structure type float:

typedef struct {
bit sign;
bit [3:0] exponent;
bit [10:0] mantissa;
} float;

The + operator can be applied to this structure by invoking a function as indicated in the overloading declarations below:

bind + function float faddif(int, float);
bind + function float faddfi(float, int);
bind + function float faddrf(real, float);
bind + function float faddrf(shortreal, float);
bind + function float faddfr(float, real);
bind + function float faddfr(float, shortreal);
bind + function float faddff(float, float);
bind + function float fcopyf(float); // unary +
bind + function float fcopyi(int); // unary +
bind + function float fcopyr(real); // unary +
bind + function float fcopyr(shortreal); // unary +
float A, B, C, D;
assign A = B + C; //equivalent to A = faddff(B, C);
assign D = A + 1.0; //equivalent to A = faddfr(A, 1.0);

The overloading declaration links the + operator to each function prototype according to the equivalent argument types in the overloaded expression, which normally must match exactly. The exception is if the actual argument is an integral type and there is only one prototype with a corresponding integral argument, the actual is implicitly cast to the type in the prototype.

Note that the function prototype does not need to match the actual function declaration exactly. If it does not, then the normal implicit casting rules apply when calling the function. For example, the fcopyi function can be defined with an int argument:

function float fcopyi (int I);
float o;
o.sign = i[31];
o.exponent = 0;
o.mantissa = 0;

return o;

Overloading the assignment operator also serves to overload implicit assignments or casting. Here these are using the same functions as the unary +.

bind = function float fcopyi(int); // cast int to float
bind = function float fcopyr(real); // cast real to float
bind = function float fcopyr(shortreal); // cast shortreal to float

The operators that can be overloaded are the arithmetic operators, the relational operators, and the assignment.

Note that the assignment operator from a float to a float cannot be overloaded here because it is already legal. Similarly, equality and inequality between floats cannot be overloaded. No format can be assumed for 0 or 1, so the user cannot rely on subtraction to give equality or on addition to give increments. Similarly, no format can be assumed for positive or negative, so comparison must be explicitly coded.

An assignment operator such as += is automatically built from both the + and = operators successively, where the = has its normal meaning. For example

float A, B;
bind + function float faddff(float, float);
always @(posedge clock) A += B; // equivalent to A = A + B

The scope and visibility of the overload declaration follow the same search rules as a data declaration. The overload declaration must be defined before use in a scope that is visible. The function bound by the overload declaration uses the same scope search rules as a function enabled from the scope where the operator is invoked.

<< Previous | Next >>