The Veryl Hardware Description Language

Veryl

Veryl is a hardware description language based on SystemVerilog, providing the following advantages:

Optimized Syntax

Veryl adopts syntax optimized for logic design while being based on a familiar basic syntax for SystemVerilog experts. This optimization includes guarantees for synthesizability, ensuring consistency between simulation results, and providing numerous syntax simplifications for common idioms. This approach enables ease of learning, improves the reliability and efficiency of the design process, and facilitates ease of code writing.

Interoperability

Designed with interoperability with SystemVerilog in mind, Veryl allows smooth integration and partial replacement with existing SystemVerilog components and projects. Furthermore, SystemVerilog source code transpiled from Veryl retains high readability, enabling seamless integration and debugging.

Productivity

Veryl comes with a rich set of development support tools, including package managers, build tools, real-time checkers compatible with major editors such as VSCode, Vim, Emacs, automatic completion, and automatic formatting. These tools accelerate the development process and significantly enhance productivity.

With these features, Veryl provides powerful support for designers to efficiently and productively conduct high-quality hardware design.

Features

In this chapter, we introduce the features of Veryl along with clear examples.

Real-time diagnostics

Issues such as undefined, unused, or unassigned variables are notified in real-time while editing in the editor. In the following example, adding the _ prefix to variables flagged as unused explicitly indicates their unused status, suppressing warnings.

diagnostics

If the video does not play1

Auto formatting

In addition to the automatic formatting feature integrated with the editor, formatting through the command line and formatting checks in CI are also possible.

format

If the video does not play1

Integrated test

Test code written by SystemVerilog or cocotb can be embeded in Veryl code, it can be executed through veryl test command.

#[test(test1)]
embed (inline) sv{{{
    module test1;
        initial begin
            assert (0) else $error("error");
        end
    endmodule
}}}

Dependency management

Veryl includes a built-in dependency management feature, allowing for easy incorporation of libraries by simply adding the repository path and version of the library on project settings like below.

[dependencies]
"https://github.com/veryl-lang/sample" = "0.1.0"

Generics

Code generation through generics achieves more reusable code than traditional parameter override. Prarmeters in function like the follwoign example, but also module names of instantiation, type names of struct definition, and so on can be parameterized.

SystemVerilog Veryl
function automatic logic [20-1:0] FuncA_20 (
    input logic [20-1:0] a
);
    return a + 1;
endfunction

function automatic logic [10-1:0] FuncA_10 (
    input logic [10-1:0] a
);
    return a + 1;
endfunction

logic [10-1:0] a;
logic [20-1:0] b;
always_comb begin
    a = FuncA_10(1);
    b = FuncA_20(1);
end
function FuncA::<T: const> (
    a: input logic<T>,
) -> logic<T> {
    return a + 1;
}

var a: logic<10>;
var b: logic<10>;
always_comb {
    a = FuncA::<10>(1);
    b = FuncA::<20>(1);
}

Clock Domain Annotation

If there are some clocks in a module, explicit clock domain annotation and unsafe (cdc) block at the clock domain boundaries are required. By the annotation, Veryl compiler detects unexpected clock domain crossing as error, and explicit unsafe (cdc) block eases to review clock domain crossing.

SystemVerilog Veryl
module ModuleA (
    input  i_clk_a,
    input  i_dat_a,
    output o_dat_a,
    input  i_clk_b,
    input  i_dat_b,
    output o_dat_b
);
    // Carefully!!!
    // From i_clk_a to i_clk_b
    assign o_dat_b = i_dat_a;
endmodule
module ModuleA (
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    i_dat_a: output `a logic,
    i_clk_b: input  `b clock,
    i_dat_b: input  `b logic,
    i_dat_b: output `b logic,
) {
    unsafe (cdc) {
        assign o_dat_b = i_dat_a;
    }
}

Trailing comma

Trailing comma is a syntax where a comma is placed after the last element in a list. It facilitates the addition and removal of elements and reduces unnecessary differences in version control systems.

SystemVerilog Veryl
module ModuleA (
    input  a,
    input  b,
    output o
);
endmodule
module ModuleA (
    a: input  logic,
    b: input  logic,
    o: output logic,
) {
}

Abstraction of clock and reset

There is no need to specify the polarity and synchronicity of the clock and reset in the syntax; these can be specified during build-time configuration. This allows generating code for both ASICs with negative asynchronous reset and FPGAs with positive synchronous reset from the same Veryl code.

Additionally, explicit clock and reset type enables to check whether clock and reset are correctly connected to registers. If there is a single clock and reset in the module, the connection can be omitted.

SystemVerilog Veryl
module ModuleA (
    input logic i_clk,
    input logic i_rst_n
);

always_ff @ (posedge i_clk or negedge i_rst_n) begin
    if (!i_rst_n) begin
    end else begin
    end
end

endmodule
module ModuleA (
    i_clk: input clock,
    i_rst: input reset,
){
    always_ff {
        if_reset {
        } else {
        }
    }
}

Documentation comment

Writing module descriptions as documentation comments allows for automatic documentation generation. You can use not only plain text but also the following formats:

SystemVerilog Veryl
// Comment
module ModuleA;
endmodule
/// Documentation comment written by Markdown
///
/// * list
/// * list
/// 
/// ```wavedrom
/// { signal: [{ name: "Alfa", wave: "01.zx=ud.23.456789" }] }
/// ```
module ModuleA {
}

Compound assignment operator in always_ff

There is no dedicated non-blocking assignment operator; within always_ff, non-blocking assignments are inferred, while within always_comb, blocking assignments are inferred. Therefore, various compound assignment operators can be used within always_ff just like within always_comb.

SystemVerilog Veryl
always_ff @ (posedge i_clk) begin
    if (a) begin
        x <= x + 1;
    end
end
always_ff {
    if a {
        x += 1;
    }
}

Individual namespace of enum variant

Variants of an enum are defined within separate namespaces for each enum, thus preventing unintended name collisions.

SystemVerilog Veryl
typedef enum logic[1:0] {
    MemberA,
    MemberB
} EnumA;

EnumA a;
assign a = MemberA;
enum EnumA: logic<2> {
    MemberA,
    MemberB
}

var a: EnumA;
assign a = EnumA::MemberA;

repeat of concatenation

By adopting the explicit repeat syntax as a repetition description in bit concatenation, readability improves over complex combinations of {}.

SystemVerilog Veryl
logic [31:0] a;
assign a = {{2{X[9:0]}}, {12{Y}}};
var a: logic<32>;
assign a = {X[9:0] repeat 2, Y repeat 12};

if / case expression

By adopting if and case expressions instead of the ternary operator, readability improves, especially when comparing a large number of items.

SystemVerilog Veryl
logic a;
assign a = X == 0 ? Y0 :
           X == 1 ? Y1 :
           X == 2 ? Y2 : 
                    Y3;
var a: logic;
assign a = case X {
    0      : Y0,
    1      : Y1,
    2      : Y2,
    default: Y3,
};

Range-based for / inside / outside

With notation representing closed intervals ..= and half-open intervals .., it is possible to uniformly describe ranges using for, inside, and outside (which denotes the inverse of inside).

SystemVerilog Veryl
for (int i = 0; i < 10; i++) begin
    a[i] =   X[i] inside {[1:10]};
    b[i] = !(X[i] inside {[1:10]});
end
for i: u32 in 0..10 {
    a[i] = inside  X[i] {1..=10};
    b[i] = outside X[i] {1..=10};
}

msb notation

The msb notation, indicating the most significant bit, eliminates the need to calculate the most significant bit from parameters, making intentions clearer.

SystemVerilog Veryl
logic a;
logic [WIDTH-1:0] X;
assign a = X[WIDTH-1];
var a: logic;
var X: logic<WIDTH>;
assign a = X[msb];

let statement

There is a dedicated let statement available for binding values simultaneously with variable declaration, which can be used in various contexts that were not supported in SystemVerilog.

SystemVerilog Veryl
logic tmp;
always_ff @ (posedge i_clk) begin
    tmp = b + 1;
    x <= tmp;
end
always_ff {
    let tmp: logic = b + 1;
    x = tmp;
}

Named block

You can define named blocks to limit the scope of variables.

SystemVerilog Veryl
if (1) begin: BlockA
end
:BlockA {
}

Visibility control

Modules without the pub keyword cannot be referenced from outside the project and are not included in automatic documentation generation. This allows distinguishing between what should be exposed externally from the project and internal implementations.

SystemVerilog Veryl
module ModuleA;
endmodule

module ModuleB;
endmodule
pub module ModuleA {
}

module ModuleB {
}
1

Some browsers by default pause the playback of GIF animations. Please check your browser settings.

Getting Started

Let’s start to use Veryl. In this section, we will install Veryl, create an example project, and build it.

Installation

Veryl can be intalled through the official toolchain installer verylup. We recommend to use verylup because it provides some usefule features like toolchain update.

Note: If you want install on an enviromnent without internet access, you can use offline installation.

Requirement

Veryl uses git command internally. Please confirm git can be launched.

Install verylup

Download binary

Download from release page, and extract to the directory in PATH.

Cargo

You can install with cargo.

cargo install verylup

Setup verylup

After installing verylup, the following command is required once at first. It downloads the latest toolchain and creates veryl and veryl-ls command at the same location as verylup.

verylup setup

Now veryl command can be used!

Editor integration

Visual Studio Code and Vim / Neovim are supported officially.

Visual Studio Code

For Visual Studio Code, Veryl extension is provided. The extension provides file type detection, syntex highlight and language server integration. You can install it by searching “Veryl” in extension panel or the following URL.

Veryl extension for Visual Studio Code

Vim / Neovim

For Vim / Neovim, Veryl plugin is provided. The plugin provides file type detection, syntex highlight. There are some instructions for plugin installation and language server integration in the following URL.

Vim / Neovim plugin

Other Editors

Veryl provides language server. So other editors supporting language server (ex. Emacs) can use it.

Shell Completion

Shell completion script for veryl and verylup is provided through verylup completion. For example, the following command generates completion script for zsh.

verylup completion zsh veryl   > _veryl
verylup completion zsh verylup > _verylup

Supported shells are below:

  • Bash
  • Elvish
  • Fish
  • PowerShell
  • Zsh

Please refer the documentation of each shell for usage of generated scripts.

Hello, World!

Create Project

At first, a new Veryl project can be created by:

veryl new hello

After the command, the following directory and file will be created.

$ veryl new hello
[INFO ]      Created "hello" project
$ cd hello
$ tree
.
`-- Veryl.toml

0 directories, 1 file

Veryl.toml is the project configuration.

[project]
name = "hello"
version = "0.1.0"

The description of all configuration is here.

Write Code

You can add source codes at an arbitrary position in the project directory. This is because Veryl project can be independent or integrated to other SystemVerilog project. The extension of Veryl’s source codes is .veryl.

For example, put the following code to src/hello.veryl.

module ModuleA {
    initial {
        $display("Hello, world!");
    }
}
$ tree
.
|-- src
|   `-- hello.veryl
`-- Veryl.toml

1 directory, 2 files

Note: Some source codes in the book have play button “▶” which will be appeared when mouse cursor is hovered at the code. If you click the button, the transpiled SystemVerilog code will appear. Please try to click the button of module ModuleA code.

Build Code

You can generate a SystemVerilog code by veryl build.

$ veryl build
[INFO ]   Processing file ([path to hello]/src/hello.veryl)
[INFO ]       Output filelist ([path to hello]/hello.f)
$ tree
.
|-- dependencies
|-- hello.f
|-- src
|   |-- hello.sv
|   `-- hello.veryl
`-- Veryl.toml

2 directories, 4 files

By default, SystemVerilog code will be generated at the same directory as Veryl code. The generated code is src/hello.sv.

module hello_ModuleA;
    initial begin
        $display("Hello, world!");
    end
endmodule

Additionally, hello.f which is the filelist of generated codes will be generated. You can use it for SystemVerilog compiler. The following example is to use Verilator.

$ verilator --cc -f hello.f

Clean up the Generated Code

The generated code can be cleaned up by veryl clean.

$ veryl clean
[INFO ]   Removing file ([path to hello]/src/hello.sv)
[INFO ]   Removing file ([path to hello]/src/hello.sv.map)
[INFO ]   Removing dir  ([path to hello]/dependencies)
[INFO ]   Removing file ([path to hello]/hello.f)

Code Examples

Veryl has the almost same semantics as SystemVerilog. If you are used to SystemVerilog, you will guess Veryl semantics with a small example source code.

This is a small example. In the following example, comments show the difference with SystemVerilog syntax.

module ModuleA (
    // name is first, and type is followed after `:`
    // bit width is represented by `<>`
    i_data: input  logic<10>,
    o_data: output logic<10>,

    // use `{}` instead of `begin`/`end`
) {
    assign o_data = i_data;
}

Additionally, the codeblocks in this chapter can be edit. Let’s try to edit and play each code.

A source code of Veryl has some module, interface and package like SystemVerilog. In this chapter, we’ll show the some example source codes of them.

Module

// module definition
module ModuleA #(
    param ParamA: u32 = 10,
    const ParamB: u32 = 10, // trailing comma is allowed
) (
    i_clk : input  clock            , // `clock` is a special type for clock
    i_rst : input  reset            , // `reset` is a special type for reset
    i_sel : input  logic            ,
    i_data: input  logic<ParamA> [2], // `[]` means unpacked array in SystemVerilog
    o_data: output logic<ParamA>    , // `<>` means packed array in SystemVerilog
) {
    // const parameter declaration
    //   `param` is not allowed in module
    const ParamC: u32 = 10;

    // variable declaration
    var r_data0: logic<ParamA>;
    var r_data1: logic<ParamA>;
    var r_data2: logic<ParamA>;

    // value binding
    let _w_data2: logic<ParamA> = i_data;

    // always_ff statement with reset
    //   `always_ff` can take a mandatory clock and a optional reset
    //   `if_reset` means `if (i_rst)`. This conceals reset porality
    //   `()` of `if` is not required
    //   `=` in `always_ff` is non-blocking assignment
    always_ff (i_clk, i_rst) {
        if_reset {
            r_data0 = 0;
        } else if i_sel {
            r_data0 = i_data[0];
        } else {
            r_data0 = i_data[1];
        }
    }

    // always_ff statement without reset
    always_ff (i_clk) {
        r_data1 = r_data0;
    }

    // clock and reset can be omitted
    // if there is a single clock and reset in the module
    always_ff {
        r_data2 = r_data1;
    }

    assign o_data = r_data1;
}

Instantiation

module ModuleA #(
    param ParamA: u32 = 10,
) (
    i_clk : input  clock        ,
    i_rst : input  reset        ,
    i_data: input  logic<ParamA>,
    o_data: output logic<ParamA>,
) {
    var r_data1: logic<ParamA>;
    var r_data2: logic<ParamA>;

    assign r_data1 = i_data + 1;
    assign o_data  = r_data2 + 2;

    // instance declaration
    //   `inst` keyword starts instance declaration
    //   port connnection can be specified by `()`
    //   each port connection is `[port_name]:[variable]`
    //   `[port_name]` means `[port_name]:[port_name]`
    inst u_module_b: ModuleB (
        i_clk          ,
        i_data: r_data1,
        o_data: r_data2,
    );

    // instance declaration with parameter override
    //   notation of parameter connection is the same as port
    inst u_module_c: ModuleC #(ParamA, ParamB: 10,);
}

module ModuleB #(
    param ParamA: u32 = 10,
) (
    i_clk : input  clock        ,
    i_data: input  logic<ParamA>,
    o_data: output logic<ParamA>,
) {
    assign o_data = 1;
}

module ModuleC #(
    param ParamA: u32 = 10,
    param ParamB: u32 = 10,
) () {}

Interface

// interface definition
interface InterfaceA #(
    param ParamA: u32 = 1,
    param ParamB: u32 = 1,
) {
    const ParamC: u32 = 1;

    var a: logic<ParamA>;
    var b: logic<ParamA>;
    var c: logic<ParamA>;

    // modport definition
    modport master {
        a: input ,
        b: input ,
        c: output,
    }

    modport slave {
        a: input ,
        b: input ,
        c: output,
    }
}

module ModuleA (
    i_clk: input clock,
    i_rst: input reset,
    // port declaration by modport
    intf_a_mst: modport InterfaceA::master,
    intf_a_slv: modport InterfaceA::slave ,
) {
    // interface instantiation
    inst u_intf_a: InterfaceA [10];
}

Package

// package definition
package PackageA {
    const ParamA: u32 = 1;
    const ParamB: u32 = 1;

    function FuncA (
        a: input logic<ParamA>,
    ) -> logic<ParamA> {
        return a + 1;
    }
}

module ModuleA {
    let a : logic<10> = PackageA::ParamA;
    let _b: logic<10> = PackageA::FuncA(a);
}

Language Reference

In this chapter, we’ll discuss the lauguage definition of Veryl.

Source Code Structure

Veryl’s source code is composed by some module, interface and package.

module ModuleA {}

module ModuleB {}

interface InterfaceA {}

package PackageA {}

The name of module, interface and package in the transpiled code will added project name as prefix. In the sample code, project_ will be added. It is to avoid name conflict between projects.

Lexical Structure

This chapter shows the lexical structure of Veryl. At the first, we’ll discuss about the general parts in it.

Encoding

The encoding of Veryl source code should be UTF-8.

White Space

(white space), \t and \n are treated as white space. All of them are skipped by Veryl’s parser.

Comment

Single line comment and multi line comment can be used. Almost all comment will be outputted at the transpiled code.

// single line comment

/*
multi

line

comment
*/

Documentation comment

Signle line comment starts with /// is treated as documentation comment. Documentation comment is used for document generation.

/// documentation comment

Identifier

Identifier is composed with ASCII alphabet and number and _. Leading number is not allowed. The following regular expression shows the definition.

[a-zA-Z_][a-zA-Z0-9_]*

Raw Identifier

Some keywords of Veryl can be used as identifier in SystemVerilog. To access these identifiers, raw identifier can be used. For example, clock which is a keyword of Veryl can be used as identifier like r#clock. The r#clock will be transpiled to clock in SystemVerilog.

String

String is surrounded by ". Escape by \ can be used like \", \n and so on.

"Hello, World!"

Operator

Almost all operators are the same as SystemVerilog. Please be careful the some differences.

  • <: less than operator which is the same as < in SystemVerilog.
  • >: greater than operator which is the same as > in SystemVerilog.
// unary arithmetic
a = +1;
a = -1;

// unary logical
a = !1;
a = ~1;

// unary reduce
a = &1;
a = |1;
a = ^1;
a = ~&1;
a = ~|1;
a = ~^1;
a = ^~1;

// binary arithmetic
a = 1 ** 1;
a = 1 * 1;
a = 1 / 1;
a = 1 % 1;
a = 1 + 1;
a = 1 - 1;

// binary shift
a = 1 << 1;
a = 1 >> 1;
a = 1 <<< 1;
a = 1 >>> 1;

// binary compare
a = 1 <: 1;
a = 1 <= 1;
a = 1 >: 1;
a = 1 >= 1;
a = 1 == 1;
a = 1 != 1;
a = 1 === 1;
a = 1 !== 1;
a = 1 ==? 1;
a = 1 !=? 1;

// binary bitwise
a = 1 & 1;
a = 1 ^ 1;
a = 1 ~^ 1;
a = 1 ^~ 1;
a = 1 | 1;

// binary logical
a = 1 && 1;
a = 1 || 1;

Number

Integer

// integer
0123456789
01_23_45_67_89

// binary
32'b01xzXZ
32'b01_xz_XZ

// octal
36'o01234567xzXZ
36'o01_23_45_67_xz_XZ

// decimal
32'd0123456789
32'd01_23_45_67_89

// hex
128'h0123456789abcdefxzABCDEFXZ
128'h01_23_45_67_89_ab_cd_ef_xz_AB_CD_EF_XZ

Set all bits

// all 0
'0

// all 1
'1

// all x
'x
'X

// all z
'z
'Z

Widthless integer

The bit width specification of integer can be omitted. If it is omitted, the appropriate width will be filled in the translated code.

module ModuleA {
    const a0: u64 = 'b0101;
    const a1: u64 = 'o01234567;
    const a2: u64 = 'd0123456789;
    const a3: u64 = 'h0123456789fffff;
}

Set sized bits

The bit width specification can be added to “set all bits”.

module ModuleA {
    const a0: u64 = 1'0;
    const a1: u64 = 2'1;
    const a2: u64 = 3'x;
    const a3: u64 = 4'z;
}

Floating point

// floating point
0123456789.0123456789
01_23_45_67_89.01_23_45_67_89

// floating with exponent
0123456789.0123456789e+0123456789
01_23_45_67_89.01_23_45_67_89E-01_23_45_67_89

Array Literal

'{} represents array literal. In the literal, expression, repeat keyword and default keyword can be placed.

module ModuleA {
    let _a: logic [3] = '{1, 2, 3};
    let _b: logic [3] = '{1 repeat 3}; // '{1, 1, 1}
    let _c: logic [3] = '{default: 3}; // '{3, 3, 3}
}

Data Type

In this chapter, we’ll discuss about data type.

Builtin Type

4-state data type which has variable width

logic is 4-state (0, 1, x, z) data type. The variable width can be specified by <> after logic. Multi-dimentional can be specified by <X, Y, Z,,,>.

module ModuleA {
    let _a: logic         = 1;
    let _b: logic<10>     = 1;
    let _c: logic<10, 10> = 1;
}

2-state data type which has variable width

bit is 2-state (0, 1) data type. The variable width can be specified by <> after bit. Multi-dimentional can be specified by <X, Y, Z,,,>.

module ModuleA {
    let _a: bit         = 1;
    let _b: bit<10>     = 1;
    let _c: bit<10, 10> = 1;
}

Type modifier

The following type modifiers can be added to logic and bit type.

  • signed: the MSB is treated as sign-bit
  • tri: tri-state type
module ModuleA {
    let _a: signed logic<10> = 1;
    let _b: tri logic   <10> = 1;
    let _c: signed bit  <10> = 1;
    let _d: tri bit     <10> = 1;
}

Integer type

There are some integer types:

  • u32: 32bit unsigned integer
  • u64: 64bit unsigned integer
  • i32: 32bit signed integer
  • i64: 64bit signed integer
module ModuleA {
    let _a: u32 = 1;
    let _b: u64 = 1;
    let _c: i32 = 1;
    let _d: i64 = 1;
}

Floating point type

There are some floating point types:

  • f32: 32bit floating point
  • f64: 64bit floating point

Both of them are represented as described by IEEE Std 754.

module ModuleA {
    let _a: f32 = 1.0;
    let _b: f64 = 1.0;
}

String type

string is string type.

module ModuleA {
    let _a: string = "";
}

Type type

type is a type which represents type kind. Variable of type can be defined as param or const only.

module ModuleA {
    const a: type = logic;
    const b: type = logic<10>;
    const c: type = u32;
}

User Defined Type

Struct

struct is composite data type. It can contain some fields, and these fields can be access through . operator.

module ModuleA {
    struct StructA {
        member_a: logic    ,
        member_b: logic<10>,
        member_c: u32      ,
    }

    var a: StructA;

    assign a.member_a = 0;
    assign a.member_b = 1;
    assign a.member_c = 2;
}

Enum

enum is enumerable type. It has some named variant, and the value of enum can be set to the one of them. The variant name can be specified by [enum name]::[variant name]. Each variant has the corresponding integral value. The value can be specified by =. Otherwise, it is assigned automatically.

module A {
    enum EnumA: logic<2> {
        member_a,
        member_b,
        member_c = 3,
    }

    var a: EnumA;

    assign a = EnumA::member_a;
}

If the type of enum is omitted, it will be infered from the variants automatically.

module A {
    enum EnumA {
        member_a,
        member_b,
        member_c = 3,
    }
}

Enum Encoding

By default, the value of each variant is assigned sequentially if it is omitted. If you want to specify value encoding, #[enum_encoding] attribute can be used. The available encodings are here:

  • sequential
  • onehot
  • gray
module A {
    #[enum_encoding(sequential)]
    enum EnumA {
        member_a,
    }

    #[enum_encoding(onehot)]
    enum EnumB {
        member_a,
    }

    #[enum_encoding(gray)]
    enum EnumC {
        member_a,
    }
}

Union

A Veryl union is a packed, untagged sum type and is transpiled to SystemVerilog’s packed union. Each union variant should have the same packed width as each other union variant.

module A {
    union UnionA {
        variant_a: logic<8>      ,
        variant_b: logic<2, 4>   ,
        variant_c: logic<4, 2>   ,
        variant_d: logic<2, 2, 2>,
    }
    var a          : UnionA;
    assign a.variant_a = 8'haa;
}

Typedef

The type keyword can be used to define a type alias to scalar or array types.

module A {
    type word_t    = logic <16>     ;
    type regfile_t = word_t     [16];
    type octbyte   = bit   <8>  [8] ;
}

Array

Array can be defined by appending [] to any data type. The length of array can be specified by the value in [].

module ModuleA {
    struct StructA {
        A: logic,
    }
    enum EnumA: logic {
        A,
    }

    var a: logic       [20];
    var b: logic  <10> [20];
    var c: u32         [20];
    var d: StructA     [20];
    var e: EnumA       [20];

    assign a[0] = 0;
    assign b[0] = 0;
    assign c[0] = 0;
    assign d[0] = 0;
    assign e[0] = 0;
}

Multi-dimentional array can be defined by [X, Y, Z,,,].

module ModuleA {
    struct StructA {
        A: logic,
    }
    enum EnumA: logic {
        A,
    }

    var a: logic       [10, 20, 30];
    var b: logic  <10> [10, 20, 30];
    var c: u32         [10, 20, 30];
    var d: StructA     [10, 20, 30];
    var e: EnumA       [10, 20, 30];

    assign a[0][0][0] = 0;
    assign b[0][0][0] = 0;
    assign c[0][0][0] = 0;
    assign d[0][0][0] = 0;
    assign e[0][0][0] = 0;
}

Clock / Reset

clock is a special types to represent clock wiring. There are 3 variants to specify clock polarity.

  • clock: clock type of which polarity is specified by the build option
  • clock_posedge: clock type of which polarity is positive
  • clock_negedge: clock type of which polarity is negative

reset is a special types to represent reset wiring. There are 5 variants to specify reset polarity and synchronicity.

  • reset: reset type of which polarity and synchronicity are specified by the build option
  • reset_async_high: async/high active reset type
  • reset_async_low: async/low active reset type
  • reset_sync_high: sync/active high reset type
  • reset_sync_low: sync/active low reset type

If there is no special requirement, clock and reset are recommended for code reusability.

module ModuleA (
    i_clk    : input `_ clock           ,
    i_clk_p  : input `_ clock_posedge   ,
    i_clk_n  : input `_ clock_negedge   ,
    i_rst    : input `_ reset           ,
    i_rst_a  : input `_ reset_async_high,
    i_rst_a_n: input `_ reset_async_low ,
    i_rst_s  : input `_ reset_sync_high ,
    i_rst_s_n: input `_ reset_sync_low  ,
) {
    var a: logic;
    var b: logic;
    var c: logic;

    always_ff (i_clk, i_rst) {
        if_reset {
            a = 0;
        } else {
            a = 1;
        }
    }

    always_ff (i_clk_p, i_rst_a) {
        if_reset {
            b = 0;
        } else {
            b = 1;
        }
    }

    always_ff (i_clk_n, i_rst_s_n) {
        if_reset {
            c = 0;
        } else {
            c = 1;
        }
    }
}

Expression

In this chapter, we’ll discuss about expression. Expression is combination of variable, operator, function call, and so on. It can be evaluated to a value.

Operator Precedence

In expression, operator precedence is almost the same as SystemVerilog.

OperatorAssociativityPrecedence
() [] :: .LeftHighest
+ - ! ~ & ~& | ~| ^ ~^ ^~ (unary)Left
**Left
* / %Left
+ - (binary)Left
<< >> <<< >>>Left
<: <= >: >=Left
== != === !== ==? !=?Left
& (binary)Left
^ ~^ ^~ (binary)Left
| (binary)Left
&&Left
||Left
= += -= *= /= %= &= ^= |=
<<= >>= <<<= >>>=
None
{} inside outside if case switchNoneLowest

Function Call

Function can be call by function_name(argument). System function of SystemVerilog like $clog2 can be used too.

module ModuleA {
    let _a: logic = PackageA::FunctionA(1, 1);
    let _b: logic = $clog2(1, 1);
}

package PackageA {
    function FunctionA (
        a: input logic,
        b: input logic,
    ) {}
}

Concatenation

{} represents bit concatenation. In {}, repeat keyword can be used to repeat specified operand.

module ModuleA {
    let a : logic<10> = 1;
    let b : logic<10> = 1;
    let _c: logic     = {a[9:0], b[4:3]};
    let _d: logic     = {a[9:0] repeat 10, b repeat 4};
}

If

Conditional expression using if can be used. The section which represents condition is placed after if keyword, and () is not required surrounding it. else is mandatory because if expression always have to be evaluated to value.

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

    assign b = if a == 0 {
        1
    } else if a >: 1 {
        2
    } else {
        3
    };
}

Case / Switch

Another conditional expression is case. case containts some arms like value: expression. If the expression after case keyword matches the left value of an arm, the right expression of the arm will be returned. As the value, range like ..= can be used too. default is a special arm which will be returned when all other arms are failed. default is mandatory because if expression always have to be evaluated to value.

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

    assign b = case a {
        0      : 1,
        1      : 2,
        3..=5  : 4,
        default: 5,
    };
}

switch is another form of case. switch containts some arms like expression: expression, and if the left expression is evaluated to 1, the right expression of the arm will be returned.

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

    assign b = switch {
        a == 0 : 1,
        a == 1 : 2,
        a == 2 : 4,
        default: 5,
    };
}

Bit Select

[] is bit select operator. If an expression is specified to [], single bit is selected. Bit range selection can be specified by [expression:expression].

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

    assign b = a[3];
    assign c = a[4:0];
}

Select by position and width

+: and -: notation can select by start position and width. [A+:B] means [(A+B-1):A], and [A-:B] means [A:(A-B+1)].

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

    assign b = a[3+:1];
    assign c = a[4-:2];
}

Select by index with step

step notation can select by index with step. [A step B] means “select index A in step B”, so it equals [(B*A)+:B].

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

    assign b = a[2 step 3];
}

Range

Range can be specified through range operator. There are two range operator:

  • ..: half-open interval
  • ..=: closed interval

Range can be used at some description like for statement.

module ModuleA {
    initial {
        for _i: u32 in 0..10 {}

        for _j: u32 in 0..=10 {}
    }
}

Msb / Lsb

msb and lsb can be used in bit selection by []. msb means most significant bit of the operand. lsb means least significant bit of the operand, it is the same as 0.

module ModuleA {
    let a : logic<10> = 1;
    let _b: logic<10> = a[msb - 3:lsb];
    let _c: logic<10> = a[msb - 1:lsb + 1];
}

Inside / Outside

inside check the specified expression is inside conditions which are specified in {}. Condition can be single expression or range. If the expression matches any condition, inside will return 1, otherwise 0. outside is vice versa.

module ModuleA {
    var a: logic;
    var b: logic;

    assign a = inside 1 + 2 / 3 {0, 0..10, 1..=10};
    assign b = outside 1 * 2 - 1 {0, 0..10, 1..=10};
}

Type Cast

as is type casting operator. Bit width speficied by based or baseless number or type name of user defined type can be used as the operand.

module ModuleA {
    var a: EnumA   ;
    var b: logic<2>;
    let x: logic    = 0;

    enum EnumA: logic {
        A,
        B,
    }

    assign a = x as EnumA;
    assign b = x as 2;
}

Statement

In this chapter, we’ll discuss about statement. Statement can be used in some declaration like always_ff, always_comb.

Assignment

Assignment statement is variable = expression;. Unlike SystemVerilog, assignment operator is = in both always_comb and always_ff. There are other assignment operators:

  • +=: addition assignment
  • -=: subtraction assignment
  • *=: multiplication assignment
  • /=: division assignment
  • %=: remainder assignment
  • &=: bitwise AND assignment
  • |=: bitwise OR assignment
  • ^=: bitwise XOR assignment
  • <<=: logical left shift assignment
  • >>=: logical right shift assignment
  • <<<=: arithmetic left shift assignment
  • >>>=: arithmetic right shift assignment
module ModuleA (
    i_clk: input clock,
) {
    let a: logic<10> = 1;
    var b: logic<10>;
    var c: logic<10>;
    var d: logic<10>;
    var e: logic<10>;

    always_comb {
        b =  a + 1;
        c += a + 1;
    }

    always_ff (i_clk) {
        d =  a + 1;
        e -= a + 1;
    }
}

Function Call

Function call can be used as statement. In this case, the return value of function will be ignored.

module ModuleA {
    initial {
        $display("Hello, world!");
    }
}

If

if can be used as statement. The difference from if expression is that statements are placed in {}.

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

    always_comb {
        if a == 0 {
            b = 1;
        } else if a >: 1 {
            b = 2;
        } else {
            b = 3;
        }
    }
}

Case / Switch

case and switch can be used as statement. The meaning of them are the same as Case / Switch expression except that the right-hand of arm is statement.

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

    always_comb {
        case a {
            0: b = 1;
            1: b = 2;
            2: {
                   b = 3;
                   b = 3;
                   b = 3;
               }
            default: b = 4;
        }
    }

    always_comb {
        switch {
            a == 0: c = 1;
            a == 1: c = 2;
            a == 2: {
                        c = 3;
                        c = 3;
                        c = 3;
                    }
            default: c = 4;
        }
    }
}

cond_type attribute

To specify unique, unique0 and priority in SystemVerilog, cond_type attribute can be used. The attribute can be annotated to case or if statement.

  • unique: There are no overlapping items. Error if no item matches.
  • unique0: There are no overlapping items. No error if no item matches.
  • priority: The first match is used only. Error if no item matches.
module ModuleA {
    let a: logic<10> = 1;
    var b: logic<10>;

    always_comb {
        #[cond_type(unique)]
        case a {
            0: b = 1;
            1: b = 2;
        }
    }
}

These attributes enable more aggressive optimization in synthesis, but if the expected condition is not complied, the result of synthesis will be broken. So these attributes are ignored by default, and if there is the following configuration, Veryl compiler emits them.

[build]
emit_cond_type = true

For

for statement represent repetition. Loop variable is placed before in keyword, and range is placed after it.

break can be used to break the loop.

module ModuleA {
    var a: logic<10>;

    always_comb {
        for i: u32 in 0..10 {
            a += i;

            if i == 5 {
                break;
            }
        }
    }
}

Return

return statement represents return from function. The expression after return keyword is the return value of the function.

module ModuleA {
    function FunctionA () -> u32 {
        return 0;
    }
}

Let

let statement represents a name bound to a value. It can be used in always_ff, always_comb and function declaration.

let statement can be placed anywhere in block.

module ModuleA (
    i_clk: input clock,
) {
    var a: logic;
    var b: logic;
    var c: logic;

    always_ff (i_clk) {
        let x: logic = 1;
        a = x + 1;
    }

    always_comb {
        let y: logic = 1;
        b = y + 1;

        let z: logic = 1;
        c = z + 1;
    }
}

Declaration

In this chapter, we’ll discuss about declaration.

Variable

Variable declaration is started by var keyword. After var, variable name, :, and the type of the variable are followed.

If there are unused variables, warning will be occured. Variable name starting with _ means unused variable, and suppresses the warning.

If you want to bind a value to a name at the declaration, let can be used instead of var.

module ModuleA {
    var _a: logic        ;
    var _b: logic<10>    ;
    var _c: logic<10, 10>;
    var _d: u32          ;
    let _e: logic         = 1;

    assign _a = 1;
    assign _b = 1;
    assign _c = 1;
    assign _d = 1;
}

Parameter

Parameter can be declarated as the same as variable. param keyword can be used at module header, it can be overridden at instantiation. const keyword can be used in module, it can’t be overridden.

module ModuleA #(
    param ParamA: u32 = 1,
) {
    const ParamB: u32 = 1;
}

Register

If a variable is assigned in always_ff declaration, it becomes register variable. Register variable will be mapped to flip-flop in synthesis phase.

always_ff has mandatory clock variable, optional reset variable, and {} block. Clock and reset are placed in (). The specified clock and reset should have clock / reset type and the witdh of them should be 1bit.

if_reset is a special keyword which can be used in always_ff. It means reset condition of the register variable. If if_reset is used, always_ff must have reset variable. if_reset can be conceal reset porality and synchronisity. The actual porality and synchronisity can be configured through [build] section of Veryl.toml.

If there is a single clock and reset in the module, clock and reset specification can be omitted.

module ModuleA (
    i_clk: input clock,
    i_rst: input reset,
) {
    var a: logic<10>;
    var b: logic<10>;
    var c: logic<10>;

    always_ff (i_clk) {
        a = 1;
    }

    always_ff (i_clk, i_rst) {
        if_reset {
            b = 0;
        } else {
            b = 1;
        }
    }

    always_ff {
        if_reset {
            c = 0;
        } else {
            c = 1;
        }
    }
}

Combinational

If a variable is assigned in always_comb declaration, it means combinational circuit.

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

    always_comb {
        b = a + 1;
    }
}

Assign

assign declaration can assign expression to variable.

module ModuleA {
    var a: logic<10>;

    assign a = 1;
}

Function

Function can be declared by function keyword. Arguments are placed in () and return type is placed after ->.

If function doesn’t have a return value, -> can be omitted.

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

    function FunctionA (
        a: input logic<10>,
    ) -> logic<10> {
        return a + 1;
    }

    function FunctionB (
        a: input logic<10>,
    ) {}

    assign b = FunctionA(a);

    initial {
        FunctionB(a);
    }
}

Initial / Final

Statements in initial are executed at the beginning of simulation, final is the end. Both will be ignored logical synthesis, and can be used as debug or assertion.

module ModuleA {
    initial {
        $display("initial");
    }

    final {
        $display("final");
    }
}

Attribute

Attribute can annotate some declarations like variable declaration.

sv Attribute

sv attribute represents SystemVerilog attribute. It will be transpiled to SystemVerilog attribute (* *).

module ModuleA {
    #[sv("ram_style=\"block\"")]
    let _a: logic<10> = 1;
    #[sv("mark_debug=\"true\"")]
    let _b: logic<10> = 1;
}

allow Attribute

allow attribute is used to disable specified lint check.

module ModuleA {
    #[allow(unused_variable)]
    let a: logic<10> = 1;
}

Available lint names are below:

  • unused_variable
  • missing_reset_statement
  • missing_port

ifdef/ifndef Attribute

ifdef and ifndef attribute is used to control whether the annotated code block is enabled by defined value. If DEFINE_A is defined, the code block with #[ifdef(DEFINE_A)] is enabled, and the code block with #[ifndef(DEFINE_A)] is disabled.

module ModuleA {
    #[ifdef(DEFINE_A)]
    {
        let _a: logic<10> = 1;
    }

    #[ifndef(DEFINE_A)]
    {
        let _b: logic<10> = 1;
    }
}

Generate

Declaration can be generated by for and if. Label which is shown by : is required to idenfity the generated declarations.

module ModuleA {
    var a: logic<10>;

    for i in 0..10 :label {
        if i >: 5 :label {
            assign a[i] = i + 2;
        } else { // label of else clause can be omit
            assign a[i] = i + 2;
        }
    }
}

Instantiation

inst keyword represents instantiation of modula and interface. The name of instance is placed after inst keyword, and the type of instance is placed after :. Parameter override is #(), and port connection is ().

module ModuleA #(
    param paramA: u32 = 1,
) {
    let a: logic<10> = 1;
    let b: logic<10> = 1;

    inst instB: ModuleB #(
        paramA    , // Parameter assignment by name
        paramB: 10,
    ) (
        a    , // Port connection by name
        bb: b,
    );
}

module ModuleB #(
    param paramA: u32 = 1,
    param paramB: u32 = 1,
) (
    a : input logic<10>,
    bb: input logic<10>,
) {}

Named Block

Label can be added to {} block. The named block has an individual namespace.

module ModuleA {
    :labelA {
        let _a: logic<10> = 1;
    }

    :labelB {
        let _a: logic<10> = 1;
    }
}

Import / Export

import declaration imports symbols from other packages. It can be placed at the top level or as a module/interface/package item. Wildcard pattern like package::* can be used as an argument of import declaration.

// file scope import
import $sv::SvPackage::*;

module ModuleA {
    import PackageA::*;
    import PackageA::paramA;
}

package PackageA {
    const paramA: u32 = 1;
}

export declaration exports symbols from the package to other. export * represents to export all symbols.

package PackageA {
    const paramA: u32 = 1;
}

package PackageB {
    import PackageA::*;
    export paramA;
}

package PackageC {
    import PackageA::*;
    export *;
}

Module

Module is one of top level components in source code. Module has overridable parameters, connection ports, and internal logic.

Overridable parameters can be declared in #(). Each parameter declaration is started by param keyword. After the keyword, an identifier, :, the type of the parameter, and a default value are placed.

Connection ports can be declared in (). Each port declaration is constructed by an identifier, :, port direction, and the type of the port. The available port directions are:

  • input: input port
  • output: output port
  • inout: bi-directional port
  • modport: modport of interface
  • interface: generic interface
module ModuleA #(
    param ParamA: u32 = 0,
    param ParamB: u32 = 0,
) (
    a: input  logic,
    b: input  logic,
    c: input  logic,
    x: output logic,
) {
    always_comb {
        if c {
            x = a;
        } else {
            x = b;
        }
    }
}

Generic interface

Generic interface is a special port direction. If interface is specified as the port direction, the port can be connected to arbitrary interface. Modport can be added to the interface like interface::ModPort too. Then the port can be connected to only the interface which has ModPort.

module ModuleA (
    bus_if  : interface,
    slave_if: interface::slave,
) {}

Interface

Interface is one of top level components in source code. Interface has overridable parameters, and interface definitions.

Overridable parameters are the same as them of module.

In interface definitions, modport can be declared. modport can be used as bundled port connection at the port declaration of module.

interface InterfaceA #(
    param ParamA: u32 = 0,
    param ParamB: u32 = 0,
) {
    var a: logic;
    var b: logic;

    modport master {
        a: output,
        b: input ,
    }

    modport slave {
        b: input ,
        a: output,
    }
}

Package

Package is one of top level components in source code. Package can organize some declarations like parameter and function.

To access an item in a package, :: symbol can be used like PackageA::ParamA.

package PackageA {
    const ParamA: u32 = 0;
}

SystemVerilog Interoperation

If you want to access to items of SystemVerilog, $sv namespace can be used. For example, “ModuleA” in SystemVerilog source code can be accessed by $sv::ModuleA. Veryl don’t check the existence of the items.

module ModuleA {
    let _a: logic = $sv::PackageA::ParamA;

    inst b: $sv::ModuleB;
    inst c: $sv::InterfaceC;
}

To access some identifiers which are used as Veryl’s keywords, raw identifier can be used.

module ModuleA (
    i_clk: input clock,
) {
    inst a: $sv::ModuleA (
        // clock: i_clk
        // ^ this is syntax error because `clock` is a keyword
        // Instead of it, `r#clock` can be used
        r#clock: i_clk,
    );
}

Visibility

By default, all top level items of a project (module, interface and package) are private. The “private” means they are not visible from other project.

pub keyword can be used to specify an item as public to other project. veryl doc will generate documents of public items only.

pub module ModuleA {}

pub interface InterfaceA {}

pub package PackageA {}

Foreign Language Integration

embed declaration

embed declaration can embed the code of foreign languages. The first argument of embed declaration shows the way of embedding. The following ways are supported:

  • inline: expand the code as is
  • cocotb: treated as cocotb based test

The code block are started by lang{{{ and ended by }}}. The following lang specifiers are supported:

  • sv: SystemVerilog
  • py: Python
embed (inline) sv{{{
    module ModuleSv;
    endmodule
}}}

include declaration

include declaration can include a file of foreign languages. The first argument is the same as embed declaration, and the second is a relative file path from the source code.

include(inline, "module.sv");

Integrated Test

Integrated test can be marked by #[test(test_name)] attribute. The marked block will be identified as test, and executed through veryl test command.

There are two way to describe integrated test:

  • SystemVerilog test
  • cocotb test

About RTL simulator used by veryl test, see Simulator. If --wave option is specified, waveforms are generated.

SystemVerilog test

SystemVerilog test can be described with inline specifier. The top level module of the block must have the same name as the test name.

The messages through $info, $warning, $error and $fatal system function are handled by Veryl compiler, and shown as exectution log. The calls of $error and $fatal are treated as test failure.

The following example, a SystemVerilog source code embeded by embed declaration are marked as test.

#[test(test1)]
embed (inline) sv{{{
    module test1;
        initial begin
            assert (0) else $error("error");
        end
    endmodule
}}}

cocotb test

cocotb test can be described with cocotb specifier. The target module name for test should be specified by the second argument of #[test] attribute.

#[test(test1, ModuleA)]
embed (cocotb) py{{{
    # cocotb code
}}}

Generics

Generics can define parameterized items which can’t achieved by parameter override. The following items support generics:

  • function
  • module
  • interface
  • package
  • struct
  • union

Each generic definition has generic parameters (often an uppercase letter is used like T) which can be placed as identifier or expression in the definition. Generic parameters are declarated after item’s identifier with ::<>.

Each generic parameter should have generic bound after colon like T: const. Generic bound represents what value can be passed to the generic parameter. The available generic bounds are below:

  • const: means constant value can be passed
  • type : means arbitrary type can be passed
  • named prototype

Named prototype is a special generic bound. See Prototype for details.

At the usage of generics, actual parameters can be given through ::<>. As the actual parameters, numeric literal and identifier concatenated by :: can be used.

Additionally, the actual parameters should be accessible at the position of the generics declaration. For example, module names can be used as actual parameters because it is accessible through the whole project. On the other hand, local parameters can’t be used as actual parameters in many cases. This is caused by that the local parameters is not accessible from the potision of the generics declaration.

Generic Function

module ModuleA {
    function FuncA::<T: const> (
        a: input logic<T>,
    ) -> logic<T> {
        return a + 1;
    }

    let _a: logic<10> = FuncA::<10>(1);
    let _b: logic<20> = FuncA::<20>(1);
}

Generic Module/Interface

module ModuleA {
    inst u0: ModuleB::<ModuleC>;
    inst u1: ModuleB::<ModuleD>;
}

proto module ProtoA;

module ModuleB::<T: ProtoA> {
    inst u: T;
}

module ModuleC for ProtoA {}
module ModuleD for ProtoA {}

Generic Package

module ModuleA {
    const A: u32 = PackageA::<1>::X;
    const B: u32 = PackageA::<2>::X;
}

package PackageA::<T: const> {
    const X: u32 = T;
}

Generic Struct

module ModuleA {
    type TypeA = i32;

    struct StructA::<T: type> {
        A: T,
    }

    // local defined type can be used
    // because `TypeA` is accessible at the definition of `StructA`
    var _a: StructA::<TypeA>          ;
    var _b: StructA::<PackageA::TypeB>;
    var _c: StructA::<PackageA::TypeC>;
}

package PackageA {
    type TypeB = u32;
    type TypeC = u64;
}

Default Parameter

Generic parameter can take a default value through = after the generic parameter. If the parameter specifications at call site is omitted, the default value is used.

module ModuleA {
    function FuncA::<T: const = 10> (
        a: input logic<T>,
    ) -> logic<T> {
        return a + 1;
    }

    let _a: logic<10> = FuncA::<>(1);
    let _b: logic<20> = FuncA::<20>(1);
}

Default parameters should be placed at the last of generic parameter list. If not, it causes ambiguous which parameters are omitted.

module ModuleA {
    function FuncA::<T: const, U: const = 1> (
        a: input logic<T>,
    ) -> logic<T> {
        return a + U;
    }

    // Error
    //function FuncA::<T: const = 1, U: const> (
    //    a: input logic<T>,
    //) -> logic<T> {
    //    return a + U;
    //}

    let _a: logic<10> = FuncA::<10>(1);
    let _b: logic<20> = FuncA::<20, 2>(1);
}

Prototype

Prototype is a special generic bound. It represents prototype which can be passed to the generic parameter. Currently module prototype is supported only.

In the following example, ProtoA is a module prototype which has parameter A and port i_dat and o_dat. By binding like T: ProtoA, it is represented that the generic parameter T should have the parameters and ports.

To use prototype, for implementation is required. ModuleC and ModuleD have for ProroA specifier which means the module satisfies the condision of ProroA. So the modules can be used as the generic parameter T of ModuleB.

module ModuleA {
    inst u0: ModuleB::<ModuleC>;
    inst u1: ModuleB::<ModuleD>;
}

proto module ProtoA #(
    param A: u32 = 1,
) (
    i_dat: input  logic,
    o_dat: output logic,
);

module ModuleB::<T: ProtoA> {
    inst u: T (
        i_dat: 0,
        o_dat: _,
    );
}

module ModuleC for ProtoA #(
    param A: u32 = 1,
) (
    i_dat: input  logic,
    o_dat: output logic,
) {
    assign o_dat = i_dat;
}

module ModuleD for ProtoA #(
    param A: u32 = 1,
) (
    i_dat: input  logic,
    o_dat: output logic,
) {
    assign o_dat = ~i_dat;
}

Clock Domain Annotation

If there are some clocks in a module, explicit clock domain annotation like `a is required. The annotation shows which clock domain each signals belong.

module ModuleA (
    // belong clock domain `a
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    o_dat_a: output `a logic,

    // belong clock domain `b
    i_clk_b: input  `b clock,
    i_dat_b: input  `b logic,
    o_dat_b: output `b logic,
) {
    // assignment in the same clock domain is safe
    assign o_dat_a = i_dat_a;
    assign o_dat_b = i_dat_b;
}

If there is single clock only in a module, the annotation can be omitted.

module ModuleA (
    i_clk: input  clock,
    i_dat: input  logic,
    o_dat: output logic,
) {
    assign o_dat = i_dat;
}

`_ is a special clock domain which means implicit clock domain. This can be used to specify that some clocks belong the same implicit clock domain.

module ModuleA (
    // all signals belong implicit clock domain
    i_clk   : input  `_ clock,
    i_clk_x2: input  `_ clock,
    i_dat   : input     logic,
    o_dat   : output    logic,
) {
    assign o_dat = i_dat;
}

Unsafe CDC

Veryl compiler detects clock domain crossing as error. So explicit unsafe (cdc) block is required for clock domain crossing. In the block, clock domain crossing error is suppressed, so designer should check whether it is safe carefully.

module ModuleA (
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    i_clk_b: input  `b clock,
    o_dat_b: output `b logic,
) {
    // Error "Clock domain crossing is detected"
    //assign o_dat_b = i_dat_a;

    unsafe (cdc) {
        assign o_dat_b = i_dat_a;
    }
}

Typically, synchronizer cells are inserted to the boundaries between clock domains. unsafe (cdc) block is required for this usage too.

module ModuleA (
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    i_clk_b: input  `b clock,
    o_dat_b: output `b logic,
) {
    unsafe (cdc) {
        inst u_sync: $sv::SynchronizerCell (
            i_clk: i_clk_b,
            i_dat: i_dat_a,
            o_dat: o_dat_b,
        );
    }
}

Standard Library

Veryl provides some useful and general modules as standard library. Standard library is under $std namespace, and it can be used without adding dependency.

The public API of standard library may be changed until Veryl 1.0 release.

module ModuleA {
    // $std::fifo is FIFO module in standard library
    inst u: $std::fifo (
        i_clk        : _,
        i_rst        : _,
        i_clear      : _,
        o_empty      : _,
        o_almost_full: _,
        o_full       : _,
        o_word_count : _,
        i_push       : _,
        i_data       : _,
        i_pop        : _,
        o_data       : _,
    );
}

The full list and document of standard library is https://std.veryl-lang.org.

Development Environment

In this chapter, we’ll discuss about development environment including project configuration and development tools.

Project Configuration

The [project] section

The first section of Veryl.toml is [project]. The mandatory fields are name and version.

The name field

The project name is used as prefix in the generated codes. So the name must start with alphabet or _, and use only alphanumeric characters or _.

The version field

The project version should follow Semantic Versioning. The version is constructed by the following three numbers.

  • Major – increment at incompatible changes
  • Minor – increment at adding features with backward compatibility
  • Patch – increment at bug fixes with backward compatibility
[project]
version = "0.1.0"

The authors field

The optional authors field lists in an array the people or organizations that are considered the “authors” of the project. The format of each string in the list is free. Name only, e-mail address only, and name with e-mail address included within angled brackets are commonly used.

[project]
authors = ["Fnu Lnu", "anonymous@example.com", "Fnu Lnu <anonymous@example.com>"]

The description field

The description is a short blurb about the project. This should be plane text (not Markdown).

The license field

The license field contains the name of license that the project is released under. The string should be follow SPDX 2.3 license expression.

[project]
license = "MIT OR Apache-2.0"

The repository field

The repository field should be a URL to the source repository for the project.

[project]
repository = "https://github.com/veryl-lang/veryl"

The [build] section

The [build] section contains the configurations of code generation. Available configurations is here.

The [format] section

The [format] section contains the configurations of code formatter. Available configurations is here.

The [lint] section

The [lint] section contains the configurations of linter. Available configurations is here.

The [test] section

The [test] section contains the configurations of test by RTL simulator. Available configurations is here.

The [publish] section

The [publish] section contains the configurations of publishing. Available configurations is here.

The [dependencies] section

The [dependencies] section contains library dependencies. Available configurations is here.

Build

[build] section specifies the configuration for code generation.

The clock_type field

The clock_type field specifies which clock edge is used to drive flip-flop. The available types are below:

  • posedge – positive edge
  • negedge – negetive edge

The reset_type field

The reset_type field specifies reset polarity and synchronisity. The available types are below:

  • async_low – asynchronous and active low
  • async_high – asynchronous and active high
  • sync_low – synchronous and active low
  • sync_high – synchronous and active high

The filelist_type field

The filelist_type field specifies filelist format. The available types are below:

  • absolute – plane text filelist including absolute file paths
  • relative – plane text filelist including relative file paths
  • flgenflgen filelist

The target field

The target field specifies where the generated codes will be placed at. The available types are below:

  • source – as the same directory as the source code
  • directory – specified directory
  • bundle – specified file

If you want to use directory or bundle, you should specify the target path by path key.

[build]
target = {type = "directory", path = "[dst dir]"}

The implicit_parameter_types field

The implicit_parameter_types field lists the types which will be elided in parameter declaration of the generated codes. This is because some EDA tools don’t support parameter declaration with specific types (ex.string). If you want to elide string, you can specify like below:

[build]
implicit_parameter_types = ["string"]

The omit_project_prefix field

If omit_project_prefix is set to true, the project prefix of module/interface/package name will be omitted. This is false by default.

[build]
omit_project_prefix = true

The strip_comments field

If strip_comments is set to true, all comments will be stripped. This is false by default.

[build]
strip_comments = true

The *_prefix and *_suffix field

*_prefix and *_suffix represent additional prefix and suffix for the generated code. The available configurations are below:

  • clock_posedge_prefix: Prefix for clock type at clock_type = posedge
  • clock_posedge_suffix: Suffix for clock type at clock_type = posedge
  • clock_negedge_prefix: Prefix for clock type at clock_type = negedge
  • clock_negedge_suffix: Suffix for clock type at clock_type = negedge
  • reset_high_prefix: Prefix for reset type at reset_type = *_high
  • reset_high_suffix: Suffix for reset type at reset_type = *_high
  • reset_low_prefix: Prefix for reset type at reset_type = *_low
  • reset_low_suffix: Suffix for reset type at reset_type = *_low

The sourcemap_target field

The sourcemap_target field specifies where the generated source maps will be placed at. The available types are below:

  • target – as the same directory as the target code
  • directory – specified directory
  • none – no source map

If you want to use directory, you should specify the target path by path key.

[build]
sourcemap_target = {type = "directory", path = "[dst dir]"}

The expand_inside_operation field

If expand_inside_operation is set to true, operations using inside operator will be expended to logic using ==? operator. This is because some EDA tools don’t support inside operator. This is false by default.

[build]
expand_inside_operation = true

The exclude_std field

If exclude_std is set to true, standard library will not be included.

[build]
exclude_std = true

The emit_cond_type field

If emit_cond_type is set to true, condition type like unique, unique0 and priority is emitted.

[build]
emit_cond_type = true

Format

[format] section specifies the configuration for formatter like below:

[format]
indent_width = 4

Available configurations

ConfigurationValueDescription
indent_widthintegerindent width by space

Lint

[lint] section specifies the configuration for linter like below:

[lint.naming]
case_enum = "snake"

Available configurations

The [lint.naming] section

This section contains configurations of naming conventions.

ConfigurationValueDescription
case_enumcase type1case style of enum
case_functioncase type1case style of function
case_function_inoutcase type1case style of inout argument
case_function_inputcase type1case style of input argument
case_function_outputcase type1case style of output argument
case_function_refcase type1case style of ref argument
case_instancecase type1case style of instance
case_interfacecase type1case style of interface
case_modportcase type1case style of modport
case_modulecase type1case style of module
case_packagecase type1case style of package
case_parametercase type1case style of parameter
case_port_inoutcase type1case style of inout port
case_port_inputcase type1case style of input port
case_port_modportcase type1case style of modport port
case_port_outputcase type1case style of output port
case_regcase type1case style of register type variable2
case_structcase type1case style of struct
case_unioncase type1case style of union
case_varcase type1case style of variable
case_wirecase type1case style of wire type variable3
prefix_enumstringprefix of enum
prefix_functionstringprefix of function
prefix_function_inoutstringprefix of inout argument
prefix_function_inputstringprefix of input argument
prefix_function_outputstringprefix of output argument
prefix_function_refstringprefix of ref argument
prefix_instancestringprefix of instance
prefix_interfacestringprefix of interface
prefix_modportstringprefix of modport
prefix_modulestringprefix of module
prefix_packagestringprefix of package
prefix_parameterstringprefix of parameter
prefix_port_inoutstringprefix of inout port
prefix_port_inputstringprefix of input port
prefix_port_modportstringprefix of modport port
prefix_port_outputstringprefix of output port
prefix_regstringprefix of register type variable2
prefix_structstringprefix of struct
prefix_unionstringprefix of union
prefix_varstringprefix of variable
prefix_wirestringprefix of wire type variable3
suffix_enumstringsuffix of enum
suffix_functionstringsuffix of function
suffix_function_inoutstringsuffix of inout argument
suffix_function_inputstringsuffix of input argument
suffix_function_outputstringsuffix of output argument
suffix_function_refstringsuffix of ref argument
suffix_instancestringsuffix of instance
suffix_interfacestringsuffix of interface
suffix_modportstringsuffix of modport
suffix_modulestringsuffix of module
suffix_packagestringsuffix of package
suffix_parameterstringsuffix of parameter
suffix_port_inoutstringsuffix of inout port
suffix_port_inputstringsuffix of input port
suffix_port_modportstringsuffix of modport port
suffix_port_outputstringsuffix of output port
suffix_regstringsuffix of register type variable2
suffix_structstringsuffix of struct
suffix_unionstringsuffix of union
suffix_varstringsuffix of variable
suffix_wirestringsuffix of wire type variable3
re_forbidden_enumregex4regex forbidden of enum
re_forbidden_functionregex4regex forbidden of function
re_forbidden_function_inoutregex4regex forbidden of inout argument
re_forbidden_function_inputregex4regex forbidden of input argument
re_forbidden_function_outputregex4regex forbidden of output argument
re_forbidden_function_refregex4regex forbidden of ref argument
re_forbidden_instanceregex4regex forbidden of instance
re_forbidden_interfaceregex4regex forbidden of interface
re_forbidden_modportregex4regex forbidden of modport
re_forbidden_moduleregex4regex forbidden of module
re_forbidden_packageregex4regex forbidden of package
re_forbidden_parameterregex4regex forbidden of parameter
re_forbidden_port_inoutregex4regex forbidden of inout port
re_forbidden_port_inputregex4regex forbidden of input port
re_forbidden_port_modportregex4regex forbidden of modport port
re_forbidden_port_outputregex4regex forbidden of output port
re_forbidden_regregex4regex forbidden of register type variable2
re_forbidden_structregex4regex forbidden of struct
re_forbidden_unionregex4regex forbidden of union
re_forbidden_varregex4regex forbidden of variable
re_forbidden_wireregex4regex forbidden of wire type variable3
re_required_enumregex4regex required of enum
re_required_functionregex4regex required of function
re_required_function_inoutregex4regex required of inout argument
re_required_function_inputregex4regex required of input argument
re_required_function_outputregex4regex required of output argument
re_required_function_refregex4regex required of ref argument
re_required_instanceregex4regex required of instance
re_required_interfaceregex4regex required of interface
re_required_modportregex4regex required of modport
re_required_moduleregex4regex required of module
re_required_packageregex4regex required of package
re_required_parameterregex4regex required of parameter
re_required_port_inoutregex4regex required of inout port
re_required_port_inputregex4regex required of input port
re_required_port_modportregex4regex required of modport port
re_required_port_outputregex4regex required of output port
re_required_regregex4regex required of register type variable2
re_required_structregex4regex required of struct
re_required_unionregex4regex required of union
re_required_varregex4regex required of variable
re_required_wireregex4regex required of wire type variable3
1

The available values are

  • "snake" – snake_case
  • "screaming_snake" – SCREAMING_SNAKE_CASE
  • "lower_camel" – lowerCamelCase
  • "upper_camel" – UpperCamelCase
4

Regular expression string like ".*". The available syntax is here.

2

Register type means that the variable is assigned in always_ff. It will be mapped to flip-flop in synthesis phase.

3

Wire type means that the variable is assigned in always_comb. It will be mapped to wire in synthesis phase.

Test

[test] section specifies the configuration for integrated unit test like below:

[test]
simulator = "vcs"

Available configurations

The [test] section

This section contains configurations of test.

ConfigurationValueDescription
simulatorsimulator name1default simulator
1

The available values are

  • "verilator"
  • "vcs"
  • "vivado"

The [test.verilator] section

This section contains configurations of test by Verilator.

ConfigurationValueDescription
compile_args[string]additional arguments to verilator command
simulate_args[string]additional arguments to simulation binary

The [test.vcs] section

This section contains configurations of test by VCS.

ConfigurationValueDescription
compile_args[string]additional arguments to vcs command
simulate_args[string]additional arguments to simulation binary

The [test.vivado] section

This section contains configurations of test by Vivado.

ConfigurationValueDescription
compile_args[string]additional arguments to xvlog command
elaborate_args[string]additional arguments to xelab command
simulate_args[string]additional arguments to xsim command

The waveform_target field

The waveform_target field specifies where the generated waveforms will be placed at. The available types are below:

  • target – as the same directory as the target code
  • directory – specified directory

If you want to use directory, you should specify the target path by path key.

[build]
waveform_target = {type = "directory", path = "[dst dir]"}

Publish

[publish] section specifies the configuration for publishing a project like below:

[publish]
bump_commit = true
bump_commit_message = "Bump"

Available configurations

ConfigurationValueDefaultDescription
bump_commitbooleanfalseautomatic commit after bump
publish_commitbooleanfalseautomatic commit after publish
bump_commit_mesasgestring“chore: Bump version”commit message after bump
publish_commit_mesasgestring“chore: Publish”commit message after publish

Dependencies

If you want to add other Veryl projects to dependencies of your project, you can add them to [dependencies] section in Veryl.toml. The left hand side of entry is path to the dependency, and the right hand side is version.

[dependencies]
"https://github.com/veryl-lang/sample" = "0.1.0"

By default, the namespace of the dependency is the same as the project name of the dependency. If you want to specify namespace, you can use name field.

[dependencies]
"https://github.com/veryl-lang/sample" = {version = "0.1.0", name = "veryl_sample_alt"}

If you want to use many versions of the same dependency path, you can specify each name.

[dependencies]
"https://github.com/veryl-lang/sample" = [
    {version = "0.1.0", name = "veryl_sample1"},
    {version = "0.2.0", name = "veryl_sample2"},
]

Usage of dependency

After adding dependencies to Veryl.toml, you can use module, interface and package in the dependencies. The following example uses delay module in the veryl_sample dependency.

module ModuleA (
    i_clk: input  clock,
    i_rst: input  reset,
    i_d  : input  logic,
    o_d  : output logic,
) {
    inst u_delay: veryl_sample::delay (
        i_clk,
        i_rst,
        i_d  ,
        o_d  ,
    );
}

Note: The result of play button in the above code is not exact because it doesn’t use dependency resolution. Actually the module name becomes veryl_sample_delay

Version Requirement

The version field of [dependencies] section shows version requirement. For example, version = "0.1.0" means the latest version which has compatibility with 0.1.0. The compatibility is judged by Semantic Versioning. A version is constructed from the following three parts.

  • MAJOR version when you make incompatible API changes
  • MINOR version when you add functionality in a backwards compatible manner
  • PATCH version when you make backwards compatible bug fixes

If MAJOR version is 0, MINOR is interpreted as incompatible changes.

If there are 0.1.0 and 0.1.1 and 0.2.0, 0.1.1 will be selected. This is because

  • 0.1.0 is compatible with 0.1.0.
  • 0.1.1 is compatible with 0.1.0.
  • 0.2.0 is not compatible with 0.1.0.
  • 0.1.1 is the latest in the compatible versions.

The version field allows other version requirement representation like =0.1.0. Please see version requirement of Rust for detailed information: Specifying Dependencies.

Publish Project

To publish your project, veryl publish can be used. Publising means to associate a version with a git revision.

$ veryl publish
[INFO ]   Publishing release (0.2.1 @ 297bc6b24c5ceca9e648c3ea5e01011c67d7efe7)
[INFO ]      Writing metadata ([path to project]/Veryl.pub)

veryl publish generates Veryl.pub which contains published version information like below.

[[releases]]
version = "0.2.1"
revision = "297bc6b24c5ceca9e648c3ea5e01011c67d7efe7"

After generating Veryl.pub, publishing sequence is completed by git add, commit and push. The git branch to be committed must be the default branch because Veryl search Veryl.pub in the default branch.

$ git add Veryl.pub
$ git commit -m "Publish"
$ git push

If you enable automatic commit by publish_commit in [publish] section of Veryl.toml, git add and commit will be executed after publish.

$ veryl publish
[INFO ]   Publishing release (0.2.1 @ 297bc6b24c5ceca9e648c3ea5e01011c67d7efe7)
[INFO ]      Writing metadata ([path to project]/Veryl.pub)
[INFO ]   Committing metadata ([path to project]/Veryl.pub)

Version Bump

You can bump version with publish at the same time by --bump option. As the same as publish, bump_commit in [publish] section of Veryl.toml can specify automatic commit after bump version.

$ veryl publish --bump patch
[INFO ]      Bumping version (0.2.1 -> 0.2.2)
[INFO ]     Updating version field ([path to project]/Veryl.toml)
[INFO ]   Committing metadata ([path to project]/Veryl.toml)
[INFO ]   Publishing release (0.2.2 @ 159dee3b3f93d3a999d8bac4c6d26d51476b178a)
[INFO ]      Writing metadata ([path to project]/Veryl.pub)
[INFO ]   Committing metadata ([path to project]/Veryl.pub)

Configuration

The available configurations are here.

Directory Layout

Veryl supports arbitrary directory layout. This is because the optimal directory layout for an independent project and an integrated project within other projects is different.

In this section, we suggest some directory layout patterns.

Single source directory

This pattern contains all sources in src directory. In src, you can configure arbitrary sub directories.

$ tree
.
|-- src
|   |-- module_a.veryl
|   `-- module_b
|       |-- module_b.veryl
|       `-- module_c.veryl
`-- Veryl.toml

2 directories, 4 files

Veryl gathers all *.veryl files and generates codes at the same directory as the source by default. You can show the behavior explicitly by the following configuration.

[build]
target = "source"

After veryl build, the directory structure will become below:

$ tree
.
|-- dependencies
|-- prj.f
|-- src
|   |-- module_a.sv
|   |-- module_a.veryl
|   `-- module_b
|       |-- module_b.sv
|       |-- module_b.veryl
|       |-- module_c.sv
|       `-- module_c.veryl
`-- Veryl.toml

3 directories, 8 files

Single source and target directory

If you want to place the generated codes into a directory, you can use target configure in [build] section of Veryl.toml.

[build]
target = {type = "directory", path = "target"}

The directory layout of this configure will become below:

$ tree
.
|-- dependencies
|-- prj.f
|-- src
|   |-- module_a.veryl
|   `-- module_b
|       |-- module_b.veryl
|       `-- module_c.veryl
|-- target
|   |-- module_a.sv
|   |-- module_b.sv
|   `-- module_c.sv
`-- Veryl.toml

4 directories, 8 files

Multi source directory

If you want to add a veryl project to the existing SystemVerilog project, you can choose the following structure.

$ tree
.
|-- dependencies
|-- module_a
|   |-- module_a.sv
|   `-- module_a.veryl
|-- module_b
|   |-- module_b.sv
|   |-- module_b.veryl
|   |-- module_c.sv
|   `-- module_c.veryl
|-- prj.f
|-- sv_module_x
|   `-- sv_module_x.sv
|-- sv_module_y
|   `-- sv_module_y.sv
`-- Veryl.toml

5 directories, 10 files

The generated prj.f lists all generated files. So you can use it along with the existing SystemVerilog filelists.

About .gitignore

Veryl doesn’t provide the default .gitignore. This is because which files should be ignored is different by each projects.

The candidates of .gitignore is below:

  • dependencies/
  • target/
  • *.sv
  • *.f

Formatter

Source code can be formatted by veryl fmt command. Alternatively, language server support formatting through textDocument/formatting request.

The available configurations are here.

Linter

Lint check is executed at veryl check or veryl build. Alternatively, language server checks lint in real time.

The available configurations are here.

Simulator

Test by RTL simulator is executed through veryl test. Supported simulators are below:

Verilator is the default simulator. If no simulator is specified through Veryl.toml and command-line option, it will be used.

The available configurations are here.

cocotb

cocotb tests require python3 environment in which cocotb is installed. The supported version of cocotb is 1.9.0 only.

For example, it can be installed by the following command.

$ pip3 install cocotb==1.9.0

As simulator backend, Verilator is only supported.

Language Server

veryl-ls is a language server binary. If you want to use it, editor configuration or plugin to use it is required.

The available configurations are below. These can be specified by each editor’s config.

ConfigurationValueDefaultDescription
useOperatorCompletionbooleanfalseuse operator (e.g. ‘>:’, ‘>>’) completion

Compatibility

Some tools supporting SystemVerilog don’t support some features. Code generation can be customized by configuration of Veryl.toml to support these tools.

Vivado

String parameter

Vivado don’t support parameter which is typed as string.

parameter string a = "A";

So you can use implicit_parameter_types like below:

[build]
implicit_parameter_types = ["string"]

By the configuration, the generated code becomes like below:

parameter a = "A";

Quartus

inside operator

Quartus don’t support inside operator. So you can use expand_inside_operation like below:

[build]
expand_inside_operation = true

By the configuration, operations using inside operator will be expanded to logic using ==? operator.

Documentation

Document of project can be generated by veryl doc command. All public modules, interfaces and packages will be listed in it. (See Visibility )

If you want to add a detailed description, you can add documentation comment. In the documentation comment, Markdown syntax can be used.

The following formats are supported too.

  • Waveform description based on WaveDrom
  • Diagram description based on Mermaid

Each syntax can be used in wavedrom and mermaid code block.

Please refer the following for the detailed syntax.

/// The detailed description of ModuleA
///
/// * list item0
/// * list item1
///
/// ```wavedrom
/// {signal: [
///   {name: 'clk', wave: 'p.....|...'},
///   {name: 'dat', wave: 'x.345x|=.x', data: ['head', 'body', 'tail', 'data']},
///   {name: 'req', wave: '0.1..0|1.0'},
///   {},
///   {name: 'ack', wave: '1.....|01.'}
///
/// ]}
/// ```
pub module ModuleA #(
    /// Data width
    param ParamA: u32 = 1,
    const ParamB: u32 = 1,
) (
    i_clk : input  clock        , /// Clock
    i_rst : input  reset        , /// Reset
    i_data: input  logic<ParamA>, /// Data input
    o_data: output logic<ParamA>, /// Data output
) {
    assign o_data = 0;
}

The available configurations are below. These can be specified in [doc] section of Veryl.toml.

[doc]
path = "document"
ConfigurationValueDefaultDescription
pathstring“doc”path to output directory

GitHub Action

The official GitHub action to download a prebuilt binary of Veryl is provided.

https://github.com/marketplace/actions/setup-veryl

The examples of GitHub action script are below:

  • Format and build check
name: Check
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: veryl-lang/setup-veryl@v1
    - run: veryl fmt --check
    - run: veryl check
  • Publish document through GitHub Pages
name: Deploy
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: veryl-lang/setup-veryl@v1
    - run: veryl doc
    - uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: doc

For this purpose, we provide GitHub action veryl-lang/setup-verilator.

name: Test
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-22.04
    steps:
    - uses: actions/checkout@v4
    - uses: veryl-lang/setup-veryl@v1
    - uses: veryl-lang/setup-verilator@v1
    - run: veryl test --sim verilator

Source Map

Source map is a file to be used for tracking location from SystemVerilog to Veryl. By this file, a file path, line and column of SystemVerilog can be translated into the position in Veryl.

By default, Veryl generates source map at the same directory as generated SystemVerilog with .sv.map extension. Source map generation can be configured through sourcemap_target field in Veryl.toml.

The format of source map follows Source Map Revision 3. The convention of linking generated code to source map is almost the same as JavaScript, but relative path is used only:

//# sourceMappingURL=<relative path>

So, if there is the above comment at the end of a SystemVerilog file, it shows source map can be used.

sourcemap-resolver

sourcemap-resolver which is shipped together Veryl compiler can be used to annotate arbitrary text file like below:

ERROR: [VRFC 10-4982] syntax error near 'endmodule' [/path/test.sv:23]
                                                     ^-- /path/test.veryl:18:18

The first line is the original text, and the second line is added by sourcemap-resolver. The usage examples are below:

$ sourcemap-resolver test.log      # annotate the existing log
$ [command] | sourcemap-resolver   # on-the-fly annotation by pipe

verylup

verylup is the official toolchain installer of Veryl. It eases to update and switch toolchains.

Update toolchain

The following command updates Veryl toolchain and verylup to the latest version.

verylup update

Install a specific toolchain

If you want to use a specific version of Veryl, verylup install can be used.

verylup install 0.12.0

After installing it, + version specifier can be used in veryl command like below:

veryl +0.12.0 build

Toolchain override for directories

If you want to use a specific version of Veryl for specific directories, verylup override can be used.

verylup override set 0.12.0

verylup override can be executed in arbitrary directories in a Veryl project. After this command, the default toolchain becomes 0.12.0 in the project.

Offline installation

If you want to verylup on an environment without internet access, offline installation can be used. The procedure of offline installation is below:

  • Download the latest toolchain package from Veryl release page.
  • Execute veryl setup with --pkg specification like the following command.
verylup setup --offline --pkg veryl-x86_64-linux.zip

If you want to update/install toolchain, --pkg specification is required as the same as setup.

verylup update --pkg veryl-x86_64-linux.zip
verylup install 0.12.0 --pkg veryl-x86_64-linux.zip

For Veryl Developer

For Veryl developer, a special toolchain target local is prepared. If verylup install local is executed in your local Veryl repository, the built toolchain is installed as local toolchain. local becomes the default toolchain if it exists.

// Build and install the toolchain from local Veryl repository
verylup install local

// Use the built toolchain
veryl build

// Use the latest toolchain
veryl +latest build

Appendix

Formal Syntax

Veryl’s parser is based on parser generator parol. The following syntex definition of parol is formal syntax.


%start Veryl
%title "Veryl grammar"
%comment "Empty grammar generated by `parol`"
%user_type VerylToken = crate::veryl_token::VerylToken
%user_type Token = crate::veryl_token::Token

%scanner Embed {
    %auto_newline_off
    %auto_ws_off
}

%scanner Generic {
}

%%

// ----------------------------------------------------------------------------
// Terminal
// ----------------------------------------------------------------------------

// Longest match should be first

CommentsTerm          : <INITIAL, Generic       >"(?:(?:(?://.*(?:\r\n|\r|\n|$))|(?:(?ms)/\u{2a}.*?\u{2a}/))\s*)+"                     : Token;
StringLiteralTerm     : <INITIAL, Generic       >"\u{0022}(?:\\[\u{0022}\\/bfnrt]|u[0-9a-fA-F]{4}|[^\u{0022}\\\u0000-\u001F])*\u{0022}": Token;
ExponentTerm          : <INITIAL, Generic       >/[0-9]+(?:_[0-9]+)*\.[0-9]+(?:_[0-9]+)*[eE][+-]?[0-9]+(?:_[0-9]+)*/                   : Token;
FixedPointTerm        : <INITIAL, Generic       >/[0-9]+(?:_[0-9]+)*\.[0-9]+(?:_[0-9]+)*/                                              : Token;
BasedTerm             : <INITIAL, Generic       >/(?:[0-9]+(?:_[0-9]+)*)?'s?[bodh][0-9a-fA-FxzXZ]+(?:_[0-9a-fA-FxzXZ]+)*/              : Token;
AllBitTerm            : <INITIAL, Generic       >/(?:[0-9]+(?:_[0-9]+)*)?'[01xzXZ]/                                                    : Token;
BaseLessTerm          : <INITIAL, Generic       >/[0-9]+(?:_[0-9]+)*/                                                                  : Token;
MinusColonTerm        : <INITIAL                >'-:'                                                                                  : Token;
MinusGTTerm           : <INITIAL                >'->'                                                                                  : Token;
PlusColonTerm         : <INITIAL                >'+:'                                                                                  : Token;
AssignmentOperatorTerm: <INITIAL                >"\+=|-=|\*=|/=|%=|&=|\|=|\^=|<<=|>>=|<<<=|>>>="                                       : Token;
Operator11Term        : <INITIAL                >"\*\*"                                                                                : Token;
Operator10Term        : <INITIAL                >"/|%"                                                                                 : Token;
Operator09Term        : <INITIAL                >"\+|-"                                                                                : Token;
Operator08Term        : <INITIAL                >"<<<|>>>|<<|>>"                                                                       : Token;
Operator07Term        : <INITIAL                >"<=|>=|<:|>:"                                                                         : Token;
Operator06Term        : <INITIAL                >"===|==\?|!==|!=\?|==|!="                                                             : Token;
Operator02Term        : <INITIAL                >"&&"                                                                                  : Token;
Operator01Term        : <INITIAL                >"\|\|"                                                                                : Token;
Operator05Term        : <INITIAL                >"&"                                                                                   : Token;
Operator04Term        : <INITIAL                >"\^~|\^|~\^"                                                                          : Token;
Operator03Term        : <INITIAL                >"\|"                                                                                  : Token;
UnaryOperatorTerm     : <INITIAL                >"~&|~\||!|~"                                                                          : Token;
BackQuoteTerm         : <INITIAL, Generic       >"`"                                                                                   : Token;
ColonColonLAngleTerm  : <INITIAL, Generic       >'::<'                                                                                 : Token;
ColonColonTerm        : <INITIAL, Generic       >'::'                                                                                  : Token;
ColonTerm             : <INITIAL, Generic       >':'                                                                                   : Token;
CommaTerm             : <INITIAL, Generic       >','                                                                                   : Token;
DotDotEquTerm         : <INITIAL, Generic       >'..='                                                                                 : Token;
DotDotTerm            : <INITIAL, Generic       >'..'                                                                                  : Token;
DotTerm               : <INITIAL, Generic       >'.'                                                                                   : Token;
EquTerm               : <INITIAL, Generic       >'='                                                                                   : Token;
HashTerm              : <INITIAL, Generic       >'#'                                                                                   : Token;
LAngleTerm            : <INITIAL, Generic       >'<'                                                                                   : Token;
QuoteLBraceTerm       : <INITIAL, Generic       >"'\{"                                                                                 : Token;
LBraceTerm            : <INITIAL, Generic, Embed>'{'                                                                                   : Token;
LBracketTerm          : <INITIAL, Generic       >'['                                                                                   : Token;
LParenTerm            : <INITIAL, Generic       >'('                                                                                   : Token;
RAngleTerm            : <INITIAL, Generic       >'>'                                                                                   : Token;
RBraceTerm            : <INITIAL, Generic, Embed>'}'                                                                                   : Token;
RBracketTerm          : <INITIAL, Generic       >']'                                                                                   : Token;
RParenTerm            : <INITIAL, Generic       >')'                                                                                   : Token;
SemicolonTerm         : <INITIAL, Generic       >';'                                                                                   : Token;
StarTerm              : <INITIAL, Generic       >'*'                                                                                   : Token;
AlwaysCombTerm        : <INITIAL, Generic       >/(?-u:\b)always_comb(?-u:\b)/                                                         : Token;
AlwaysFfTerm          : <INITIAL, Generic       >/(?-u:\b)always_ff(?-u:\b)/                                                           : Token;
AssignTerm            : <INITIAL, Generic       >/(?-u:\b)assign(?-u:\b)/                                                              : Token;
AsTerm                : <INITIAL, Generic       >/(?-u:\b)as(?-u:\b)/                                                                  : Token;
BitTerm               : <INITIAL, Generic       >/(?-u:\b)bit(?-u:\b)/                                                                 : Token;
CaseTerm              : <INITIAL, Generic       >/(?-u:\b)case(?-u:\b)/                                                                : Token;
ClockTerm             : <INITIAL, Generic       >/(?-u:\b)clock(?-u:\b)/                                                               : Token;
ClockPosedgeTerm      : <INITIAL, Generic       >/(?-u:\b)clock_posedge(?-u:\b)/                                                       : Token;
ClockNegedgeTerm      : <INITIAL, Generic       >/(?-u:\b)clock_negedge(?-u:\b)/                                                       : Token;
ConstTerm             : <INITIAL, Generic       >/(?-u:\b)const(?-u:\b)/                                                               : Token;
DefaultTerm           : <INITIAL, Generic       >/(?-u:\b)default(?-u:\b)/                                                             : Token;
ElseTerm              : <INITIAL, Generic       >/(?-u:\b)else(?-u:\b)/                                                                : Token;
EmbedTerm             : <INITIAL, Generic       >/(?-u:\b)embed(?-u:\b)/                                                               : Token;
EnumTerm              : <INITIAL, Generic       >/(?-u:\b)enum(?-u:\b)/                                                                : Token;
ExportTerm            : <INITIAL, Generic       >/(?-u:\b)export(?-u:\b)/                                                              : Token;
F32Term               : <INITIAL, Generic       >/(?-u:\b)f32(?-u:\b)/                                                                 : Token;
F64Term               : <INITIAL, Generic       >/(?-u:\b)f64(?-u:\b)/                                                                 : Token;
FinalTerm             : <INITIAL, Generic       >/(?-u:\b)final(?-u:\b)/                                                               : Token;
ForTerm               : <INITIAL, Generic       >/(?-u:\b)for(?-u:\b)/                                                                 : Token;
FunctionTerm          : <INITIAL, Generic       >/(?-u:\b)function(?-u:\b)/                                                            : Token;
I32Term               : <INITIAL, Generic       >/(?-u:\b)i32(?-u:\b)/                                                                 : Token;
I64Term               : <INITIAL, Generic       >/(?-u:\b)i64(?-u:\b)/                                                                 : Token;
IfResetTerm           : <INITIAL, Generic       >/(?-u:\b)if_reset(?-u:\b)/                                                            : Token;
IfTerm                : <INITIAL, Generic       >/(?-u:\b)if(?-u:\b)/                                                                  : Token;
ImportTerm            : <INITIAL, Generic       >/(?-u:\b)import(?-u:\b)/                                                              : Token;
IncludeTerm           : <INITIAL, Generic       >/(?-u:\b)include(?-u:\b)/                                                             : Token;
InitialTerm           : <INITIAL, Generic       >/(?-u:\b)initial(?-u:\b)/                                                             : Token;
InoutTerm             : <INITIAL, Generic       >/(?-u:\b)inout(?-u:\b)/                                                               : Token;
InputTerm             : <INITIAL, Generic       >/(?-u:\b)input(?-u:\b)/                                                               : Token;
InsideTerm            : <INITIAL, Generic       >/(?-u:\b)inside(?-u:\b)/                                                              : Token;
InstTerm              : <INITIAL, Generic       >/(?-u:\b)inst(?-u:\b)/                                                                : Token;
InterfaceTerm         : <INITIAL, Generic       >/(?-u:\b)interface(?-u:\b)/                                                           : Token;
InTerm                : <INITIAL, Generic       >/(?-u:\b)in(?-u:\b)/                                                                  : Token;
LetTerm               : <INITIAL, Generic       >/(?-u:\b)let(?-u:\b)/                                                                 : Token;
LogicTerm             : <INITIAL, Generic       >/(?-u:\b)logic(?-u:\b)/                                                               : Token;
LsbTerm               : <INITIAL, Generic       >/(?-u:\b)lsb(?-u:\b)/                                                                 : Token;
ModportTerm           : <INITIAL, Generic       >/(?-u:\b)modport(?-u:\b)/                                                             : Token;
ModuleTerm            : <INITIAL, Generic       >/(?-u:\b)module(?-u:\b)/                                                              : Token;
MsbTerm               : <INITIAL, Generic       >/(?-u:\b)msb(?-u:\b)/                                                                 : Token;
OutputTerm            : <INITIAL, Generic       >/(?-u:\b)output(?-u:\b)/                                                              : Token;
OutsideTerm           : <INITIAL, Generic       >/(?-u:\b)outside(?-u:\b)/                                                             : Token;
PackageTerm           : <INITIAL, Generic       >/(?-u:\b)package(?-u:\b)/                                                             : Token;
ParamTerm             : <INITIAL, Generic       >/(?-u:\b)param(?-u:\b)/                                                               : Token;
ProtoTerm             : <INITIAL, Generic       >/(?-u:\b)proto(?-u:\b)/                                                               : Token;
PubTerm               : <INITIAL, Generic       >/(?-u:\b)pub(?-u:\b)/                                                                 : Token;
RefTerm               : <INITIAL, Generic       >/(?-u:\b)ref(?-u:\b)/                                                                 : Token;
RepeatTerm            : <INITIAL, Generic       >/(?-u:\b)repeat(?-u:\b)/                                                              : Token;
ResetTerm             : <INITIAL, Generic       >/(?-u:\b)reset(?-u:\b)/                                                               : Token;
ResetAsyncHighTerm    : <INITIAL, Generic       >/(?-u:\b)reset_async_high(?-u:\b)/                                                    : Token;
ResetAsyncLowTerm     : <INITIAL, Generic       >/(?-u:\b)reset_async_low(?-u:\b)/                                                     : Token;
ResetSyncHighTerm     : <INITIAL, Generic       >/(?-u:\b)reset_sync_high(?-u:\b)/                                                     : Token;
ResetSyncLowTerm      : <INITIAL, Generic       >/(?-u:\b)reset_sync_low(?-u:\b)/                                                      : Token;
ReturnTerm            : <INITIAL, Generic       >/(?-u:\b)return(?-u:\b)/                                                              : Token;
BreakTerm             : <INITIAL, Generic       >/(?-u:\b)break(?-u:\b)/                                                               : Token;
SignedTerm            : <INITIAL, Generic       >/(?-u:\b)signed(?-u:\b)/                                                              : Token;
StepTerm              : <INITIAL, Generic       >/(?-u:\b)step(?-u:\b)/                                                                : Token;
StringTerm            : <INITIAL, Generic       >/(?-u:\b)string(?-u:\b)/                                                              : Token;
StructTerm            : <INITIAL, Generic       >/(?-u:\b)struct(?-u:\b)/                                                              : Token;
SwitchTerm            : <INITIAL, Generic       >/(?-u:\b)switch(?-u:\b)/                                                              : Token;
TriTerm               : <INITIAL, Generic       >/(?-u:\b)tri(?-u:\b)/                                                                 : Token;
TypeTerm              : <INITIAL, Generic       >/(?-u:\b)type(?-u:\b)/                                                                : Token;
U32Term               : <INITIAL, Generic       >/(?-u:\b)u32(?-u:\b)/                                                                 : Token;
U64Term               : <INITIAL, Generic       >/(?-u:\b)u64(?-u:\b)/                                                                 : Token;
UnionTerm             : <INITIAL, Generic       >/(?-u:\b)union(?-u:\b)/                                                               : Token;
UnsafeTerm            : <INITIAL, Generic       >/(?-u:\b)unsafe(?-u:\b)/                                                              : Token;
VarTerm               : <INITIAL, Generic       >/(?-u:\b)var(?-u:\b)/                                                                 : Token;
DollarIdentifierTerm  : <INITIAL, Generic       >/\$[a-zA-Z_][0-9a-zA-Z_$]*/                                                           : Token;
IdentifierTerm        : <INITIAL, Generic       >/(?:r#)?[a-zA-Z_][0-9a-zA-Z_$]*/                                                      : Token;
AnyTerm               : <                  Embed>/[^{}]*/                                                                              : Token;

// ----------------------------------------------------------------------------
// Token
// ----------------------------------------------------------------------------

Comments: [ CommentsTerm ];

StartToken: Comments;

StringLiteralToken: StringLiteralTerm: Token Comments;

ExponentToken  : ExponentTerm  : Token Comments;
FixedPointToken: FixedPointTerm: Token Comments;
BasedToken     : BasedTerm     : Token Comments;
BaseLessToken  : BaseLessTerm  : Token Comments;
AllBitToken    : AllBitTerm    : Token Comments;

AssignmentOperatorToken: AssignmentOperatorTerm: Token Comments;
Operator01Token        : Operator01Term        : Token Comments;
Operator02Token        : Operator02Term        : Token Comments;
Operator03Token        : Operator03Term        : Token Comments;
Operator04Token        : Operator04Term        : Token Comments;
Operator05Token        : Operator05Term        : Token Comments;
Operator06Token        : Operator06Term        : Token Comments;
Operator07Token        : Operator07Term        : Token Comments;
Operator08Token        : Operator08Term        : Token Comments;
Operator09Token        : Operator09Term        : Token Comments;
Operator10Token        : Operator10Term        : Token Comments;
Operator11Token        : Operator11Term        : Token Comments;
UnaryOperatorToken     : UnaryOperatorTerm     : Token Comments;

BackQuoteToken       : BackQuoteTerm       : Token Comments;
ColonToken           : ColonTerm           : Token Comments;
ColonColonLAngleToken: ColonColonLAngleTerm: Token Comments;
ColonColonToken      : ColonColonTerm      : Token Comments;
CommaToken           : CommaTerm           : Token Comments;
DotDotToken          : DotDotTerm          : Token Comments;
DotDotEquToken       : DotDotEquTerm       : Token Comments;
DotToken             : DotTerm             : Token Comments;
EquToken             : EquTerm             : Token Comments;
HashToken            : HashTerm            : Token Comments;
QuoteLBraceToken     : QuoteLBraceTerm     : Token Comments;
LAngleToken          : LAngleTerm          : Token Comments;
LBraceToken          : LBraceTerm          : Token Comments;
LBracketToken        : LBracketTerm        : Token Comments;
LParenToken          : LParenTerm          : Token Comments;
MinusColonToken      : MinusColonTerm      : Token Comments;
MinusGTToken         : MinusGTTerm         : Token Comments;
PlusColonToken       : PlusColonTerm       : Token Comments;
RAngleToken          : RAngleTerm          : Token Comments;
RBraceToken          : RBraceTerm          : Token Comments;
RBracketToken        : RBracketTerm        : Token Comments;
RParenToken          : RParenTerm          : Token Comments;
SemicolonToken       : SemicolonTerm       : Token Comments;
StarToken            : StarTerm            : Token Comments;

AlwaysCombToken    : AlwaysCombTerm    : Token Comments;
AlwaysFfToken      : AlwaysFfTerm      : Token Comments;
AsToken            : AsTerm            : Token Comments;
AssignToken        : AssignTerm        : Token Comments;
BitToken           : BitTerm           : Token Comments;
CaseToken          : CaseTerm          : Token Comments;
ClockToken         : ClockTerm         : Token Comments;
ClockPosedgeToken  : ClockPosedgeTerm  : Token Comments;
ClockNegedgeToken  : ClockNegedgeTerm  : Token Comments;
ConstToken         : ConstTerm         : Token Comments;
DefaultToken       : DefaultTerm       : Token Comments;
ElseToken          : ElseTerm          : Token Comments;
EmbedToken         : EmbedTerm         : Token Comments;
EnumToken          : EnumTerm          : Token Comments;
ExportToken        : ExportTerm        : Token Comments;
F32Token           : F32Term           : Token Comments;
F64Token           : F64Term           : Token Comments;
FinalToken         : FinalTerm         : Token Comments;
ForToken           : ForTerm           : Token Comments;
FunctionToken      : FunctionTerm      : Token Comments;
I32Token           : I32Term           : Token Comments;
I64Token           : I64Term           : Token Comments;
IfResetToken       : IfResetTerm       : Token Comments;
IfToken            : IfTerm            : Token Comments;
ImportToken        : ImportTerm        : Token Comments;
IncludeToken       : IncludeTerm       : Token Comments;
InitialToken       : InitialTerm       : Token Comments;
InoutToken         : InoutTerm         : Token Comments;
InputToken         : InputTerm         : Token Comments;
InsideToken        : InsideTerm        : Token Comments;
InstToken          : InstTerm          : Token Comments;
InterfaceToken     : InterfaceTerm     : Token Comments;
InToken            : InTerm            : Token Comments;
LetToken           : LetTerm           : Token Comments;
LogicToken         : LogicTerm         : Token Comments;
LsbToken           : LsbTerm           : Token Comments;
ModportToken       : ModportTerm       : Token Comments;
ModuleToken        : ModuleTerm        : Token Comments;
MsbToken           : MsbTerm           : Token Comments;
OutputToken        : OutputTerm        : Token Comments;
OutsideToken       : OutsideTerm       : Token Comments;
PackageToken       : PackageTerm       : Token Comments;
ParamToken         : ParamTerm         : Token Comments;
ProtoToken         : ProtoTerm         : Token Comments;
PubToken           : PubTerm           : Token Comments;
RefToken           : RefTerm           : Token Comments;
RepeatToken        : RepeatTerm        : Token Comments;
ResetToken         : ResetTerm         : Token Comments;
ResetAsyncHighToken: ResetAsyncHighTerm: Token Comments;
ResetAsyncLowToken : ResetAsyncLowTerm : Token Comments;
ResetSyncHighToken : ResetSyncHighTerm : Token Comments;
ResetSyncLowToken  : ResetSyncLowTerm  : Token Comments;
ReturnToken        : ReturnTerm        : Token Comments;
BreakToken         : BreakTerm         : Token Comments;
SignedToken        : SignedTerm        : Token Comments;
StepToken          : StepTerm          : Token Comments;
StringToken        : StringTerm        : Token Comments;
StructToken        : StructTerm        : Token Comments;
SwitchToken        : SwitchTerm        : Token Comments;
TriToken           : TriTerm           : Token Comments;
TypeToken          : TypeTerm          : Token Comments;
U32Token           : U32Term           : Token Comments;
U64Token           : U64Term           : Token Comments;
UnionToken         : UnionTerm         : Token Comments;
UnsafeToken        : UnsafeTerm        : Token Comments;
VarToken           : VarTerm           : Token Comments;

DollarIdentifierToken: DollarIdentifierTerm: Token Comments;
IdentifierToken      : IdentifierTerm      : Token Comments;

// ----------------------------------------------------------------------------
// VerylToken
// ----------------------------------------------------------------------------

// Start
Start: StartToken: VerylToken;

// StringLiteral
StringLiteral: StringLiteralToken: VerylToken;

// Number
Exponent  : ExponentToken  : VerylToken;
FixedPoint: FixedPointToken: VerylToken;
Based     : BasedToken     : VerylToken;
BaseLess  : BaseLessToken  : VerylToken;
AllBit    : AllBitToken    : VerylToken;

// Operator
AssignmentOperator: AssignmentOperatorToken: VerylToken;
Operator01        : Operator01Token        : VerylToken;
Operator02        : Operator02Token        : VerylToken;
Operator03        : Operator03Token        : VerylToken;
Operator04        : Operator04Token        : VerylToken;
Operator05        : Operator05Token        : VerylToken;
Operator06        : Operator06Token        : VerylToken;
Operator07        : Operator07Token        : VerylToken;
Operator08        : Operator08Token        : VerylToken;
Operator09        : Operator09Token        : VerylToken;
Operator10        : Operator10Token        : VerylToken;
Operator11        : Operator11Token        : VerylToken;
UnaryOperator     : UnaryOperatorToken     : VerylToken;

// Symbol
BackQuote       : BackQuoteToken       : VerylToken;
Colon           : ColonToken           : VerylToken;
ColonColonLAngle: ColonColonLAngleToken: VerylToken;
ColonColon      : ColonColonToken      : VerylToken;
Comma           : CommaToken           : VerylToken;
DotDot          : DotDotToken          : VerylToken;
DotDotEqu       : DotDotEquToken       : VerylToken;
Dot             : DotToken             : VerylToken;
Equ             : EquToken             : VerylToken;
Hash            : HashToken            : VerylToken;
QuoteLBrace     : QuoteLBraceToken     : VerylToken;
LAngle          : LAngleToken          : VerylToken;
LBrace          : LBraceToken          : VerylToken;
LBracket        : LBracketToken        : VerylToken;
LParen          : LParenToken          : VerylToken;
MinusColon      : MinusColonToken      : VerylToken;
MinusGT         : MinusGTToken         : VerylToken;
PlusColon       : PlusColonToken       : VerylToken;
RAngle          : RAngleToken          : VerylToken;
RBrace          : RBraceToken          : VerylToken;
RBracket        : RBracketToken        : VerylToken;
RParen          : RParenToken          : VerylToken;
Semicolon       : SemicolonToken       : VerylToken;
Star            : StarToken            : VerylToken;

// Keyword
AlwaysComb    : AlwaysCombToken    : VerylToken;
AlwaysFf      : AlwaysFfToken      : VerylToken;
As            : AsToken            : VerylToken;
Assign        : AssignToken        : VerylToken;
Bit           : BitToken           : VerylToken;
Break         : BreakToken         : VerylToken;
Case          : CaseToken          : VerylToken;
Clock         : ClockToken         : VerylToken;
ClockPosedge  : ClockPosedgeToken  : VerylToken;
ClockNegedge  : ClockNegedgeToken  : VerylToken;
Const         : ConstToken         : VerylToken;
Defaul        : DefaultToken       : VerylToken; // avoid to conflict with Rust's Default trait
Else          : ElseToken          : VerylToken;
Embed         : EmbedToken         : VerylToken;
Enum          : EnumToken          : VerylToken;
Export        : ExportToken        : VerylToken;
F32           : F32Token           : VerylToken;
F64           : F64Token           : VerylToken;
Final         : FinalToken         : VerylToken;
For           : ForToken           : VerylToken;
Function      : FunctionToken      : VerylToken;
I32           : I32Token           : VerylToken;
I64           : I64Token           : VerylToken;
If            : IfToken            : VerylToken;
IfReset       : IfResetToken       : VerylToken;
Import        : ImportToken        : VerylToken;
In            : InToken            : VerylToken;
Include       : IncludeToken       : VerylToken;
Initial       : InitialToken       : VerylToken;
Inout         : InoutToken         : VerylToken;
Input         : InputToken         : VerylToken;
Inside        : InsideToken        : VerylToken;
Inst          : InstToken          : VerylToken;
Interface     : InterfaceToken     : VerylToken;
Let           : LetToken           : VerylToken;
Logic         : LogicToken         : VerylToken;
Lsb           : LsbToken           : VerylToken;
Modport       : ModportToken       : VerylToken;
Module        : ModuleToken        : VerylToken;
Msb           : MsbToken           : VerylToken;
Output        : OutputToken        : VerylToken;
Outside       : OutsideToken       : VerylToken;
Package       : PackageToken       : VerylToken;
Param         : ParamToken         : VerylToken;
Proto         : ProtoToken         : VerylToken;
Pub           : PubToken           : VerylToken;
Ref           : RefToken           : VerylToken;
Repeat        : RepeatToken        : VerylToken;
Reset         : ResetToken         : VerylToken;
ResetAsyncHigh: ResetAsyncHighToken: VerylToken;
ResetAsyncLow : ResetAsyncLowToken : VerylToken;
ResetSyncHigh : ResetSyncHighToken : VerylToken;
ResetSyncLow  : ResetSyncLowToken  : VerylToken;
Return        : ReturnToken        : VerylToken;
Signed        : SignedToken        : VerylToken;
Step          : StepToken          : VerylToken;
Strin         : StringToken        : VerylToken; // avoid to conflict with Rust's String struct
Struct        : StructToken        : VerylToken;
Switch        : SwitchToken        : VerylToken;
Tri           : TriToken           : VerylToken;
Type          : TypeToken          : VerylToken;
U32           : U32Token           : VerylToken;
U64           : U64Token           : VerylToken;
Union         : UnionToken         : VerylToken;
Unsafe        : UnsafeToken        : VerylToken;
Var           : VarToken           : VerylToken;

// Identifier
DollarIdentifier: DollarIdentifierToken: VerylToken;
Identifier      : IdentifierToken      : VerylToken;

// ----------------------------------------------------------------------------
// Number
// ----------------------------------------------------------------------------

Number: IntegralNumber
      | RealNumber
      ;

IntegralNumber: Based
              | BaseLess
              | AllBit
              ;

RealNumber: FixedPoint
          | Exponent
          ;

// ----------------------------------------------------------------------------
// Complex Identifier
// ----------------------------------------------------------------------------

HierarchicalIdentifier: Identifier { Select } { Dot Identifier { Select } };
ScopedIdentifier      : ( DollarIdentifier | Identifier [ WithGenericArgument ] ) { ColonColon Identifier [ WithGenericArgument ] };
ExpressionIdentifier  : ScopedIdentifier [ Width ] { Select } { Dot Identifier { Select } };

// ----------------------------------------------------------------------------
// Expression
// ----------------------------------------------------------------------------

Expression  : Expression01 { Operator01 Expression01 };
Expression01: Expression02 { Operator02 Expression02 };
Expression02: Expression03 { Operator03 Expression03 };
Expression03: Expression04 { Operator04 Expression04 };
Expression04: Expression05 { Operator05 Expression05 };
Expression05: Expression06 { Operator06 Expression06 };
Expression06: Expression07 { Operator07 Expression07 };
Expression07: Expression08 { Operator08 Expression08 };
Expression08: Expression09 { Operator09 Expression09 };
Expression09: Expression10 { ( Operator10 | Star ) Expression10 };
Expression10: Expression11 { Operator11 Expression11 };
Expression11: Expression12 [ As CastingType ];
Expression12: { ( UnaryOperator | Operator09 | Operator05 | Operator03 | Operator04 ) } Factor;

Factor: Number
      | ExpressionIdentifier [ FunctionCall ]
      | LParen Expression RParen
      | LBrace ConcatenationList RBrace
      | QuoteLBrace ArrayLiteralList RBrace
      | IfExpression
      | CaseExpression
      | SwitchExpression
      | StringLiteral
      | ( Msb | Lsb )
      | InsideExpression
      | OutsideExpression
      | TypeExpression
      | FactorType
      ;

FunctionCall: LParen [ ArgumentList ] RParen;

ArgumentList: ArgumentItem { Comma ArgumentItem } [ Comma ];

ArgumentItem: Expression;

ConcatenationList: ConcatenationItem { Comma ConcatenationItem } [ Comma ];

ConcatenationItem: Expression [ Repeat Expression ];

ArrayLiteralList: ArrayLiteralItem { Comma ArrayLiteralItem } [ Comma ];

ArrayLiteralItem: ( Expression [ Repeat Expression ] | Defaul Colon Expression );

IfExpression: If Expression LBrace Expression RBrace { Else If Expression LBrace Expression RBrace } Else LBrace Expression RBrace;

CaseExpression: Case Expression LBrace CaseCondition Colon Expression Comma { CaseCondition Colon Expression Comma } Defaul Colon Expression [ Comma ] RBrace;

SwitchExpression: Switch LBrace SwitchCondition Colon Expression Comma { SwitchCondition Colon Expression Comma } Defaul Colon Expression [ Comma ] RBrace;

TypeExpression: Type LParen Expression RParen;

InsideExpression: Inside Expression LBrace RangeList RBrace;

OutsideExpression: Outside Expression LBrace RangeList RBrace;

RangeList: RangeItem { Comma RangeItem } [ Comma ];

RangeItem: Range;

// ----------------------------------------------------------------------------
// Select / Width / Array / Range
// ----------------------------------------------------------------------------

Select: LBracket Expression [ SelectOperator Expression ] RBracket;

SelectOperator: Colon
              | PlusColon
              | MinusColon
              | Step
              ;

Width: LAngle Expression { Comma Expression } RAngle;

Array: LBracket Expression { Comma Expression } RBracket;

Range: Expression [ RangeOperator Expression ];

RangeOperator: DotDot
             | DotDotEqu
             ;

// ----------------------------------------------------------------------------
// ScalarType / ArrayType / CastingType
// ----------------------------------------------------------------------------

FixedType: U32 | U64 | I32 | I64 | F32 | F64 | Strin;

VariableType: Clock
            | ClockPosedge
            | ClockNegedge
            | Reset
            | ResetAsyncHigh
            | ResetAsyncLow
            | ResetSyncHigh
            | ResetSyncLow
            | Logic
            | Bit;

UserDefinedType: ScopedIdentifier;

TypeModifier: Tri | Signed;

FactorType: ( VariableType [ Width ] | FixedType );

ScalarType: { TypeModifier } ( UserDefinedType [ Width ] | FactorType );

ArrayType: ScalarType [ Array ];

CastingType: U32
           | U64
           | I32
           | I64
           | F32
           | F64
           | Clock
           | ClockPosedge
           | ClockNegedge
           | Reset
           | ResetAsyncHigh
           | ResetAsyncLow
           | ResetSyncHigh
           | ResetSyncLow
           | UserDefinedType
           | Based
           | BaseLess
           ;

// ----------------------------------------------------------------------------
// ClockDomain
// ----------------------------------------------------------------------------

ClockDomain: BackQuote Identifier;

// ----------------------------------------------------------------------------
// Statement
// ----------------------------------------------------------------------------

StatementBlock: LBrace { StatementBlockGroup } RBrace;

StatementBlockGroup: { Attribute } ( LBrace { StatementBlockGroup } RBrace | StatementBlockItem );

StatementBlockItem: VarDeclaration | LetStatement | Statement;

Statement: IdentifierStatement
         | IfStatement
         | IfResetStatement
         | ReturnStatement
         | BreakStatement
         | ForStatement
         | CaseStatement
         | SwitchStatement
         ;

LetStatement: Let Identifier Colon [ ClockDomain ] ArrayType Equ Expression Semicolon;

IdentifierStatement: ExpressionIdentifier ( FunctionCall | Assignment ) Semicolon;

Assignment: ( Equ | AssignmentOperator ) Expression;

IfStatement: If Expression StatementBlock { Else If Expression StatementBlock } [ Else StatementBlock ];

IfResetStatement: IfReset StatementBlock { Else If Expression StatementBlock } [ Else StatementBlock ];

ReturnStatement: Return Expression Semicolon;

BreakStatement: Break Semicolon;

ForStatement: For Identifier Colon ScalarType In Range [ Step AssignmentOperator Expression ] StatementBlock;

CaseStatement: Case Expression LBrace { CaseItem } RBrace;

CaseItem: ( CaseCondition | Defaul ) Colon ( Statement | StatementBlock );

CaseCondition: RangeItem { Comma RangeItem } ;

SwitchStatement: Switch LBrace { SwitchItem } RBrace;

SwitchItem: ( SwitchCondition | Defaul ) Colon ( Statement | StatementBlock );

SwitchCondition: Expression { Comma Expression } ;

// ----------------------------------------------------------------------------
// Attribute
// ----------------------------------------------------------------------------

Attribute: Hash LBracket Identifier [ LParen AttributeList RParen ] RBracket;

AttributeList: AttributeItem { Comma AttributeItem } [ Comma ];

AttributeItem: Identifier
             | StringLiteral
             ;

// ----------------------------------------------------------------------------
// Declaration
// ----------------------------------------------------------------------------

LetDeclaration: Let Identifier Colon [ ClockDomain ] ArrayType Equ Expression Semicolon;

VarDeclaration: Var Identifier Colon [ ClockDomain ] ArrayType Semicolon;

ConstDeclaration: Const Identifier Colon ( ArrayType | Type ) Equ Expression Semicolon;

TypeDefDeclaration: Type Identifier Equ ArrayType Semicolon;

AlwaysFfDeclaration: AlwaysFf [ AlwayfFfEventList ] StatementBlock;

AlwayfFfEventList: LParen AlwaysFfClock [ Comma AlwaysFfReset ] RParen;

AlwaysFfClock: HierarchicalIdentifier;

AlwaysFfReset: HierarchicalIdentifier;

AlwaysCombDeclaration: AlwaysComb StatementBlock;

AssignDeclaration: Assign HierarchicalIdentifier Equ Expression Semicolon;

ModportDeclaration: Modport Identifier LBrace ModportList RBrace;

ModportList: ModportGroup { Comma ModportGroup } [ Comma ];

ModportGroup: { Attribute } ( LBrace ModportList RBrace | ModportItem );

ModportItem: Identifier Colon Direction;

EnumDeclaration: Enum Identifier [ Colon ScalarType ] LBrace EnumList RBrace;

EnumList: EnumGroup { Comma EnumGroup } [ Comma ];

EnumGroup: { Attribute } ( LBrace EnumList RBrace | EnumItem );

EnumItem: Identifier [ Equ Expression ];

StructUnion: Struct | Union;

StructUnionDeclaration: StructUnion Identifier [ WithGenericParameter ] LBrace StructUnionList RBrace;

StructUnionList: StructUnionGroup { Comma StructUnionGroup } [ Comma ];

StructUnionGroup: { Attribute } ( LBrace StructUnionList RBrace | StructUnionItem );

StructUnionItem: Identifier Colon ScalarType;

InitialDeclaration: Initial StatementBlock;

FinalDeclaration: Final StatementBlock;

// ----------------------------------------------------------------------------
// InstDeclaration
// ----------------------------------------------------------------------------

InstDeclaration: Inst Identifier Colon ScopedIdentifier [ Array ] [ InstParameter ] [ LParen [ InstPortList ] RParen ] Semicolon;

InstParameter: Hash LParen [ InstParameterList ] RParen;

InstParameterList: InstParameterGroup { Comma InstParameterGroup } [ Comma ];

InstParameterGroup: { Attribute } ( LBrace InstParameterList RBrace | InstParameterItem );

InstParameterItem: Identifier [ Colon Expression ];

InstPortList: InstPortGroup { Comma InstPortGroup } [ Comma ];

InstPortGroup: { Attribute } ( LBrace InstPortList RBrace | InstPortItem );

InstPortItem: Identifier [ Colon Expression ];

// ----------------------------------------------------------------------------
// WithParameter
// ----------------------------------------------------------------------------

WithParameter: Hash LParen [ WithParameterList ] RParen;

WithParameterList: WithParameterGroup { Comma WithParameterGroup } [ Comma ];

WithParameterGroup: { Attribute } ( LBrace WithParameterList RBrace | WithParameterItem );

WithParameterItem: ( Param | Const ) Identifier Colon ( ArrayType | Type ) Equ Expression;

// ----------------------------------------------------------------------------
// WithGenericParameter
// ----------------------------------------------------------------------------

GenericBound: Const
            | Type
            | ScopedIdentifier;

WithGenericParameter: ColonColonLAngle WithGenericParameterList RAngle;

WithGenericParameterList: WithGenericParameterItem { Comma WithGenericParameterItem } [ Comma ];

WithGenericParameterItem: Identifier Colon GenericBound [ Equ WithGenericArgumentItem ];

// ----------------------------------------------------------------------------
// WithGenericArgument
// ----------------------------------------------------------------------------

WithGenericArgument: ColonColonLAngle %push(Generic) [ WithGenericArgumentList ] RAngle %pop();

WithGenericArgumentList: WithGenericArgumentItem { Comma WithGenericArgumentItem } [ Comma ];

WithGenericArgumentItem: ScopedIdentifier
                       | Number
                       ;

// ----------------------------------------------------------------------------
// PortDeclaration
// ----------------------------------------------------------------------------

PortDeclaration: LParen [ PortDeclarationList ] RParen;

PortDeclarationList: PortDeclarationGroup { Comma PortDeclarationGroup } [ Comma ];

PortDeclarationGroup: { Attribute } ( LBrace PortDeclarationList RBrace | PortDeclarationItem );

PortDeclarationItem: Identifier Colon ( PortTypeConcrete | PortTypeAbstract );

PortTypeConcrete: Direction [ ClockDomain ] ArrayType;

PortTypeAbstract: [ ClockDomain ] Interface [ ColonColon Identifier ] [ Array ];

Direction: Input
         | Output
         | Inout
         | Ref
         | Modport
         | Import
         ;

// ----------------------------------------------------------------------------
// Function
// ----------------------------------------------------------------------------

FunctionDeclaration: Function Identifier [ WithGenericParameter ] [ PortDeclaration ] [ MinusGT ScalarType ] StatementBlock;

// ----------------------------------------------------------------------------
// Import / Export
// ----------------------------------------------------------------------------

ImportDeclaration: Import ScopedIdentifier [ ColonColon Star ] Semicolon;

ExportDeclaration: Export ( Star | ScopedIdentifier [ ColonColon Star ] ) Semicolon;

// ----------------------------------------------------------------------------
// Unsafe
// ----------------------------------------------------------------------------

UnsafeBlock: Unsafe LParen Identifier RParen LBrace { GenerateGroup } RBrace;

// ----------------------------------------------------------------------------
// Module/Interface
// ----------------------------------------------------------------------------

ModuleDeclaration: [ Pub ] Module Identifier [ WithGenericParameter ] [ For ScopedIdentifier ] [ WithParameter ] [ PortDeclaration ] LBrace { ModuleGroup } RBrace;

ModuleGroup: { Attribute } ( LBrace { ModuleGroup } RBrace | ModuleItem );

ModuleItem: GenerateItem;

InterfaceDeclaration: [ Pub ] Interface Identifier [ WithGenericParameter ] [ WithParameter ] LBrace { InterfaceGroup } RBrace;

InterfaceGroup: { Attribute } ( LBrace { InterfaceGroup } RBrace | InterfaceItem );

InterfaceItem: GenerateItem | ModportDeclaration;

GenerateIfDeclaration: If Expression GenerateNamedBlock { Else If Expression GenerateOptionalNamedBlock } [ Else GenerateOptionalNamedBlock ];

GenerateForDeclaration: For Identifier In Range [ Step AssignmentOperator Expression ] GenerateNamedBlock;

GenerateBlockDeclaration: GenerateNamedBlock;

GenerateNamedBlock: Colon Identifier LBrace { GenerateGroup } RBrace;

GenerateOptionalNamedBlock: [ Colon Identifier ] LBrace { GenerateGroup } RBrace;

GenerateGroup: { Attribute } ( LBrace { GenerateGroup } RBrace | GenerateItem );

GenerateItem: LetDeclaration
            | VarDeclaration
            | InstDeclaration
            | ConstDeclaration
            | AlwaysFfDeclaration
            | AlwaysCombDeclaration
            | AssignDeclaration
            | FunctionDeclaration
            | GenerateIfDeclaration
            | GenerateForDeclaration
            | GenerateBlockDeclaration
            | TypeDefDeclaration
            | EnumDeclaration
            | StructUnionDeclaration
            | ImportDeclaration
            | InitialDeclaration
            | FinalDeclaration
            | UnsafeBlock
            ;

// ----------------------------------------------------------------------------
// Package
// ----------------------------------------------------------------------------

PackageDeclaration: [ Pub ] Package Identifier [ WithGenericParameter ] LBrace { PackageGroup } RBrace;

PackageGroup: { Attribute } ( LBrace { PackageGroup } RBrace | PackageItem );

PackageItem: VarDeclaration
           | ConstDeclaration
           | TypeDefDeclaration
           | EnumDeclaration
           | StructUnionDeclaration
           | FunctionDeclaration
           | ImportDeclaration
           | ExportDeclaration
           ;

// ----------------------------------------------------------------------------
// Proto
// ----------------------------------------------------------------------------

ProtoModuleDeclaration: [ Pub ] Proto Module Identifier [ WithParameter ] [ PortDeclaration ] Semicolon;

// ----------------------------------------------------------------------------
// Embed
// ----------------------------------------------------------------------------

EmbedDeclaration: Embed LParen Identifier RParen Identifier EmbedContent;

EmbedContent: EmbedContentToken: VerylToken;

EmbedContentToken: LBraceTerm %push(Embed) LBraceTerm LBraceTerm { EmbedItem } RBraceTerm RBraceTerm RBraceTerm %pop() Comments;

EmbedItem: LBraceTerm { EmbedItem } RBraceTerm
         | AnyTerm;

// ----------------------------------------------------------------------------
// Include
// ----------------------------------------------------------------------------

IncludeDeclaration: Include LParen Identifier Comma StringLiteral RParen Semicolon;

// ----------------------------------------------------------------------------
// Description
// ----------------------------------------------------------------------------

DescriptionGroup: { Attribute } ( LBrace { DescriptionGroup } RBrace | DescriptionItem );

DescriptionItem: ModuleDeclaration
               | InterfaceDeclaration
               | PackageDeclaration
               | ProtoModuleDeclaration
               | ImportDeclaration
               | EmbedDeclaration
               | IncludeDeclaration
               ;

// ----------------------------------------------------------------------------
// SourceCode
// ----------------------------------------------------------------------------

Veryl: Start { DescriptionGroup };

Semantic Error

duplicated_identifier

invalid_allow

invalid_direction

invalid_identifier

invalid_lsb

invalid_msb

invalid_number_character

invalid_statement

invalid_system_function

mismatch_arity

mismatch_attribute_args

mismatch_type

missing_if_reset

missing_port

missing_reset_signal

missing_reset_statement

too_large_enum_variant

too_large_number

too_much_enum_variant

undefined_identifier

unknown_attribute

unknown_member

unknown_msb

unknown_port

unused_variable