Primitive Modules

Yodl provides a few essential modules for building sequential digital circuits.

Registers

Registers are fundamental storage elements in digital circuits. They store data between clock cycles.

Basic Register

module Counter(clk: clock) -> (value: uint<8>) {
    let counter = Reg<uint<8>>(clk)
    counter.d = counter.q + 1'b1
    value = counter.q
}

Parameters

ParameterTypeDescription
TtypeData type of the register

Ports

PortDirectionTypeDescription
clkInputclockClock input
dInputTData input (next state)
enInputboolEnable signal (optional)
rstInputboolReset signal (optional)
qOutputTData output (current state)

Register with Reset

module Counter(clk: clock, rst: bool) -> (value: uint<8>) {
    let counter = Reg<uint<8>>(clk, rst)
    counter.d = counter.q + 1'b1
    value = counter.q
}

When rst is asserted, the register's value is synchronously reset to 0.

Register with Enable

module Counter(clk: clock, enable: bool) -> (value: uint<8>) {
    let counter = Reg<uint<8>>(clk, en: enable)
    counter.d = counter.q + 1'b1
    value = counter.q
}

The register only updates its value when enable is asserted.

The same behaviour can be obtained using the d port only:

module Counter(clk: clock, enable: bool) -> (value: uint<8>) {
    let counter = Reg<uint<8>>(clk)
    counter.d = enable ? counter.q + 1'b1 : counter.q
    value = counter.q
}

Register with Asynchronous Reset

 module Test(clk: clock, rst: bool) -> () {
    let state = RegAsyncReset<uint<2>>(clk, rst)
 }

With RegAsyncReset, the reset signal is asynchronous and takes effect immediately.

Memory

Memories are arrays of registers that can be read from and written to.

Some configurations may be synthesised as block RAMs in FPGAs.

Parameters

ParameterTypeDescription
TtypeData type of each element
DepthuintNumber of entries
ReadPortsuintNumber of read ports
WritePortsuintNumber of write ports
ReadLatencyuintCycles to read
WriteLatencyuintCycles to write

Ports

PortDirectionTypeDescription
readInput{clk: clock, en: bool, addr: uint<$clog2(Depth)>}[ReadPorts]Read port(s)
writeInput{clk: clock, en: bool, addr: uint<$clog2(Depth)>, data: T, mask: MemoryMask<T>}[WritePorts]Write port(s)
qOutputT[ReadPorts]Read data port(s)

Basic Memory

module RAM(
    clk: clock,
    addr: uint<10>,
    write_data: uint<8>,
    write_enable: bool,
) -> (
    read_data: uint<8>,
) {
    let mem = Memory<
        T: uint<8>,           // Data type
        Depth: 1024,          // Number of entries
        ReadPorts: 1,         // Number of read ports
        WritePorts: 1,        // Number of write ports
        ReadLatency: 1,       // Cycles to read
        WriteLatency: 1,      // Cycles to write
    >(
        read: [{ clk: clk, en: true, addr: addr }],
        write: [{ clk: clk, en: write_enable, addr: addr, data: write_data, mask: true }],
    )
    
    read_data = mem.q[0]
}

Memory with Write Masking

Write masking allows selective updates to parts of a memory word:

module ByteAddressableRAM(
    clk: clock,
    addr: uint<10>,
    write_data: uint<8>[4],   // 32-bit word as 4 bytes
    byte_mask: bool[4],       // Which bytes to write
    write_enable: bool,
) -> (
    read_data: uint<32>,
) {
    let mem = Memory<
        T: uint<8>[4],
        Depth: 1024,
        ReadPorts: 1,
        WritePorts: 1,
        ReadLatency: 1,
        WriteLatency: 1,
    >(
        read: [{ clk: clk, en: true, addr: addr }],
        write: [{ clk: clk, en: write_enable, addr: addr, data: write_data, mask: byte_mask }],
    )

    read_data = uint(mem.q[0])
}

Mask Type

Intuitively, the mask type MemoryMask<T> of a data type T matches the structure of T with each ground type (e.g. uint<N>, sint<N>, ..) replaced with bool.

Examples:

Data TypeMask Type
boolbool
uint<8>bool
uint<32>[4]bool[4]
{a: uint<8>, b: uint<8>}{a: bool, b: bool}
{a: {b: uint<16>[64], c: bool}}{a: {b: bool[64], c: bool}}
Portions of an integer cannot directly be masked in Yodl, just like in Chisel. Instead, the integer can be split into smaller parts which can be masked individually.

To learn more about write masks, check out the FIRRTL specification.