LANGUAGE GUIDE05 / 10

Operators

Unary Operators

OperatorDescriptionExample
-Arithmetic negation-x
notBitwise NOTnot x
andrAND reductionandr x
orrOR reductionorr x
xorrXOR reductionxorr x

Reduction Operators

Reduction operators apply the corresponding logic operation across all bits of the operand, returning a single bit result.

Yodl / reduction operators
let all_ones = andr 8'b11111111
assert!(all_ones == 1)

let any_one = orr 8'b00000001
assert!(any_one == 1)

let parity = xorr 8'b10101010
assert!(parity == 0)

Binary Operators

Arithmetic Operators

OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
modModuloa mod b

Bitwise Operators

OperatorDescriptionExample
andBitwise ANDa and b
orBitwise ORa or b
xorBitwise XORa xor b
nandBitwise NANDa nand b
norBitwise NORa nor b
xnorBitwise XNORa xnor b
shlShift Lefta shl b
shrShift Righta shr b

Note 1: When the shift amount (the right hand side) in a shr operation is signed (sint type), the operation corresponds to an arithmetic shift right.

Note 2: The and, or, xor, nand, nor, and xnor operators are used both to perform bitwise and logical operations.

Comparison Operators

OperatorDescriptionExample
==Equala == b
!=Not Equala != b
<Less Thana < b
>Greater Thana > b
<=Less Than or Equala <= b
>=Greater Than or Equala >= b

Ternary Operator

The ternary operator is a concise way to express conditional expressions, generally spanning a single line.

Yodl / ternary operator
let max = a >= b ? a : b

Concatenation

Integer concatenation is performed with the cat! built-in function:

Yodl / concatenation
let upper_nibble = 8'hAB
let lower_nibble = 8'hCD
let word = cat!(upper_nibble, lower_nibble)
assert!(word == 16'hABCD)

cat! also accepts Vectors of integers, which it flattens automatically.

Yodl / concatenation 2
let value = cat!([1'1, 1'0, 1'0, 1'0]) // 4'b1000
assert!(value == 4'b1000)

Slicing and Indexing

Elements of vectors and bits of integers can be accessed using the [] operator:

Yodl / slicing and indexing
let bits = [..8'd233]
let first = bits[0]      // Access the first element
let nibble = bits[7:4]   // Extract a range of bits (inclusive)
let byte_desc = bits[7-:8] // 8 bits ending at bit 7 (equivalent to bits[7:0])
let byte_asc = bits[0+:8]  // 8 bits starting at bit 0 (equivalent to bits[7:0])

assert!(first == 1'b1)
assert!(uint!(nibble) == 4'hE)
assert!(uint!(byte_desc) == 8'hE9)
assert!(uint!(byte_asc) == 8'hE9)

There are three forms of bit slicing:

  1. [high:low] - Extract bits from position high down to low (inclusive)
  2. [base-:width] - Extract width bits ending at base (selects [base : base - width + 1])
  3. [base+:width] - Extract width bits starting at base (selects [base + width - 1 : base])

The +: and -: forms match SystemVerilog's indexed part-select syntax

Note:

  • Integers are indexed from the least significant bit (LSB) to the most significant bit (MSB) (right to left).
  • Vectors follow standard array-indexing conventions, with the first element at index 0 (left to right).

When a bit vector ([N]bool i.e. [N]u1) is used as the argument of the uint and sint built-in functions, the first element of the vector becomes the MSB of the resulting integer:

Yodl / slicing and indexing 2
let n = uint!([1'b1, 1'b0, 1'b0])
assert!(n == 3'b0001)

Replication

The fill!(n, x) built-in produces a vector [n]T of n copies of x:

Yodl / replication
let zeros = fill!(4, 1'b0)        // [1'b0, 1'b0, 1'b0, 1'b0]
let ones  = cat!(fill!(3, 1'b1))  // Concat-replicate: 3'b111

assert!(uint!(zeros) == 4'd0)
assert!(ones == 3'b111)

The repeated expressions can contain any value, including instances:

Yodl / replication 2
// initialise a Rows by Cols grid of cells
let cells = fill!(Cols, fill!(Rows, Cell(clk, rst)))

Concatenation

The cat! built-in concatenates a list of integers (or integer vectors) into a single integer. The width of the result is the sum of the widths of the operands.

If any operand is a signed integer (sint), then all operands are required to be signed.

Yodl / concatenation 3
let concat_args = cat!(16'hBABA, 16'hFABE)
let concat_vec  = cat!([16'hBABA, 16'hFABE])

assert!(concat_args == 32'hBABAFABE)
assert!(concat_vec == 32'hBABAFABE)

Spread

The spread operator .. can only appear inside a vector expression and is used to decompose a value into its individual elements.

Yodl / spread
let bits: [4]u1 = [..4'b1100] // [1'b0, 1'b0, 1'b1, 1'b1]
let chars: [3]u8 = [.."Yo!"] // [8'h59, 8'h6F, 8'h21]
let flat: [3]u2 = [..[2'd1, 2'd2], 2'd3] // [2'd1, 2'd2, 2'd3]

assert!(bits[0] == 1'b0)
assert!(bits[1] == 1'b0)
assert!(bits[2] == 1'b1)
assert!(bits[3] == 1'b1)
assert!(chars[0] == 8'h59)
assert!(chars[1] == 8'h6F)
assert!(chars[2] == 8'h21)
assert!(flat[0] == 2'd1)
assert!(flat[1] == 2'd2)
assert!(flat[2] == 2'd3)

Operator Precedence

Operators are evaluated in the following order (from highest to lowest precedence):

  1. Unary operators (not, -, andr, orr, xorr)
  2. Multiplication, division, modulo (*, /, mod)
  3. Addition, subtraction (+, -)
  4. Shift operations (shl, shr)
  5. Comparisons (<, >, <=, >=)
  6. Equality operators (==, !=)
  7. Bitwise AND and NAND (and, nand)
  8. Bitwise XOR and XNOR (xor, xnor)
  9. Bitwise OR and NOR (or, nor)

Parentheses can be used to override the default precedence order.

Resulting Type

The output type of a binary operation is determined by the types of the operands and the operation being performed.

It matches the FIRRTL specification

The following table summarises the resulting type for each operation:

operationlhs typerhs typeoutput type
+, -uint<A>uint<B>uint<max(A, B) + 1>
+, -sint<A>sint<B>sint<max(A, B) + 1>
*uint<A>uint<B>uint<A + B>
*sint<A>sint<B>sint<A + B>
/uint<A>uint<B>uint<A>
/sint<A>sint<B>sint<A + 1>
moduint<A>uint<B>uint<min(A, B)>
modsint<A>sint<B>sint<min(A, B)>
==, !=, <, >uint<A>uint<B>u1
==, !=, <, >sint<A>sint<B>u1
and, nand, or, nor, xor, xnoruint<A>uint<B>uint<max(A, B)>
and, nand, or, nor, xor, xnorsint<A>sint<B>uint<max(A, B)>

Shift Operations

When the shift amount is known at compile time, the output type is determined as follows:

operationlhs typeshift amountoutput type
shluint<A>nuint<A + n>
shlsint<A>nsint<A + n>
shruint<A>nuint<max(A - n, 0)>
shrsint<A>nsint<max(A - n, 1)>

When the shift amount is not known at compile time, the output type is determined as follows:

operationlhs typerhs typeoutput type
shluint<A>uint<B>uint<A + 2^B - 1>
shlsint<A>uint<B>sint<A + 2^B - 1>
shruint<A>uint<B>uint<A>
shrsint<A>uint<B>sint<A>

Type to search all chapters.

Reset this example?

Your local edits will be replaced by the original source.

Share this example

The link includes your edits and selected compiler stage. It uses the deployed compiler version.