Operators
This documentation is complete and ready to use.
Integer types (Bits<N>, U64) support most of the same operators as Verilog and use the same order of precedence. Notably excluded are many bitwise reduction operators (e.g., and-reduce, or-reduce).
Binary operators between operands of different bit widths extend the smaller operand to the size of the larger operand prior to the operation. When the smaller operand is signed, the extension is a sign extension; otherwise it is a zero extension.
The result of a binary operation is signed if both operands are signed; otherwise the result is unsigned.
Widening Operators
IDL includes widening variants of several binary operators, indicated by a backtick (`) prefix. These are unique to IDL and have no direct C or Verilog equivalent. They are useful when you need to preserve the full result of an operation that would otherwise overflow or lose bits.
In the table below, L(i) denotes the bit width of operand i, and value(j) denotes the compile-time constant value of j.
| Operator | Operation | Result width |
|---|---|---|
i `* j | Widening multiply | L(i) + L(j) |
i `+ j | Widening addition | max(L(i), L(j)) + 1 |
i `- j | Widening subtraction | max(L(i), L(j)) + 1 |
i `<< j | Widening left shift (j must be compile-time constant) | L(i) + value(j) |
Bits<32> a = 0xFFFFFFFF;
Bits<32> b = 0x00000002;
# Standard multiply: upper bits discarded, result stays 32 bits
Bits<32> narrow_mul = a * b; # 0xFFFFFFFE (truncated)
# Widening multiply: full 64-bit result
Bits<64> wide_mul = a `* b; # 0x00000001FFFFFFFE
# Standard addition: carry bit lost
Bits<32> wrapped_add = a + b; # 0x00000001 (truncation)
# Widening addition: carry preserved in extra bit, result is 33 bits
Bits<33> wide_add = a `+ b; # 0x100000001
# Widening left shift by compile-time constant 4: result is 36 bits
Bits<36> wide_shl = a `<< 4; # 0xFFFFFFFF0
`<< requires the shift amount to be known at compile time. Use << for variable shift amounts (discarding the upper bits).
Operator Precedence Table
For an operand i (which may be an expression), L(i) is the number of bits in i and typeof(i) is the exact type of i. Precedence 0 is highest.
| Precedence | Operator | Result Type | Comments |
|---|---|---|---|
| 0 | i[idx] | Bits<1> | Extract a single bit from bit position idx.i must be an integral type or an array.Result is unsigned, regardless of the sign of i. |
i[msb:lsb] | Bits<msb - lsb + 1> | Extract a range of bits between msb and lsb, inclusive.i must be an integral type.Result is unsigned, regardless of the sign of i. | |
| 1 | (i) | typeof(i) | Grouping. |
| 2 | !i | Boolean | Logical negation. i must be a Boolean type. |
~i | typeof(i) | Bitwise negation. i must be an integral type. | |
| 3 | -i | typeof(i) | Unary minus in two's complement, i.e., 2N - i.i must be an integral type. |
| 4 | {i, j, ...} | Bits<L(i) + L(j) + ...> | Concatenation. All operands must be Bits<N> type. Result is always unsigned. |
| 5 | {N{i}} | Bits<N * L(i)> | Replicates i N times. i must be a Bits<N> type.N must be a literal or compile-time constant. |
| 6 | i * j | Bits<max(L(i), L(j))> | Multiply i times j. Result is the same width as the widest operand. Upper half of the result is discarded. |
i `* j | Bits<L(i) + L(j)> | Widening multiply i times j. | |
i / j | Bits<max(L(i), L(j))> | Divide i by j. Result is the same width as the widest operand. Remainder is discarded.Division by zero is undefined and must be avoided. When both are signed, signed overflow is undefined and must be avoided. | |
i % j | Bits<max(L(i), L(j))> | Remainder of the division of i by j. Quotient is discarded.Division by zero is undefined and must be avoided. When both are signed, signed overflow is undefined and must be avoided. | |
| 7 | i + j | Bits<max(L(i), L(j))> | Addition. Carry bit is discarded. To preserve carry, widen operands before adding. |
i `+ j | Bits<max(L(i), L(j)) + 1> | Widening addition. | |
i - j | Bits<max(L(i), L(j))> | Subtraction. Carry bit is discarded. To preserve carry, widen operands before subtracting. | |
i `- j | Bits<max(L(i), L(j)) + 1> | Widening subtraction. | |
| 8 | i << j | typeof(i) | Left logical shift. |
i `<< j | Bits<L(i) + value(j)> | Widening left logical shift. j must be known at compile time — otherwise a type error. | |
i >> j | typeof(i) | Right logical shift. | |
i >>> j | typeof(i) | Right arithmetic shift. | |
| 9 | i > j | Boolean | Greater than. i and j must be integral. |
i < j | Boolean | Less than. i and j must be integral. | |
i >= j | Boolean | Greater than or equal. i and j must be integral. | |
i <= j | Boolean | Less than or equal. i and j must be integral. | |
| 10 | i == j | Boolean | Equality. i and j must be the same type: integral, boolean, or string. |
i != j | Boolean | Inequality. i and j must be the same type: integral, boolean, or string. | |
| 11 | i & j | Bits<max(L(i), L(j))> | Bitwise AND. i and j must be integral. |
| 12 | i ^ j | Bits<max(L(i), L(j))> | Bitwise exclusive OR. i and j must be integral. |
| 13 | i | j | Bits<max(L(i), L(j))> | Bitwise OR. i and j must be integral. |
| 14 | i && j | Boolean | Logical AND. i and j must be boolean. |
i || j | Boolean | Logical OR. i and j must be boolean. | |
| 15 | c ? t : f | typeof(t) | Ternary operator. Result is t if c is true, f otherwise.c must be boolean; t and f must be compatible types.When t and f are Bits types of different widths, the result is the larger of the two widths. |
The / and % operators produce undefined behavior when the divisor is zero. When both operands are signed, overflow (e.g., MIN_INT / -1) is also undefined. The IDL compiler does not insert runtime guards — it is the programmer's responsibility to ensure these cases cannot occur.
# Safe pattern: guard before dividing
if (b != 0) {
result = a / b;
}