Statements
Statements are the declarations that appear inside a module body. Unlike expressions, which compute values, statements declare the structure and behavior of a module.
The full set of module body statements includes:
Component declarations (ports, wires, registers)
Driver statements (assignments)
Instance declarations
Socket instance declarations
Conditional statements (when)
Match statements
Unused declarations
Statements are order-independent within a module body. Virdant processes them as a set of declarations, not as a sequence of imperative steps.
Component Declarations
Component declarations introduce named signals into a module.
ModDefStmtComponent :=
DocString Annotations "incoming" Ident ":" Type OnClause? ItBlock?
| DocString Annotations "outgoing" Ident ":" Type OnClause? ItBlock?
| DocString Annotations "outgoing" "wire" Ident ":" Type OnClause? ItBlock?
| DocString Annotations "outgoing" "reg" Ident ":" Type OnClause? ItBlock?
| DocString Annotations "wire" Ident ":" Type OnClause? ItBlock?
| DocString Annotations "reg" Ident ":" Type OnClause? ItBlock?
mod Example {
// Port declarations
incoming clock : Clock
incoming inp : Word[8]
outgoing out : Word[8]
// Internal wire
wire sum : Word[8]
// Register
reg counter : Word[8] on clock
// Outgoing register port
outgoing reg status : Word[8] on clock
}
For a detailed description of each component type, see Modules.
Driver Statements
Driver statements assign values to components.
ModDefStmtDriver :=
Path ":=" Expr
| Path "<=" Expr
| Path ":=:" Path
Combinational assignment :=
The := operator drives a combinational signal continuously.
out := a + b
sum := ha1.sum
The target of := must be a combinational signal (wire, port, or combinational output of a submodule).
Sequential assignment <=
The <= operator drives a register. The value is latched on the next rising edge of the register’s clock.
reg counter : Word[8] on clock
counter <= counter + 1
The target of <= must be a reg declaration.
Bidirectional connection :=:
The :=: operator connects two signals bidirectionally.
memory.mem :=: core.mem
Both sides of :=: must be socket instances of compatible socket types. The operator connects every port in the socket, respecting the direction of each port relative to each instance.
Instance Declarations
Instance declarations instantiate a submodule inside the current module.
ModDefStmtInstance :=
DocString Annotations "mod" Ident "of" Ofness ItBlock?
The instance name must be unique within the enclosing module. Ports on the instance are accessed via dot notation.
mod ha1 of HalfAdder
ha1.a := a
ha1.b := b
An instance may have an optional it block for grouping connections:
mod ha1 of HalfAdder {
it.a := a
it.b := b
}
The type (after of) must be a module definition visible in the current scope, either defined in the same package or imported from another package.
Socket Instance Declarations
Socket instances declare either a client or server role for a socket type.
ModDefStmtSocket :=
DocString Annotations "client" "socket" Ident "of" Ofness ItBlock?
| DocString Annotations "server" "socket" Ident "of" Ofness ItBlock?
client socket mem of Mem
unused mem.data
mem.addr := 0
For details on sockets, see Sockets.
Conditional Statements
Conditional statements allow a module body to contain different sets of statements depending on conditions.
ModDefStmtWhen := "when" "{" ModDefStmtWhenArm* "}"
ModDefStmtWhenArm :=
"case" Expr "=>" Expr
| "case" Expr ModDefStmtBlock
| "case" Expr ModDefStmtWhen
| "case" Expr ModDefStmtMatch
| "else" "=>" Expr
| "else" ModDefStmtBlock
| "else" ModDefStmtWhen
| "else" ModDefStmtMatch
when {
case reset {
r <= 0
state := #Idle
}
case enable {
r <= r + 1
state := #Active
}
else {
r <= r
state := #Idle
}
}
Each condition must be of type Bit. The else arm is optional in statement context, though it is recommended for completeness.
Arm Syntax
Each arm may use one of four forms:
Bare expression: case cond => expr (rarely used in statement context)
Block: case cond { stmts }
Nested when: case cond when { ... }
Nested match: case cond match expr { ... }
The same forms apply to the else arm.
Conditional statements are different from when expressions: statements can contain declarations, assignments, instances, and other module body constructs, while when expressions only compute values.
Match Statements
Match statements perform pattern-based dispatch in the module body.
ModDefStmtMatch :=
"match" Expr "{" ModDefStmtMatchArm* "}"
ModDefStmtMatchArm :=
"case" Pat "=>" Expr
| "case" Pat ModDefStmtBlock
| "case" Pat ModDefStmtWhen
| "case" Pat ModDefStmtMatch
| "else" "=>" Expr
| "else" ModDefStmtBlock
| "else" ModDefStmtWhen
| "else" ModDefStmtMatch
match state {
case #Idle => {
r <= 0
out := ready
}
case #Active => {
r <= r + 1
out := busy
}
else => {
r <= r
out := idle
}
}
Arm Syntax
Each arm may use one of four forms:
Bare expression: case pat => expr (rarely used in statement context)
Block: case pat { stmts }
Nested when: case pat when { ... }
Nested match: case pat match expr { ... }
The same forms apply to the else arm.
The else arm is optional but recommended for catch-all behavior. If no else arm is present and the match is not exhaustive, the compiler will report an error.
For more details on patterns, see Patterns.
The Unused Statement
The unused statement declares that a signal is intentionally not used. This suppresses compiler warnings about unused components or ports.
ModDefStmtUnused :=
"unused" Path
mod Core {
client socket mem of Mem
unused mem.data // data output is intentionally unused
mem.addr := 0
}
The path must refer to an existing signal, port, or instance field.
on Clause
The on clause associates a register with a clock signal.
OnClause :=
"on" Expr
reg r : Word[8] on clock
The expression after on must evaluate to a value of type Clock.
Every reg declaration must have an on clause.
it Block
An it block groups statements under a declaration. Inside the block, the keyword it refers to the enclosing declaration.
ItBlock :=
ModDefStmtBlock
ModDefStmtBlock :=
"{" ModDefStmt* "}"
reg r : Word[8] on clock {
it <= inp
out := it
}
The it block is syntactic sugar that avoids repeating the declaration name. It is purely a matter of style — the above is equivalent to:
reg r : Word[8] on clock
r <= inp
out := r