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;
endfunction

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

Operators and Expressions

Assignment operators

SystemVerilog assignment operator includes the C assignment operators and special bitwise assignment operators: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, <<<=, and >>>=. An assignment operator is semantically equivalent to a blocking assignment, with the exception that any left-hand side index expression is only evaluated once. For example:

a[i]+=2; // same as a[i] = a[i] +2;

In SystemVerilog, an expression can include a blocking assignment, provided it does not have a timing control, Such an assignment must be enclosed in parentheses to avoid common mistakes such as using a=b for a==b, or a|=b for a!=b.

The semantics of such an assignment expression are those of a function that evaluates the right-hand side, casts the right-hand side to the left-hand data type, stacks it, updates the left-hand side, and returns the stacked value. The type returned is the type of the left-hand side data type. If the left-hand side is a concatenation, the type returned shall be an unsigned integral value whose bit length is the sum of the length of its operands.

SystemVerilog includes increment and decrement assignment operators ++i, –i, i++, and i–. These do not need parentheses when used in expressions. These increment and decrement assignment operators behave as blocking assignments.

The ordering of assignment operations relative to any other operation within an expression is undefined. An implementation can warn whenever a variable is both written and read or written within an integral expression or in other contexts where an implementation cannot guarantee the order of evaluation. In the following example:

i = 10;
j = i++ + (i = i - 1);

After execution, the value of j can be 18, 19, or 20 depending upon the relative ordering of the increment and the assignment statements.

Operations on logic and bit types

When a binary operator has one operand of type bit and another of type logic, the result is of type logic. If one operand is of type int and the other of type integer, the result is of type integer.

The operators != and == return an X if either operand contains an X or a Z, as in Verilog-2001. This is converted to a 0 if the result is converted to a type bit.

The unary reduction operators (&, ~&, |, ~|, ^, ~^) can be applied to any integer expression (including packed arrays). The operators shall return a single value of type logic if the packed type is four-valued, and of type bit if the packed type is two-valued.

int i;
bit b = &i;
integer j;
logic c = &j;

Wild equality and wild inequality

SystemVerilog wild-card comparison operators, as described below.

=?=             a =?= b            a equals b,            X, and Z values act as wild cards
!?=             a !?= b             a not equal b,         X, and Z values act as wild cards

The wild equality operator (=?=) and inequality operator (!?=) treat X and Z values in a given bit position as a wildcard. A wildcard bit matches any bit value (0, 1, Z, or X) in the value of the expression being compared against it.

These operators compare operands bit for bit and return a 1-bit self-determined result. If the operands to the wild-card equality/inequality are of unequal bit length, the operands are extended in the same manner as for the case equality/inequality operators. If the relation is true, the operator yields a 1. If the relation is false, it yields a 0.

The three types of equality (and inequality) operators in SystemVerilog behave differently when their operands contain unknown values (X or Z). The == and != operators result in X if any of their operands contains an X or Z. The === and !== check the 4-state explicitly, therefore, X and Z values shall either match or mismatch, never resulting in X. The =?= and !?= operators treat X or Z as wild cards that match any value, thus, they too never result in X.

Real operators

Operands of type shortreal have the same operation restrictions as Verilog real operands. The unary operators ++ and — can have operands of type real and shortreal (the increment or decrement is by 1.0). The assignment operators +=, -=, *=, /= can also have operands of type real and shortreal.

If any operand, except before the ? in the ternary operator, is real, the result is real. Otherwise, if any operand, except before the ? in the ternary operator, is shortreal, the result is shortreal.

Size

The number of bits of an expression is determined by the operands and the context, following the same rules as Verilog. In SystemVerilog, casting can be used to set the size context of an intermediate value.

With Verilog, tools can issue a warning when the left and right-hand sides of an assignment are different sizes. Using the SystemVerilog size casting, these warnings can be prevented.

Sign

The rules for determining the signedness of SystemVerilog expression types shall be the same as those for Verilog. A shortreal converted to an integer by type coercion shall be signed.

<< Previous | Next >>