Statements

The body of any unit, or block is a list of statements followed by a resulting expression. Statements can declare things local to the block and contain expressions to be evaluated

Let bindings

Let bindings bind a pattern to a value.

Those not used to bindings and patterns can view a let binding as assigning a value to a variable.

The pattern has to be an irrefutable pattern

If the type specification is omitted, the type is inferred.

Syntax

let pattern [: type specification] = expression ;

Examples

Binding a value to a variable

let a = some_value;

Binding a value to the result of an if expression

let a = if x {0} else {2};

Unpacking a tuple

let (a, b) = some_value;

Unpacking a struct with an explicit type signature

let Struct$(a, b): Struct<int<8>> = some_value;

Registers

Free-standing (i.e. non-pipelining registers) are defined using reg(clk) ... The register definition is quite complex and includes

  • The clock signal which triggers an update
  • A pattern to bind the current value of the register to. It must be irrefutable
  • An optional type specification. Like let bindings, the type is inferred if the type signature is omitted
  • An optional reset consisting of a reset trigger and a reset value. Whenever the reset trigger is true the value of the register is asynchronously set to the reset value1
  • An expression which gives new value

On the rising edge of the clock signal, the value of the register is updated to the value of the new value. The new value expression can include variables from the register itself.

Syntax

reg( clock: expression ) pattern [: type specification] [reset( reset trigger: expression : reset value expression)] = new value: expression ;

Examples

A register which counts from -128 to 127 (Note that because no initial value is specified, this will be undefined in simulation):

reg(clk) value: int<8> = trunc(value + 1);

A register which counts from 0 to 200 (inclusive) and is reset to 0 by rst:

reg(clk) value reset(rst: 0) =
    if value == 200 {
        0
    } else {
        trunc(value + 1)
    };

Pipeline stage markers

Stage markers (reg;) are used in pipelines to specify where pipeline registers should be inserted. After a reg statement, all variables above the statement will be put in registers and any reference to those variables refer to the registered version.

Syntax

Repeated

In cases where more than one stage should be inserted without any new statements in between, there is a shorthand syntax:

reg * n`

where n is an integer. This is compiled into n simple reg statements, i.e.

reg * 3;

is the same as

reg;
reg;
reg;

Conditioned

A condition for the registers to accept values can also be specified in square brackets

reg[condition]

The semantics of this are explained in the section on dynamic pipelines

Pipeline stage labels

Pipeline stages can be given names to refer to them from other stages. This is done using 'name.

  'first
  let x = ...;
reg;

To refer to a named stage, use a []

Set

Set the value of a mutable wire to the specified value.

set wire = value;

Set statements can only appear at the top block of a unit. This might be surprising as you would expect to be able to write


#![allow(unused_variables)]
fn main() {
if condition {
  set wire = value;
}
}

However, this is not well-defined in hardware because the wire needs some value, but no value is specified if condition does not hold. This particular point isn't true if an else branch is also specified, but the exact hardware that gets generated from imperative code like this is not obvious, particularly with more nesting.

Therefore, if you want to write

if condition {
  set wire = on_true;
} else {
  set wire = on_false
}

you should move the set statement outside to make it unconditional, i.e.

set wire =
  if condition {
    on_true
  } else {
    on_false
  }

Syntax

set expression = expression;

Assert

Takes a boolean condition and evaluates it, raising a runtime error in simulation if it ever evaluates to false. In synthesis, this is ignored

assert this_should_be_0 == 0;

NOTE: Assert statements are currently not supported for synthesis with Verilator, only with Icarus.

Comptime

TODO

Real world example