LANGUAGE GUIDE03 / 10

Data Types

Ground Types

Integer types

Yodl supports two integer types, which require a width specifier:

FormExampleWhen to use
uN / sNu8, u32, s7Width is a literal
uint[expr] / sint[expr]uint[clog2!(N)], sint[W + 1]Width is a compile-time expression

Unsigned Integers

Yodl / unsigned integers
// a 16-bit unsigned integer
let a: u16
a = 16'd1721

Signed Integers

Yodl / signed integers
// a 7-bit signed integer
let b: s7 = -6'd8
let c: s32 = sint!(32'd11)

Integer literals can be specified in decimal, binary, octal, or hexadecimal format. The syntax is <width in bits>'[base prefix]<value>

The decimal value 1621 which requires at least 11 bits can be represented as follows:

prefixbaseexample
b211'b11001010101
o811'o3125
d1011'd1621
h1611'h655

When an integer value is assigned to a same-signed integer type, Yodl resizes it to the destination width. Widening an unsigned value zero-extends it, widening a signed value sign-extends it, and narrowing keeps the least significant bits. Unsized literals instead adopt the contextual width when the value fits. Signedness is never changed implicitly: use uint!(x) or sint!(x) to request a signedness reinterpretation explicitly.

Without a context, an unsized nonnegative literal synthesizes as an unsigned integer with its minimum representable width (1 is u1, 255 is u8). In a uN or sN context it is checked at the literal for overflow and then adopts that exact type. Negative values are rejected by unsigned contexts and must fit the two's-complement range of a signed context. Static Nat parameters and width expressions are evaluated in the separate static/index language; they are not runtime signal types.

Try a width error

An unsized literal must fit the width supplied by its context. Compile this example to see the error, then change u8 to u9 so that the value fits.

Yodl / literal overflow
module WidthError() -> (value: u8) {
    value = 256
}

This example intentionally produces a compiler error. Edit it to explore the diagnostic.

Booleans

The bool type is an alias for the u1 type.

For readability, the true and false keywords are supported and correspond to 1'b1 and 1'b0 respectively.

Yodl / booleans
let is_yodl_neat: bool = true

Clocks

The clock type is required for clock signals used in Reg and Memory module instances.

Yodl / clocks
module Counter(clk: clock) -> (count: u24) {
    let counter = Reg[u24](clk)
    counter.d = counter.q + 1'd1
    count = counter.q
}

Aggregate Types

Ground types can be combined to create more complex data structures.

Vectors

Vectors are ordered and sized collections of elements.

Yodl / vectors
// a vector of eight booleans
let neighbours: [8]bool = [false, false, true, false, true, false, true, false]

// a vector of four 16-bit unsigned integers
let ints: [4]u16 = [16'd1, 16'd12, 16'd3, 16'd4]

Note: Instead of storing multiple register instances in a vector, define a single register instance with a vector type:

Yodl / vectors 2
module RegisterFile(
    clk: clock,
    rst: bool,
    write_enable: bool,
    write_dest: u3,
    write_data: u16,
    read_src1: u3,
    read_src2: u3,
) -> (
    read_data1: u16,
    read_data2: u16,
) {
    // A register storing a vector of eight 16-bit unsigned integers
    let regs = Reg[[8]u16](clk, rst)

    if write_enable {
        regs.d[write_dest] = write_data
    }

    read_data1 = regs.q[read_src1]
    read_data2 = regs.q[read_src2]
}

Characters and Strings

Characters use the ISO 8601 encoding (extended ASCII).

Yodl / characters and strings
let char: u8 = 'a'

Escape sequences

SequenceDescription
\nNewline
\tTab
\\Backslash
\'Single quote

Strings

Strings are fixed-length vectors of characters.

Yodl / strings
let message: [9]u8 = "Yo, Yodl!"

Individual characters can be accessed using the following syntax:

Yodl / strings 2
let first_char = "Yo!"[0]
assert!(first_char == 'Y')

Records

Records group a fixed number of values, accessed either by position or by name. They subsume what other languages call tuples (positional) and structs / bundles (named).

Positional records (tuples)

Fields are accessed by their index (.0, .1, ...). A 1-element positional record requires a trailing comma to distinguish it from a parenthesised expression.

Yodl / positional records tuples
let pair: (bool, u8) = (true, 8'hFF)
let single: (u8,) = (8'hFF,)
let nested: ((a: [1]u4, b: bool), (bool, u8), u1) =
    ((a: [4'd1], b: false), pair, 1'b0)

let first: bool = pair.0 // true
let second: u8 = pair.1 // 8'hFF

Named records (structs / bundles)

Fields are named. This is what other HDLs call a bundle (Chisel/FIRRTL) or struct (SystemVerilog).

Yodl / named records structs bundles
let colour: (r: u8, g: u8, b: u8) = (r: 8'd17, g: 8'd128, b: 8'd211)
let red = colour.r

A named record can be partially updated by spreading another record with ..base followed by overriding fields. Spread is only allowed in named records.

Yodl / named records structs bundles 2
let base = (r: 8'd17, g: 8'd128, b: 8'd211)
let darker = (..base, r: 8'd0)

Mixing positional and named fields in a single record is not allowed: a record is either fully positional or fully named.

Records and module signatures

Because a module's input and output port lists use the same (name: type, ...) syntax as named record types, a module signature is literally a function from a record of inputs to a record of outputs. Module instantiation passes a record of named ports:

Yodl / records and module signatures
let r = Adder(a: 8'd1, b: 8'd2)

Instance Types

The type of a module instance behaves like a named record corresponding to the port signature of the module.

Note: Instance types are not compatible with named record types because only the ports which are accessed on a particular instance are included in the resulting type.

Yodl / instance types
module Adder(a: u8, b: u8) -> (sum: u9) {
    sum = a + b;
}

module Top() -> () {
    let adder = Adder()
    adder.a = 8'd1
    adder.b = 8'd2
    let sum = adder.sum
}

Structural Typing

Yodl's type system is structural (except for instance types), which means that two types are compatible if they have the same shape.

Type AType BCompatible?
u8u8Yes
u1boolYes
(a: u16, b: bool)(b: bool, a: u16)Yes
(a: u16, b: bool)(a: u16)No
(a: u16, b: bool)(a: u16, b: bool, c: [7]bool)No
[8][8]u8[64]u8No
AdderAdderYes
Adder(a: u8, b: u8, sum: u9)No

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.