Assignment Semantics

Veryl uses a single = operator for all assignments. The semantics — blocking or non-blocking — are automatically determined by context, enforcing SystemVerilog best practices.

Blocking Assignment

In always_comb and assign, = performs a blocking assignment — the value is immediately visible to subsequent reads.

module ModuleA {
    let a: logic<8> = 1;
    var b: logic<8>;
    var c: logic<8>;

    always_comb {
        b = a + 1; // b is immediately updated to 2
        c = b + 1; // c reads the new value of b (2), so c = 3
    }
}

Non-Blocking Assignment

In always_ff, = for module-level variables performs a non-blocking assignment — the value does not take effect until the FF commit.

module ModuleA (
    i_clk: input clock,
) {
    var a: logic<8>;
    var b: logic<8>;

    always_ff {
        // a.next = 1 (current a is unchanged during this phase)
        a = 1;
        // reads a.current (not a.next), so b.next = a.current + 1
        b = a + 1;
    }
}

Local Bindings in always_ff

A let inside always_ff uses blocking semantics and is never mapped to a flip-flop.

module ModuleA (
    i_clk: input clock,
) {
    var a     : logic<8>;
    var result: logic<8>;

    always_ff {
        // blocking: doubled is immediately available
        let doubled: logic<8> = a * 2;
        // non-blocking: result.next = doubled + 1
        result = doubled + 1;
    }
}

Mapping to SystemVerilog

Veryl contextVeryl syntaxSystemVerilog equivalent
always_combx = expr;x = expr; (blocking)
assignassign x = expr;assign x = expr; (continuous)
always_ff, module-level varx = expr;x <= expr; (non-blocking)
always_ff, local letlet x: T = expr;x = expr; (blocking)
always_ff, compoundx += expr;x <= x + expr; (non-blocking)