The ap_[u]int
types do not support implicit conversion
from wide ap_[u]int
(>64bits) to builtin C/C++
integer types. For example, the following code example return s1, because
the implicit cast from ap_int[65]
to bool
in the if-statement returns a 0.
bool nonzero(ap_uint<65> data) {
return data; // This leads to implicit truncation to 64b int
}
int main() {
if (nonzero((ap_uint<65>)1 << 64)) {
return 0;
}
printf(FAIL\n);
return 1;
}
To convert wide ap_[u]int
types to built-in
integers, use the explicit conversion functions included with the ap_[u]int
types:
-
to_int()
-
to_long()
-
to_bool()
In general, any valid operation that can be done on a native C/C++ integer data
type is supported using operator overloading for ap_[u]int
types.
In addition to these overloaded operators, some class specific operators and methods are included to ease bit-level operations.
Binary Arithmetic Operators
Standard binary integer arithmetic operators are overloaded to provide arbitrary precision arithmetic. These operators take either:
- Two operands of
ap_[u]int
, or - One
ap_[u]int
type and one C/C++ fundamental integer data type
For example:
- char
- short
- int
The width and signedness of the resulting value is determined by the width and signedness of the operands, before sign-extension, zero-padding or truncation are applied based on the width of the destination variable (or expression). Details of the return value are described for each operator.
When expressions contain a mix of ap_[u]int
and C/C++ fundamental integer types, the C++ types assume the
following widths:
-
char
(8-bits) -
short
(16-bits) -
int
(32-bits) -
long
(32-bits) -
long long
(64-bits)
Addition
ap_(u)int::RType ap_(u)int::operator + (ap_(u)int op)
Returns the sum of:
- Two
ap_[u]int
, or - One
ap_[u]int
and a C/C++ integer type
The width of the sum value is:
- One bit more than the wider of the two operands, or
- Two bits if and only if the wider is unsigned and the narrower is signed
The sum is treated as signed if either (or both) of the operands is of a signed type.
Subtraction
ap_(u)int::RType ap_(u)int::operator - (ap_(u)int op)
Returns the difference of two integers.
The width of the difference value is:
- One bit more than the wider of the two operands, or
- Two bits if and only if the wider is unsigned and the narrower signed
This is true before assignment, at which point it is sign-extended, zero-padded, or truncated based on the width of the destination variable.
The difference is treated as signed regardless of the signedness of the operands.
Multiplication
ap_(u)int::RType ap_(u)int::operator * (ap_(u)int op)
Returns the product of two integer values.
The width of the product is the sum of the widths of the operands.
The product is treated as a signed type if either of the operands is of a signed type.
Division
ap_(u)int::RType ap_(u)int::operator / (ap_(u)int op)
Returns the quotient of two integer values.
The width of the quotient is the width of the dividend if the divisor is an unsigned type. Otherwise, it is the width of the dividend plus one.
The quotient is treated as a signed type if either of the operands is of a signed type.
Modulus
ap_(u)int::RType ap_(u)int::operator % (ap_(u)int op)
Returns the modulus, or remainder of integer division, for two integer values.
The width of the modulus is the minimum of the widths of the operands, if they are both of the same signedness.
If the divisor is an unsigned type and the dividend is signed, then the width is that of the divisor plus one.
The quotient is treated as having the same signedness as the dividend.
Following are examples of arithmetic operators:
ap_uint<71> Rslt;
ap_uint<42> Val1 = 5;
ap_int<23> Val2 = -8;
Rslt = Val1 + Val2; // Yields: -3 (43 bits) sign-extended to 71 bits
Rslt = Val1 - Val2; // Yields: +3 sign extended to 71 bits
Rslt = Val1 * Val2; // Yields: -40 (65 bits) sign extended to 71 bits
Rslt = 50 / Val2; // Yields: -6 (33 bits) sign extended to 71 bits
Rslt = 50 % Val2; // Yields: +2 (23 bits) sign extended to 71 bits
Bitwise Logical Operators
The bitwise logical operators all return a value with a width that is the maximum of the widths of the two operands. It is treated as unsigned if and only if both operands are unsigned. Otherwise, it is of a signed type.
Sign-extension (or zero-padding) may occur, based on the signedness of the expression, not the destination variable.
Bitwise OR
ap_(u)int::RType ap_(u)int::operator | (ap_(u)int op)
Returns the bitwise OR
of the two operands.
Bitwise AND
ap_(u)int::RType ap_(u)int::operator & (ap_(u)int op)
Returns the bitwise AND
of the two operands.
Bitwise XOR
ap_(u)int::RType ap_(u)int::operator ^ (ap_(u)int op)
Returns the bitwise XOR
of the two operands.
Unary Operators
Addition
ap_(u)int ap_(u)int::operator + ()
Returns the self copy of the ap_[u]int
operand.
Subtraction
ap_(u)int::RType ap_(u)int::operator - ()
Returns the following:
- The negated value of the operand with the same width if it is a signed type, or
- Its width plus one if it is unsigned.
The return value is always a signed type.
Bitwise Inverse
ap_(u)int::RType ap_(u)int::operator ~ ()
Returns the bitwise-NOT
of the operand with the same width and
signedness.
Logical Invert
bool ap_(u)int::operator ! ()
Returns a Boolean false
value if and only if the operand is
not equal to zero (0
).
Returns a Boolean true
value if the operand is equal to zero
(0
).
Ternary Operators
When you use the ternary operator with the standard C int
type, you
must explicitly cast from one type to the other to ensure that both results have the same
type. For example:
// Integer type is cast to ap_int type
ap_int<32> testc3(int a, ap_int<32> b, ap_int<32> c, bool d) {
return d?ap_int<32>(a):b;
}
// ap_int type is cast to an integer type
ap_int<32> testc4(int a, ap_int<32> b, ap_int<32> c, bool d) {
return d?a+1:(int)b;
}
// Integer type is cast to ap_int type
ap_int<32> testc5(int a, ap_int<32> b, ap_int<32> c, bool d) {
return d?ap_int<33>(a):b+1;
}
Shift Operators
Each shift operator comes in two versions:
- One version for unsigned right-hand side (RHS) operands
- One version for signed right-hand side (RHS) operands
A negative value supplied to the signed RHS versions reverses the shift operations direction. That is, a shift by the absolute value of the RHS operand in the opposite direction occurs.
The shift operators return a value with the same width as the left-hand side (LHS) operand. As with C/C++, if the LHS operand of a shift-right is a signed type, the sign bit is copied into the most significant bit positions, maintaining the sign of the LHS operand.
Unsigned Integer Shift Right
ap_(u)int ap_(u)int::operator >> (ap_uint<int_W2> op)
Integer Shift Right
ap_(u)int ap_(u)int::operator >> (ap_int<int_W2> op)
Unsigned Integer Shift Left
ap_(u)int ap_(u)int::operator << (ap_uint<int_W2> op)
Integer Shift Left
ap_(u)int ap_(u)int::operator << (ap_int<int_W2> op)
Following are examples of shift operations:
ap_uint<13> Rslt;
ap_uint<7> Val1 = 0x41;
Rslt = Val1 << 6; // Yields: 0x0040, i.e. msb of Val1 is lost
Rslt = ap_uint<13>(Val1) << 6; // Yields: 0x1040, no info lost
ap_int<7> Val2 = -63;
Rslt = Val2 >> 4; //Yields: 0x1ffc, sign is maintained and extended
Compound Assignment Operators
Vitis HLS supports compound assignment operators:
- *=
- /=
- %=
- +=
- -=
- <<=
- >>=
- &=
- ^=
- |=
The RHS
expression is first evaluated then supplied as the
RHS
operand to the base operator, the result of which is assigned
back to the LHS
variable. The expression sizing, signedness, and
potential sign-extension or truncation rules apply as discussed above for the relevant
operations.
ap_uint<10> Val1 = 630;
ap_int<3> Val2 = -3;
ap_uint<5> Val3 = 27;
Val1 += Val2 - Val3; // Yields: 600 and is equivalent to:
// Val1 = ap_uint<10>(ap_int<11>(Val1) +
// ap_int<11>((ap_int<6>(Val2) -
// ap_int<6>(Val3))));
Increment and Decrement Operators
The increment and decrement operators are provided. All return a value of the same width as the operand and which is unsigned if and only if both operands are of unsigned types and signed otherwise.
Pre-Increment
ap_(u)int& ap_(u)int::operator ++ ()
Returns the incremented value of the operand.
Assigns the incremented value to the operand.
Post-Increment
const ap_(u)int ap_(u)int::operator ++ (int)
Returns the value of the operand before assignment of the incremented value to the operand variable.
Pre-Decrement
ap_(u)int& ap_(u)int::operator -- ()
Returns the decremented value of, as well as assigning the decremented value to, the operand.
Post-Decrement
const ap_(u)int ap_(u)int::operator -- (int)
Returns the value of the operand before assignment of the decremented value to the operand variable.
Relational Operators
Vitis HLS supports all relational operators. They return a Boolean value based on the
result of the comparison. You can compare variables of ap_[u]int
types to C/C++ fundamental integer types with these operators.
Equality
bool ap_(u)int::operator == (ap_(u)int op)
Inequality
bool ap_(u)int::operator != (ap_(u)int op)
Less than
bool ap_(u)int::operator < (ap_(u)int op)
Greater than
bool ap_(u)int::operator > (ap_(u)int op)
Less than or equal to
bool ap_(u)int::operator <= (ap_(u)int op)
Greater than or equal to
bool ap_(u)int::operator >= (ap_(u)int op)