ハードウェア記述言語 Veryl

Veryl

Veryl は SystemVerilog をベースに設計されたハードウェア記述言語であり、以下のような特徴があります。

最適化された構文

Verylは、SystemVerilogの経験者にとって親しみやすい基本構文に基づきながら、論理設計に最適化された構文を採用しています。この最適化には、たとえば合成可能性の保証やシミュレーション結果の一致の保証、頻出する定型文を簡素化する多数の構文などの提供が含まれます。このアプローチにより、学習の容易さ、設計プロセスの信頼性と効率の向上、およびコードの記述の容易さが実現されます。

相互運用性

VerylはSystemVerilogとの相互運用性を考慮して設計されており、既存のSystemVerilogコンポーネントやプロジェクトとの組み合わせや部分的な置き換えをスムーズに行うことができます。さらに、VerylからトランスパイルされたSystemVerilogソースコードは、その高い可読性により、シームレスな統合やデバッグを可能にします。

生産性

Verylはパッケージマネージャ、ビルドツール、そしてVSCode、Vim、Emacsなどの主要なエディタに対応するリアルタイムチェッカー、自動補完機能、自動フォーマッタなど、豊富な開発支援ツールを備えています。これらのツールは、開発プロセスを加速し、生産性を大幅に向上させることができます。

これらの特性により、Verylは設計者が高品質なハードウェア設計をより効率的かつ生産的に行うための強力なサポートを提供します。

特徴

この章ではVerylの特徴的な機能をわかりやすい例とともに紹介します。

リアルタイム診断

変数の未定義・未使用・未代入といった問題はエディタでの編集中にリアルタイムに通知されます。次の例では、未使用変数として通知された変数に _ プレフィックスを付加することで未使用であることを明示し、警告を抑制しています。

diagnostics

ビデオが再生されない場合1

自動フォーマット

エディタと連携した自動フォーマット機能のほか、コマンドラインでのフォーマットやCIでのフォーマットチェックも可能です。

format

ビデオが再生されない場合1

組み込みテスト

SystemVerilogまたはcocotbで書かれたテストコードをVerylに埋め込み、veryl test コマンドで実行することができます。

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

依存関係管理

Verylには依存関係の管理機能が組み込まれており、プロジェクト設定に以下のようにライブラリのリポジトリパスとバージョンを追加するだけで、簡単にライブラリを組み込むことができます。

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

ジェネリクス

ジェネリクスによるコード生成は従来のパラメータオーバーライドよりさらに再利用性の高いコードを記述することができます。以下の例のような関数のパラメータだけでなく、インスタンスのモジュール名や構造体定義の型名もパラメータ化することができます。

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);
}

クロックドメインアノテーション

モジュール内に複数のクロックがある場合、明示的なクロックドメインアノテーションとクロックドメイン境界への unsafe (cdc) ブロックが必要です。Veryl コンパイラは意図しないクロックドメインクロッシングをエラーとして検出し、明示的な unsafe (cdc) ブロックによりレビューが容易になります。

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
);
    // 注意!!!
    // i_clk_a から 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;
    }
}

末尾カンマ

末尾カンマは、リストの最後の要素の後ろにカンマが置かれる構文です。これにより、要素の追加や削除が容易になり、バージョン管理システムにおける不必要な差異を減らすことができます。

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

クロックとリセットの抽象化

クロックの極性やリセットの極性と同期性を構文上指定する必要はなく、ビルド時の設定で指定することができます。これにより同じVerylのコードからASIC向けの負極性・非同期リセットとFPGA向けの正極性・同期リセットのそれぞれのコードを生成することができます。

さらに、明示的な clockreset 型により、レジスタへのクロック・リセット接続が正しく行われているかどうかを確認することができます。モジュール内にクロックとリセットが1つだけの場合、レジスタへの接続を省略することもできます。

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 {
        }
    }
}

ドキュメンテーションコメント

ドキュメンテーションコメントとしてモジュールの説明を書いておくとドキュメントを自動生成することができます。単なるテキストだけでなく、以下のフォーマットを使用することができます。

SystemVerilog Veryl
// コメント
module ModuleA;
endmodule
/// マークダウン形式のドキュメンテーションコメント
///
/// * リスト
/// * リスト
/// 
/// ```wavedrom
/// { signal: [{ name: "Alfa", wave: "01.zx=ud.23.456789" }] }
/// ```
module ModuleA {
}

always_ff での複合代入演算子

ノンブロッキング専用の代入演算子はなく、always_ff 内ではノンブロッキング代入が、 always_comb 内ではブロッキング代入が推論されます。そのため always_ff 内でも always_comb 内と同様に様々な複合代入演算子を使用することができます。

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

独立した名前空間を持つenumバリアント

enumのバリアントはenum毎に独立した名前空間を持っており意図しない名前の衝突を防ぐことができます。

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

ビット連結における繰り返し記述として明示的な repeat 記法を採用し、 複雑な {} の組み合わせより可読性が向上しています。

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

三項演算子の代わりに if 式と case 式を採用することで、比較するアイテム数が多い場合の可読性が向上します。

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,
};

範囲 for / inside / outside

閉区間 ..= と半開区間 .. を表す記法を導入し、 forinside で範囲を統一的に記述できるようにしました。また、inside の逆を意味する outside も導入しました。

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 記法

最上位ビットを示す msb 記法により、パラメータから最上位ビットを計算する必要がなくなり、より意図を明確にすることができます。

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

変数宣言と同時に値を束縛する専用の let 文が用意されており、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;
}

名前付きブロック

変数のスコープを限定するための名前付きブロックを定義することができます。

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

可視性制御

pub キーワードの付かないモジュールはプロジェクト外から参照できず、ドキュメントの自動生成にも含まれません。これによりプロジェクト外に公開したいものと内部実装とを区別することができます。

SystemVerilog Veryl
module ModuleA;
endmodule

module ModuleB;
endmodule
pub module ModuleA {
}

module ModuleB {
}
1

いくつかのブラウザはデフォルトでGIF動画の再生を停止しています。ブラウザの設定を確認してください。

はじめに

Veryl を使ってみましょう。この章では Veryl のインストール、サンプルプロジェクトの作成とビルドまでを行います。

インストール

Veryl は公式のツールチェーンインストーラ verylup を使ってインストールできます。ツールチェーンのアップデートなど便利な機能があるので verylup の使用を推奨します。

注: インターネットアクセスのない環境にインストールしたい場合は オフラインインストール が利用できます。

要件

Veryl は git コマンドを内部で使用します。 git が起動できることを確認しておいてください。

verylup のインストール

バイナリのダウンロード

リリースページからダウンロードして、パスの通ったところに展開してください。

Cargo

cargo コマンドからインストールすることもできます。

cargo install verylup

verylup のセットアップ

verylup をインストールした後、以下のコマンドを1回実行してください。最新のツールチェーンをダウンロードし、verylveryl-ls コマンドをverylupと同じ場所に作成します。

verylup setup

これで veryl コマンドが使えるようになりました。

エディタ設定

公式には Visual Studio CodeVim / Neovim がサポートされています。

Visual Studio Code

Visual Studio Code 向けに Veryl 拡張が提供されています。拡張はファイルタイプの検出とシンタックスハイライト、言語サーバの組み込みを提供します。拡張パネルから “Veryl” で検索するか、以下の URL からインストールしてください。

Veryl extension for Visual Studio Code

Vim / Neovim

Vim / Neovim 向けに Veryl プラグインが提供されています。プラグインはファイルタイプの検出とシンタックスハイライトを提供します。プラグインのインストールと言語サーバの組み込みは以下の URL を参照してください。

Vim / Neovim plugin

そのほかのエディタ

Veryl は言語サーバを提供しているので、言語サーバをサポートしているエディタ(例えば Emacs)であれば利用できます。

シェル補完

verylverylup のシェル補完スクリプトは verylup completion によって提供されます。例えば以下のコマンドはzsh向けの補完スクリプトを生成します。

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

サポートされているシェルは以下の通りです。

  • Bash
  • Elvish
  • Fish
  • PowerShell
  • Zsh

生成されたスクリプトの使い方は各シェルのドキュメントを参照してください。

Hello, World!

プロジェクトを作る

まず始めに、新しい Veryl プロジェクトを作りましょう。

veryl new hello

コマンドを実行すると、以下のようなディレクトリとファイルが作成されます。

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

0 directories, 1 file

Veryl.toml はプロジェクトの設定ファイルです。

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

全設定の説明はこちら

コードを書く

ソースコードはプロジェクトディレクトリ内のどこに書いても構いません。これは Veryl プロジェクトが独立したプロジェクトである場合もあれば、他のSystemVerilog プロジェクトに組み込まれている場合もあるからです。Veryl のソースコードの拡張子は .veryl です。

例えば以下のコードを src/hello.veryl に書いてみましょう。

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

1 directory, 2 files

注:この本のいくつかのソースコードには、マウスをホバーすると現れるプレイボタン “▶” があります。ボタンをクリックすると、トランスパイルされた SystemVerilog のコードが現れます。module ModuleA のコードのボタンを押してみましょう。

ビルドする

veryl build コマンドで SystemVerilog のソースコードを生成できます。

$ 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

デフォルトでは SystemVerilog のコードは Veryl のコードと同じディレクトリに生成されます。つまり src/hello.sv です。

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

さらに、生成されたコードのファイルリスト hello.f も生成されます。これは SystemVerilog コンパイラで使用できます。Verilator で使用するには以下のようにします。

$ verilator --cc -f hello.f

コード例

Veryl は SystemVerilog とほとんど同じセマンティクスを持っています。もし SystemVerilog に慣れていれば、いくつかの例をみるだけで Veryl の構文をだいたい把握できるでしょう。

この小さな例では、コメントに SystemVerilog 構文との違いが書かれています。

module ModuleA (
    // 識別子が先で `:` の後に型が来ます
    // ビット幅は `<>` で表されます
    i_data: input  logic<10>,
    o_data: output logic<10>,

    // `begin`/`end` ではなく `{}` を使います
) {
    assign o_data = i_data;
}

さらに、この章のコードブロックは編集することもできます。それぞれのコードを編集して実行してみましょう。

Veryl のソースコードは SystemVerilog と同様に、moduleinterfacepackage を持ちます。この章ではそれらの例を示します。

モジュール

// モジュール定義
module ModuleA #(
    param ParamA: u32 = 10,
    const ParamB: u32 = 10, // 末尾カンマが可能です
) (
    i_clk : input  clock            , // `clock` はクロックのための特別な型です
    i_rst : input  reset            , // `reset` はリセットのための特別な型です
    i_sel : input  logic            ,
    i_data: input  logic<ParamA> [2], // `[]` は SystemVerilog のアンパック配列です
    o_data: output logic<ParamA>    , // `<>` は SystemVerilog のパック配列です
) {
    // ローカルパラメータ宣言
    //   モジュール内では `param` は使えません
    const ParamC: u32 = 10;

    // 変数宣言
    var r_data0: logic<ParamA>;
    var r_data1: logic<ParamA>;
    var r_data2: logic<ParamA>;

    // 値の束縛
    let _w_data2: logic<ParamA> = i_data;

    // リセット付き always_ff 文
    //   `always_ff` はクロック(必須)とリセット(オプション)を持ちます
    //   `if_reset` は `if (i_rst)` を意味し、リセット極性を隠蔽するための構文です
    //   `if` 文に `()` はいりません
    //   `always_ff` 内の `=` はノンブロッキング代入です
    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 文
    always_ff (i_clk) {
        r_data1 = r_data0;
    }

    // モジュール内にクロックとリセットが1つしかない場合
    // クロックとリセットの指定は省略できます
    always_ff {
        r_data2 = r_data1;
    }

    assign o_data = r_data1;
}

インスタンス

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;

    // インスタンス宣言
    //   インスタンス宣言は `inst` キーワードではじまります
    //   ポート接続は `()` 内で指定します
    //   各ポートの接続は `[port_name]:[variable]` のような形式になります
    //   `[port_name]` は `[port_name]:[port_name]` を意味します
    inst u_module_b: ModuleB (
        i_clk          ,
        i_data: r_data1,
        o_data: r_data2,
    );

    // パラメータオーバーライド付きインスタンス宣言
    //   パラメータの接続記法はポートと同様です
    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 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 定義
    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,
    // modport によるポート宣言
    intf_a_mst: modport InterfaceA::master,
    intf_a_slv: modport InterfaceA::slave ,
) {
    // インターフェースのインスタンス
    inst u_intf_a: InterfaceA [10];
}

パッケージ

// パッケージ定義
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);
}

言語リファレンス

この章では Veryl の言語仕様について説明します。

ソースコードの構造

Veryl のソースコードはいくつかの moduleinterfacepackage からなります。

module ModuleA {}

module ModuleB {}

interface InterfaceA {}

package PackageA {}

トランスパイルされたコードにおける moduleinterfacepackage の名前には先頭にプロジェクト名が付きます。このサンプルコードでは project_ が付きます。これはプロジェクト間で名前が衝突するのを防ぐためです。

字句構造

この章では Veryl の字句構造について説明します。まず始めに、全体的なことがらからです。

エンコーディング

Veryl のソースコードは UTF-8 エンコーディングでなければなりません。

空白

(空白)、\t\n は空白として扱われ、Veryl のパーサはこれらを全て無視します。

コメント

行コメントと複数行コメントが使えます。ほとんどのコメントはトランスパイルされたコードにも出力されます。

// 行コメント

/*
複数

行

コメント
*/

ドキュメンテーションコメント

/// ではじまる行コメントはドキュメンテーションコメントとして扱われます。ドキュメンテーションコメントはドキュメントの生成に使われます。

/// ドキュメンテーションコメント

識別子

識別子は ASCII のアルファベットと数値、 _ からなります。先頭が数値であってはなりません。正式な定義は以下の正規表現です。

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

生識別子

Veryl のいくつかのキーワードは SystemVerilog では識別子として使用できるため、これらの識別子にアクセスするために生識別子を使います。例えば、clock は Veryl のキーワードなので r#clock とします。r#clock は SystemVerilog では clock にトランスパイルされます。

文字列

" で囲んだものが文字列になります。\"\n のように \ によるエスケープも可能です。

"Hello, World!"

演算子

ほとんどの演算子は SystemVerilog と同じです。いくつか違いがあるので注意してください。

  • <: 小なり演算子です。SystemVerilog の < と同じです。
  • >: 大なり演算子です。SystemVerilog の > と同じです。
// 単項算術演算
a = +1;
a = -1;

// 単項論理演算
a = !1;
a = ~1;

// 単項集約演算
a = &1;
a = |1;
a = ^1;
a = ~&1;
a = ~|1;
a = ~^1;
a = ^~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;
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;
a = 1 !=? 1;

// ビット演算
a = 1 & 1;
a = 1 ^ 1;
a = 1 ~^ 1;
a = 1 ^~ 1;
a = 1 | 1;

// 二項論理演算
a = 1 && 1;
a = 1 || 1;

数値

整数

// 整数
0123456789
01_23_45_67_89

// 2進数
32'b01xzXZ
32'b01_xz_XZ

// 8進数
36'o01234567xzXZ
36'o01_23_45_67_xz_XZ

// 10進数
32'd0123456789
32'd01_23_45_67_89

// 16進数
128'h0123456789abcdefxzABCDEFXZ
128'h01_23_45_67_89_ab_cd_ef_xz_AB_CD_EF_XZ

全ビットのセット

// 全て 0
'0

// 全て 1
'1

// 全て x
'x
'X

// 全て z
'z
'Z

幅なし整数

ビット幅指定は省略することができます。省略された場合、トランスパイルされたコードでは適切なビット幅が付与されます。

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

指定ビットのセット

“全ビットのセット” にビット幅指定を付与することもできます。

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

浮動小数点数

// 浮動小数点数
0123456789.0123456789
01_23_45_67_89.01_23_45_67_89

// 指数表記
0123456789.0123456789e+0123456789
01_23_45_67_89.01_23_45_67_89E-01_23_45_67_89

配列リテラル

'{} は配列リテラルを表します。リテラル内には式、repeat キーワード、default キーワードを配置することができます。

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}
}

データ型

この章ではデータ型について説明します。

組み込み型

幅指定可能な4値データ型

logic は4値(01xz)のデータ型です。幅は logic のあとの <> で指定できます。<X, Y, Z,,,> のように多次元指定も可能です。

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

幅指定可能な2値データ型

bit は2値(01)のデータ型です。幅は logic のあとの <> で指定できます。<X, Y, Z,,,> のように多次元指定も可能です。

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

整数型

整数型にはいくつかの種類があります。

  • u32:32ビットの符号なし整数
  • u64:64ビットの符号なし整数
  • i32:32ビットの符号付き整数
  • i64:64ビットの符号付き整数
module ModuleA {
    let _a: u32 = 1;
    let _b: u64 = 1;
    let _c: i32 = 1;
    let _d: i64 = 1;
}

浮動小数点数型

浮動小数点数型にもいくつかの種類があります。

  • f32:32ビット浮動小数点数
  • f64:64ビット浮動小数点数

いずれも IEEE Std 754 準拠の表現です。

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

文字列型

string は文字列を表す型です。

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

Type型

type は型の種類を表す型です。type 型の変数は paramconst としてのみ定義可能です。

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

ユーザ定義型

構造体

struct は複合データ型です。いくつかのフィールドを持つことができ、. 演算子を通してアクセスできます。

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 型の変数にはそのバリアントのうち1つだけをセットできます。バリアント名は [enum name]::[variant name] の形式で指定可能です。それぞれのバリアントは対応する整数値を持ち、= で指定することができます。指定されなかった場合は自動的に割り当てられます。

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

    var a: EnumA;

    assign a = EnumA::member_a;
}

enum の型が省略されている場合、適切なサイズの型がバリアントから自動的に推定されます。

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

列挙型エンコーディング

デフォルトでは各バリアントの値が省略されたときは0から順に割り当てられます。この割り当てのエンコードを指定したい場合は、#[enum_encoding] アトリビュートを指定できます。使用できるエンコードは以下の通りです。

  • 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 はパックされたタグなしの直和型で、SystemVerilog では packed union にトランスパイルされます。ユニオンのそれぞれのバリアントの幅は同じでなければなりません。

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;
}

型定義

type キーワードを使って、スカラー型や配列型への型エイリアスを定義することができます。

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

配列

任意のデータ型に対して [] と付与することで配列を定義することができます。配列の長さは [] 内の値で指定します。

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;
}

[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 はクロック配線を表す特別な型です。クロックの極性を指定するため以下の3種類があります。

  • clock: ビルド時の設定で指定される極性を持つクロック型
  • clock_posedge: 正極性のクロック型
  • clock_negedge: 負極性のクロック型

reset はリセット配線を表す特別な型です。リセットの極性と同期・非同期を指定するため以下の5種類があります。

  • reset: ビルド時の設定で指定される極性と同期性を持つリセット型
  • reset_async_high: 正極性の非同期リセット型
  • reset_async_low: 負極性の非同期リセット型
  • reset_sync_high: 正極性の同期リセット型
  • reset_sync_low: 負極性の同期リセット型

特別な要件がなければ、コードの再利用を高めるため clockreset の使用を推奨します。

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;
        }
    }
}

この章では式について説明します。式は変数や演算子、関数呼び出しなどを組み合わせたもので、評価して値を得ることができます。

演算子の優先順位

式内での演算子の優先順位は SystemVerilog とほとんど同じです。

演算子結合性優先順位
() [] :: .高い
+ - ! ~ & ~& | ~| ^ ~^ ^~ (単項)
**
* / %
+ - (二項)
<< >> <<< >>>
<: <= >: >=
== != === !== ==? !=?
& (二項)
^ ~^ ^~ (二項)
| (二項)
&&
||
= += -= *= /= %= &= ^= |=
<<= >>= <<<= >>>=
なし
{} inside outside if case switchなし低い

関数呼び出し

関数は function_name(argument) の形式で呼び出すことができます。$clog2 のような SystemVerilog のシステム関数も使えます。

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,
    ) {}
}

連結

{} はビット連結を表します。{} の中では repeat キーワードを使うことで指定されたオペランドを繰り返すこともできます。

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

if を用いた条件式を使えます。if キーワードの後に条件を示す節を置きますが、() で囲む必要はありません。if 式は常になんらかの値に評価される必要があるため else は必須です。

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

もう一つの条件式が case です。case値: 式 という形式の条件を複数持ちます。もし case キーワードの後の式と条件の左側の値が一致すれば、その条件の右側の式が返されます。値としては ..= のような範囲も指定できます。default はそれ以外の条件が全て失敗したときに返される特別な条件です。case 式は常になんらかの値に評価される必要があるため default は必須です。

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

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

switchcase のもう一つの形式です。switch式: 式 という形式を持ち、左側の式の評価結果が1の場合に、右側の式が返されます。

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,
    };
}

ビット選択

[] はビット選択演算子です。[] に式を指定すれば1ビットを選択できます。範囲選択する場合は [式:式] とします。

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

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

位置と幅による選択

+:-: 記法は開始位置と幅により選択することができます。[A+:B][(A+B-1):A] を、 [A-:B][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];
}

ステップ付きインデックスによる選択

step 記法はステップ付きのインデックスにより選択することができます。[A step B] は “ステップ B で分割したときのインデックス A を選択する” を意味し、[(B*A)+:B] と等しくなります。

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

    assign b = a[2 step 3];
}

範囲

範囲は範囲演算子で指定できます。範囲演算子には以下の2種類があります。

  • ..:半開区間
  • ..=:閉区間

範囲は for 文などの場所で使うことができます。

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

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

msb / lsb

msblsb[] によるビット選択で使用できます。msb はオペランドの最上位ビットを意味します。lsb はオペランドの最下位ビットを意味し、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 は 指定された式が {} で与えられた条件内にあるかどうかを調べます。条件は単一の式または範囲を指定できます。条件を満たすとき inside1 を、そうでなければ 0 を返します。outside はその逆です。

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};
}

この章では文について説明します。文は always_ffalways_comb などいくつかの宣言で使用することができます。

代入

代入文は 変数 = 式; の形式です。SystemVerilog と異なり、always_comb でも always_ff でも代入演算子は = です。以下のような代入演算子もあります。

  • +=:加算代入
  • -=:減算代入
  • *=:乗算代入
  • /=:除算代入
  • %=:剰余代入
  • &=:ビットAND代入
  • |=:ビットOR代入
  • ^=:ビットXOR代入
  • <<=:論理左シフト代入
  • >>=:論理右シフト代入
  • <<<=:算術左シフト代入
  • >>>=:算術右シフト代入
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;
    }
}

関数呼び出し

関数呼び出しは文として使うこともできます。この場合、関数の戻り値は無視されます。

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

if

if は文として使うこともできます。if 式との違いは {} 内に文を書くことです。

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

caseswitch は文として使うこともできます。各アームの右辺が文になる点を除けば Case / Switch 式 と同じです。

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;
        }
    }
}

for

for 文は繰り返しを表します。in キーワードの前にループ変数を、後に範囲を書きます。

break を使ってループを中断することもできます。

module ModuleA {
    var a: logic<10>;

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

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

return

return 文は関数からの戻りを示します。return キーワードの後の式は関数の戻り値です。

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

let

let 文はある名前に値を束縛します。これは always_ffalways_comb および関数宣言の中で使うことができます。

let 文はブロック中のどこにでも置くことができます。

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;
    }
}

宣言

この章では宣言について説明します。

変数

変数宣言は var キーワードで始まり、変数名、:、変数の型と続きます。

未使用の変数は警告が発生します。_ で始まる変数名は未使用変数を意味し、警告を抑制します。

宣言時に名前に値を束縛する場合は var の代わりに let を使います。

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;
}

パラメータ

パラメータは変数と同時に宣言できます。param キーワードはモジュールヘッダで使用することができ、インスタンス時に上書きできます。const キーワードはモジュール内で使用することができ、上書きできません。

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

レジスタ

レジスタ変数とは always_ff で代入される変数です。合成フェーズでフリップフロップにマップされます。

always_ff は必須のクロック変数、オプションのリセット変数、{} ブロックをとります。クロックとリセットは () に書きます。指定されたクロックとリセットは clock / reset 型を持ち、そのビット幅は1ビットでなければなりません。

if_resetalways_ff に書ける特別なキーワードで、そのレジスタ変数のリセット条件を示します。if_reset を使う場合は always_ff のリセット変数は必須です。これを使うことで、リセットの極性と同期性を隠ぺいすることができます。実際の極性と同期性は Veryl.toml[build] セクションで設定できます。

モジュール内にクロックとリセットが1つしかない場合、クロックとリセットの指定は省略できます。

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;
        }
    }
}

組み合わせ回路

always_comb で代入される変数は組み合わせ回路を意味します。

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

    always_comb {
        b = a + 1;
    }
}

assign

assign 宣言で変数に式を代入することができます。

module ModuleA {
    var a: logic<10>;

    assign a = 1;
}

関数

関数は function キーワードで宣言できます。引数は () 内に書き、戻り値の型を -> の後に書きます。

関数が戻り値を持たない場合、-> は省略できます。

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

initial ブロック内の文はシミュレーション開始時に実行され、final は終了時です。どちらも論理合成では無視され、デバッグやアサーションに使うことができます。

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

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

アトリビュート

アトリビュートは変数宣言などいくつかの宣言に注釈を付けることができます。

sv アトリビュート

sv アトリビュートは SystemVerilog のアトリビュートを表し、(* *) という形式の SystemVerilog アトリビュートに変換されます。

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

allow アトリビュート

allow アトリビュートは指定されたリントチェックを無効化するために使用できます。

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

指定可能なリント名は以下の通りです。

  • unused_variable
  • missing_reset_statement
  • missing_port

ifdef/ifndef アトリビュート

ifdefifndef アトリビュートは定義された値によってコードブロックを有効にするかどうかを制御するために使用することができます。もし DEFINE_A が定義されていれば、#[ifdef(DEFINE_A)] のついたコードブロックは有効に、#[ifndef(DEFINE_A)] のついたコードブロックは無効になります。

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

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

生成

宣言や forif を使って生成することができます。: で示すラベルは生成された複数の宣言を識別するのに必要です。

module ModuleA {
    var a: logic<10>;

    for i in 0..10 :label {
        if i >: 5 :label {
            assign a[i] = i + 2;
        } else { // else 句のラベルは省略可能
            assign a[i] = i + 2;
        }
    }
}

インスタンス

inst キーワードはモジュールやインターフェースのインスタンス化を表します。インスタンスの名前は inst の後に、インスタンスの型は : の後に書きます。#() でパラメータオーバーライドを、() でポート接続を表します。

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

    inst instB: ModuleB #(
        paramA    , // 名前によるパラメータ代入
        paramB: 10,
    ) (
        a    , // 名前によるポート接続
        bb: b,
    );
}

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

名前付きブロック

{} ブロックにラベルを付けることができます。そのような名前付きブロックは独立した名前空間を持ちます。

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

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

Import / Export

import 宣言は他のパッケージからシンボルをインポートします。モジュール、インターフェース、パッケージの要素としてだけでなくトップレベルにも配置することができます。import 宣言の引数には package::* のようなワイルドカードパターンを使用することができます。

// ファイルスコープインポート
import $sv::SvPackage::*;

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

package PackageA {
    const paramA: u32 = 1;
}

export 宣言は宣言したパッケージからシンボルをエクスポートします。全てのシンボルをエクスポートするには export * を使用します。

package PackageA {
    const paramA: u32 = 1;
}

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

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

モジュール

モジュールはソースコードの最上位コンポーネントの1つです。モジュールはオーバーライド可能なパラメータ、接続ポート、内部ロジックを持ちます。

オーバーライド可能なパラメータは #() 内で宣言できます。それぞれのパラメータ宣言は param キーワードで始まり、識別子、:、パラメータの型、デフォルト値で構成されます。

接続ポートは () 内で宣言できます。それぞれのポート宣言は識別子、:、ポートの方向、ポートの型で構成されます。利用可能なポート方向は以下の通りです。

  • input:入力ポート
  • output:出力ポート
  • inout:双方向ポート
  • modport:インターフェースのmodport
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;
        }
    }
}

インターフェース

インターフェースはソースコードの最上位コンポーネントの1つです。インターフェースはオーバーライド可能なパラメータ、インターフェース定義を持ちます。

オーバーライド可能なパラメータについてはモジュールと同じです。

インターフェース定義では modport を宣言することができます。modport はモジュールのポート宣言で、ポートを束ねて接続するために使うことができます。

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,
    }
}

パッケージ

パッケージはソースコードの最上位コンポーネントの1つです。パッケージはパラメータや関数などいくつかの宣言をまとめることができます。

パッケージ内の要素にアクセスするには、:: 記号を使って PackageA::ParamA のようにします。

package PackageA {
    const ParamA: u32 = 0;
}

SystemVerilogとの相互運用

SystemVerilogの要素にアクセスする場合は $sv 名前空間を使えます。例えば、SystemVerilogソースコードの “ModuleA” は $sv::ModuleA です。Veryl はこれらの要素が実際に存在するかどうかは確認しません。

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

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

Veryl のキーワードとして使われている識別子にアクセスするには生識別子を使います。

module ModuleA (
    i_clk: input clock,
) {
    inst a: $sv::ModuleA (
        // clock: i_clk
        // ^ `clock` はキーワードなので構文エラー
        // 代わりに `r#clock` を使います
        r#clock: i_clk,
    );
}

可視性

デフォルトではプロジェクトのトップレベルアイテム(モジュール、インターフェース、パッケージ)はプライベートです。プライベートとは他のプロジェクトから参照できないことを意味します。

pub キーワードによって他のプロジェクトから見えるように指定することができます。veryl doc コマンドはパブリックなアイテムの ドキュメント のみを生成します。

pub module ModuleA {}

pub interface InterfaceA {}

pub package PackageA {}

他言語組み込み

embed 宣言

embed 宣言により他言語をコードに埋め込むことができます。embed 宣言の第一引数は埋め込み方法です。以下の方法がサポートされています。

  • inline: コードをそのまま展開する
  • cocotb: cocotb テストとして扱う

コードブロックは lang{{{ で始まり、}}} で終わります。以下の lang 指示子がサポートされています。

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

include 宣言

include 宣言により他言語のファイルを含めることができます。include 宣言の第一引数は embed 宣言と同じです。第二引数はソースコードからの相対ファイルパスです。

include(inline, "module.sv");

組み込みテスト

組み込みテストは #[test(test_name)] アトリビュートでマークすることができます。マークされたブロックはテストとして認識され、 veryl test コマンドによって実行されます。

組み込みテストにはいくつかの種類があります。

  • SystemVerilogテスト
  • cocotb テスト

veryl test で使用される RTLシミュレータについては シミュレータ を参照してください。--wave オプションで波形を生成することもできます。

SystemVerilogテスト

SystemVerilog テストは inline 指定子で記述することができます。ブロックのトップレベルモジュールはテスト名と同じでなければなりません。

$info$warning$error$fatal システム関数によるメッセージは Veryl コンパイラにより実行ログとして表示されます。$error$fatal の呼び出しはテストの失敗として扱われます。

以下の例では SystemVerilog のソースコードを embed 宣言で埋め込み、テストとしてマークしています。

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

cocotb テスト

cocotb テストは cocotb 指定子で記述することができます。テスト対象のモジュール名は #[test] アトリビュートの第二引数で指定します。

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

ジェネリクス

ジェネリクスはパラメータオーバーライドでは実現できないアイテムのパラメータ化を可能にします。以下のアイテムがジェネリクスをサポートしています。

  • 関数
  • モジュール
  • インターフェース
  • パッケージ
  • 構造体
  • ユニオン

それぞれのジェネリック定義はジェネリックパラメータ(T のような大文字1文字がよく使われます)を持ち、定義内で識別子や式として配置できます。ジェネリックパラメータはアイテムの識別子の後に ::<> を用いて宣言します。

各ジェネリックパラメータにはコロンの後に T: const のようなジェネリック境界が必要です。ジェネリック境界はどのような値をそのパラメータに渡すことができるかを示します。使用可能なジェネリック境界は以下の通りです。

  • const: 定数値を渡すことができる
  • type: 任意の型を渡すことができる
  • 名前付きプロトタイプ

名前付きプロトタイプは特別なジェネリック境界です。詳細は プロトタイプ を参照してください。

ジェネリクスを使用するためには ::<> を用いて実パラメータを与えます。実パラメータとしては数値リテラルと :: で連結された識別子を使用することができます。

さらに、実パラメータはジェネリクス定義位置から参照できなければなりません。例えば、モジュール名はプロジェクト全体から参照できるので、実パラメータとして使用できます。一方、ローカルパラメータは多くの場合、実パラメータとして使用できません。これはローカルパラメータがジェネリクス定義位置からは参照できない場合に発生します。

ジェネリック関数

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);
}

ジェネリックモジュール/インターフェース

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 {}

ジェネリックパッケージ

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

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

ジェネリック構造体

module ModuleA {
    type TypeA = i32;

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

    // ローカルに定位された型が使用できています
    // これは `TypeA` が `StructA` の定義位置から参照できるためです
    var _a: StructA::<TypeA>          ;
    var _b: StructA::<PackageA::TypeB>;
    var _c: StructA::<PackageA::TypeC>;
}

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

デフォルトパラメータ

ジェネリックパラメータはその後に = を加えることででフォルト値を指定することができます。呼び出し側でパラメータ指定が省略された場合にデフォルト値が使われます。

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);
}

デフォルトパラメータはジェネリックパラメータリストの最後に置く必要があります。そうでなければ、どのパラメータが省略されたかが曖昧になるためです。

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

    // エラー
    //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);
}

プロトタイプ

プロトタイプは特別なジェネリック境界で、ジェネリックパラメータに渡せるプロトタイプを示します。現在はモジュールプロトタイプのみがサポートされています。

以下の例では、ProtoA がパラメータ A とポート i_dat o_dat を持つモジュールプロトタイプです。T: ProtoA のように制約することで、ジェネリックパラメータ T がそれらのパラメータとポートを持つ必要があることが示されます。

プロトタイプを使うには for による実装が必要です。ModuleCModuleDfor ProtoA 指定により、 ProtoA の条件を満たすことが示されています。これにより、それらのモジュールは ModuleB の ジェネリックパラメータ T として使用できるようになります。

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;
}

クロックドメインアノテーション

モジュール内に複数のクロックがある場合、`a のような明示的なクロックドメインアノテーションが必要です。アノテーションはそれぞれの信号がどのクロックドメインに所属するかを示します。

module ModuleA (
    // クロックドメイン `a に所属
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    o_dat_a: output `a logic,

    // クロックドメイン `b に所属
    i_clk_b: input  `b clock,
    i_dat_b: input  `b logic,
    o_dat_b: output `b logic,
) {
    // 同じクロックドメイン内の代入は安全
    assign o_dat_a = i_dat_a;
    assign o_dat_b = i_dat_b;
}

モジュール内にクロックが1つしかない場合、アノテーションは省略できます。

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

`_ は暗黙のクロックドメインを表す特別なクロックドメインです。これは複数のクロックが同じ暗黙のクロックドメインに所属することを示すために使用できます。

module ModuleA (
    // 全ての信号は暗黙のクロックドメインに所属
    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 コンパイラはクロックドメインクロッシングをエラーとして検出します。そのためクロックドメインクロッシングを行う場所には明示的な unsafe (cdc) ブロックが必要です。ブロック内ではクロックドメインクロッシングのチェックが抑制されるため、設計者はそれが安全かどうか注意深く確認する必要があります。

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,
) {
    // エラー "Clock domain crossing is detected"
    //assign o_dat_b = i_dat_a;

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

クロックドメイン境界には通常シンクロナイザセルが挿入されます。この場合も unsafe (cdc) ブロックが必要です。

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,
        );
    }
}

標準ライブラリ

Verylはいくつかの便利な汎用モジュールを標準ライブラリとして提供しています。標準ライブラリは $std 名前空間にあり、依存関係を追加することなく使用できます。

標準ライブラリの公開APIは Veryl 1.0 がリリースされるまでは変更される可能性があります。

module ModuleA {
    // $std::fifo は標準ライブラリの FIFO モジュール
    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       : _,
    );
}

標準ライブラリの完全なリストとドキュメントは https://std.veryl-lang.org にあります。

開発環境

この章ではプロジェクト設定や開発ツールなど開発環境について説明します。

プロジェクト設定

[project] セクション

Veryl.toml の最初のセクションは [project] です。nameversion は必須です。

name フィールド

プロジェクト名は生成されるコードのプレフィックスに使われます。そのためプロジェクト名はアルファベットか _ で始まり、英数字と_ しか使ってはいけません。

version フィールド

プロジェクトのバージョンは セマンティックバージョニングに従います。バージョンは以下の3つの数字からなります。

  • メジャー – 互換性のない変更時に上げる
  • マイナー – 互換性のある機能追加時に上げる
  • バッチ – 互換性のあるバグ修正時に上げる
[project]
version = "0.1.0"

authors フィールド

オプションの authors フィールドにはこのプロジェクトの作者である人や組織を配列にリストアップします。配列内の各文字列のフォーマットは自由です。名前のみ、Eメールアドレスのみ、名前と括弧で囲んだEメールアドレスといった形式がよく使われます。

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

description フィールド

description はプロジェクトの短い説明です。マークダウンではなくプレーンテキスト形式で書きます。

license フィールド

license フィールドはこのプロジェクトがどのライセンスで公開されているかを指定します。指定する文字列はSPDX 2.3 license expressionに従ってください。

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

repository フィールド

repository フィールドはプロジェクトのソースリポジトリへのURLです。

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

[build] セクション

[build] セクションはコード生成の設定です。詳細はこちら

[format] セクション

[format] セクションはコードフォーマッターの設定です。詳細はこちら

[lint] セクション

[lint] セクションはリンタの設定です。詳細はこちら

[test] セクション

[test] セクションはRTLシミュレータによるテストの設定です。詳細はこちら

[publish] セクション

[publish] セクションはプロジェクト公開の設定です。詳細はこちら

[dependencies] セクション

[dependencies] セクションはライブラリの依存関係です。詳細はこちら

Build

[build] セクションはコード生成の設定です。

clock_type フィールド

clock_type フィールドはフリップフロップを駆動するクロックエッジを指定します。

  • posedge – 立ち上がりエッジ
  • negedge – 立ち下がりエッジ

reset_type フィールド

reset_type フィールドはリセットの極性と同期性を指定します。

  • async_low – 非同期・負極性
  • async_high – 非同期・正極性
  • sync_low – 同期・負極性
  • sync_high – 同期・正極性

filelist_type フィールド

filelist_type フィールドはファイルリストのフォーマットを指定します。

  • absolute – プレーンテキスト形式の絶対パスのリスト
  • relative – プレーンテキスト形式の相対パスのリスト
  • flgenflgen 形式のファイルリスト

target フィールド

target フィールドはコードの生成先を指定します。

  • source – ソースコードと同じディレクトリ
  • directory – 特定のディレクトリ
  • bundle – 特定のファイル

directory あるいは bundle を指定する場合は、ターゲットパスを path キーで指定します。

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

implicit_parameter_types フィールド

implicit_parameter_types フィールドは生成コードの parameter 宣言で省略する型をリストアップします。いくつかのEDAツールでは特定の型(例えば string)を parameter 宣言で使うことができないためです。例えば string を指定する場合は以下のようにします。

[build]
implicit_parameter_types = ["string"]

omit_project_prefix フィールド

omit_project_prefixtrue のとき、モジュール・インターフェース・パッケージ名のプロジェクトプレフィックスは省略されます。この値はデフォルトで false です。

[build]
omit_project_prefix = true

strip_comments フィールド

strip_commentstrue のとき、コメント出力は省略されます。この値はデフォルトで false です。

[build]
strip_comments = true

*_prefix*_suffix フィールド

*_prefix*_suffix はコード生成時の追加のプレフィックスとサフィックスを指定します。指定可能な設定は以下の通りです。

  • clock_posedge_prefix: clock_type = posedge のときの clock 型のプレフィックス
  • clock_posedge_suffix: clock_type = posedge のときの clock 型のサフィックス
  • clock_negedge_prefix: clock_type = negedge のときの clock 型のプレフィックス
  • clock_negedge_suffix: clock_type = negedge のときの clock 型のサフィックス
  • reset_high_prefix: reset_type = *_high のときの reset 型のプレフィックス
  • reset_high_suffix: reset_type = *_high のときの reset 型のサフィックス
  • reset_low_prefix: reset_type = *_low のときの reset 型のプレフィックス
  • reset_low_suffix: reset_type = *_low のときの reset 型のサフィックス

sourcemap_target フィールド

sourcemap_target フィールドはソースマップの生成先を指定します。指定可能な設定は以下の通りです。

  • target – ターゲットコードと同じディレクトリ
  • directory – 特定のディレクトリ
  • none – ソースマップなし

directory を指定する場合は、ターゲットパスを path キーで指定します。

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

expand_inside_operation フィールド

expand_inside_operationtrue のとき、inside 演算子を使った演算は==? 演算子を使った論理に展開されます。これはいくつかのEDAツールがinside 演算子をサポートしていないためです。この値はデフォルトで false です。

[build]
expand_inside_operation = true

exclude_std フィールド

exclude_stdtrue のとき、標準ライブラリはインクルードされません。

[build]
exclude_std = true

Format

[format] セクションはフォーマッタの設定です。

[format]
indent_width = 4

設定

設定設定値説明
indent_width整数インデントのスペース幅

Lint

[lint] セクションはリンターの設定です。

[lint.naming]
case_enum = "snake"

設定

[lint.naming] セクション

このセクションは命名規則の設定です。

設定設定値説明
case_enumケースタイプ1enum のケーススタイル
case_functionケースタイプ1function のケーススタイル
case_function_inoutケースタイプ1inout 引数のケーススタイル
case_function_inputケースタイプ1input 引数のケーススタイル
case_function_outputケースタイプ1output 引数のケーススタイル
case_function_refケースタイプ1ref 引数のケーススタイル
case_instanceケースタイプ1インスタンスのケーススタイル
case_interfaceケースタイプ1interface のケーススタイル
case_modportケースタイプ1modport のケーススタイル
case_moduleケースタイプ1module のケーススタイル
case_packageケースタイプ1package のケーススタイル
case_parameterケースタイプ1parameter のケーススタイル
case_port_inoutケースタイプ1inout ポートのケーススタイル
case_port_inputケースタイプ1input ポートのケーススタイル
case_port_modportケースタイプ1modport ポートのケーススタイル
case_port_outputケースタイプ1output ポートのケーススタイル
case_regケースタイプ1レジスタ変数2のケーススタイル
case_structケースタイプ1struct のケーススタイル
case_unionケースタイプ1union のケーススタイル
case_varケースタイプ1変数のケーススタイル
case_wireケースタイプ1ワイヤ変数3のケーススタイル
prefix_enum文字列enum のプレフィックス
prefix_function文字列function のプレフィックス
prefix_function_inout文字列inout 引数のプレフィックス
prefix_function_input文字列input 引数のプレフィックス
prefix_function_output文字列output 引数のプレフィックス
prefix_function_ref文字列ref 引数のプレフィックス
prefix_instance文字列インスタンスのプレフィックス
prefix_interface文字列interface のプレフィックス
prefix_modport文字列modport のプレフィックス
prefix_module文字列module のプレフィックス
prefix_package文字列package のプレフィックス
prefix_parameter文字列parameter のプレフィックス
prefix_port_inout文字列inout ポートのプレフィックス
prefix_port_input文字列input ポートのプレフィックス
prefix_port_modport文字列modport ポートのプレフィックス
prefix_port_output文字列output ポートのプレフィックス
prefix_reg文字列レジスタ変数2のプレフィックス
prefix_struct文字列struct のプレフィックス
prefix_union文字列union のプレフィックス
prefix_var文字列変数のプレフィックス
prefix_wire文字列ワイヤ変数3のプレフィックス
suffix_enum文字列enum のサフィックス
suffix_function文字列function のサフィックス
suffix_function_inout文字列inout 引数のサフィックス
suffix_function_input文字列input 引数のサフィックス
suffix_function_output文字列output 引数のサフィックス
suffix_function_ref文字列ref 引数のサフィックス
suffix_instance文字列インスタンスのサフィックス
suffix_interface文字列interface のサフィックス
suffix_modport文字列modport のサフィックス
suffix_module文字列module のサフィックス
suffix_package文字列package のサフィックス
suffix_parameter文字列parameter のサフィックス
suffix_port_inout文字列inout ポートのサフィックス
suffix_port_input文字列input ポートのサフィックス
suffix_port_modport文字列modport ポートのサフィックス
suffix_port_output文字列output ポートのサフィックス
suffix_reg文字列レジスタ変数2のサフィックス
suffix_struct文字列struct のサフィックス
suffix_union文字列union のサフィックス
suffix_var文字列変数のサフィックス
suffix_wire文字列ワイヤ変数3のサフィックス
re_forbidden_enum正規表現4enum の禁止正規表現
re_forbidden_function正規表現4function の禁止正規表現
re_forbidden_function_inout正規表現4inout 引数の禁止正規表現
re_forbidden_function_input正規表現4input 引数の禁止正規表現
re_forbidden_function_output正規表現4output 引数の禁止正規表現
re_forbidden_function_ref正規表現4ref 引数の禁止正規表現
re_forbidden_instance正規表現4インスタンスの禁止正規表現
re_forbidden_interface正規表現4interface の禁止正規表現
re_forbidden_modport正規表現4modport の禁止正規表現
re_forbidden_module正規表現4module の禁止正規表現
re_forbidden_package正規表現4package の禁止正規表現
re_forbidden_parameter正規表現4parameter の禁止正規表現
re_forbidden_port_inout正規表現4inout ポートの禁止正規表現
re_forbidden_port_input正規表現4input ポートの禁止正規表現
re_forbidden_port_modport正規表現4modport ポートの禁止正規表現
re_forbidden_port_output正規表現4output ポートの禁止正規表現
re_forbidden_reg正規表現4レジスタ変数2の禁止正規表現
re_forbidden_struct正規表現4struct の禁止正規表現
re_forbidden_union正規表現4union の禁止正規表現
re_forbidden_var正規表現4変数の禁止正規表現
re_forbidden_wire正規表現4ワイヤ変数3の禁止正規表現
re_required_enum正規表現4enum の必須正規表現
re_required_function正規表現4function の必須正規表現
re_required_function_inout正規表現4inout 引数の必須正規表現
re_required_function_input正規表現4input 引数の必須正規表現
re_required_function_output正規表現4output 引数の必須正規表現
re_required_function_ref正規表現4ref 引数の必須正規表現
re_required_instance正規表現4インスタンスの必須正規表現
re_required_interface正規表現4interface の必須正規表現
re_required_modport正規表現4modport の必須正規表現
re_required_module正規表現4module の必須正規表現
re_required_package正規表現4package の必須正規表現
re_required_parameter正規表現4parameter の必須正規表現
re_required_port_inout正規表現4inout ポートの必須正規表現
re_required_port_input正規表現4input ポートの必須正規表現
re_required_port_modport正規表現4modport ポートの必須正規表現
re_required_port_output正規表現4output ポートの必須正規表現
re_required_reg正規表現4レジスタ変数2の必須正規表現
re_required_struct正規表現4struct の必須正規表現
re_required_union正規表現4union の必須正規表現
re_required_var正規表現4変数の必須正規表現
re_required_wire正規表現4ワイヤ変数3の必須正規表現
1

設定可能な値は以下です。

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

".*"のような正規表現です。使用可能な構文はこちら.

2

レジスタ変数とは always_ff で代入される変数です。合成フェーズでフリップフロップにマップされます。

3

ワイヤ変数とは always_comb で代入される変数です。合成フェーズでワイヤにマップされます。

Test

[test] セクションは以下のように組み込みユニットテストの設定を指定します。

[test]
simulator = "vcs"

設定

[test] セクション

このセクションはテストの設定です。

設定設定値説明
simulatorシミュレータ名1デフォルトのシミュレータ
1

設定可能な値は以下です。

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

[test.verilator] セクション

このセクションはVerilatorによるテストの設定です。

設定設定値説明
compile_args[文字列]verilator コマンドへの追加の引数
simulate_args[文字列]シミュレーションバイナリへの追加の引数

[test.vcs] セクション

このセクションはVCSによるテストの設定です。

設定設定値説明
compile_args[文字列]vcs コマンドへの追加の引数
simulate_args[文字列]シミュレーションバイナリへの追加の引数

[test.vivado] セクション

このセクションはVivadoによるテストの設定です。

設定設定値説明
compile_args[文字列]xvlog コマンドへの追加の引数
elaborate_args[文字列]xelab コマンドへの追加の引数
simulate_args[文字列]xsim コマンドへの追加の引数

waveform_target フィールド

waveform_target フィールドは波形の生成先を指定します。

  • target – ターゲットコードと同じディレクトリ
  • directory – 特定のディレクトリ

directory を指定する場合は、ターゲットパスを path キーで指定します。

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

Publish

[publish] セクションは以下のようにプロジェクト公開の設定を指定します。

[publish]
bump_commit = true
bump_commit_message = "Bump"

設定

設定設定値デフォルト説明
bump_commitブーリアンfalseバージョンアップ後の自動コミット
publish_commitブーリアンfalse公開後の自動コミット
bump_commit_mesasge文字列“chore: Bump version”バージョンアップ後のコミットメッセージ
publish_commit_mesasge文字列“chore: Publish”公開後のコミットメッセージ

依存関係

他の Veryl プロジェクトへの依存関係をプロジェクトに追加したい場合、Veryl.toml[dependencies] セクションを追加します。エントリの左辺は依存関係へのパス、右辺はバージョンです。

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

デフォルトでは依存関係の名前空間はそのプロジェクト名と同じです。もし名前空間を指定したい場合は、 name フィールドを使います。

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

同じ依存関係の複数のバージョンを使う場合は、以下のようにそれぞれの名前を指定できます。

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

依存関係の使用

Veryl.toml に依存関係を追加したあとは、その依存関係の moduleinterfacepackageを使うことができます。以下は veryl_sample の依存関係に含まれる delay モジュールを使った例です。

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  ,
    );
}

注:上記のコードのプレイボタンの結果は依存関係解決を行わないので正確ではありません。実際のモジュール名は veryl_sample_delay になります。

バージョン要求

[dependencies] セクションの version フィールドはバージョン要求を示します。例えば、version = "0.1.0"0.1.0 と互換性のある最新バージョンを意味します。互換性はセマンティックバージョニングで判定されます。バージョンは以下の3つの部分からなります。

  • メジャー バージョンはAPI非互換な変更
  • マイナー バージョンは互換性のある機能追加
  • パッチ バージョンは互換性のあるバグ修正

もし メジャー バージョンが 0 なら、マイナー が非互換変更と解釈されます。

バージョン 0.1.00.1.10.2.0があった場合、0.1.1 が選択されます。これは以下のように決定されます。

  • 0.1.00.1.0 と互換性がある
  • 0.1.10.1.0 と互換性がある
  • 0.2.00.1.0 と互換性がない
  • 0.1.1 は互換性のある最新バージョン

version フィールドは =0.1.0 のような指定も可能です。詳細は Rust のバージョン要求についてのドキュメントを参照してください。Specifying Dependencies.

プロジェクトを公開する

プロジェクトを公開するには veryl publish コマンドを使います。公開とはバージョン番号とgitのリビジョンを紐づけることです。

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

veryl publish は以下のように公開されたバージョンの情報を含んだ Veryl.pub というファイルを生成します。

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

Veryl.pub と生成した後、gitのadd、commit、pushを行えば公開手続きは完了です。gitブランチはデフォルトブランチでなければなりません。これは Veryl が Veryl.pub をデフォルトブランチから探すためです。

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

Veryl.toml[publish] セクションに publish_commit を設定して自動コミットを有効にすれば、gitのaddとcommitが自動で実行されます。

$ 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)

バージョンを上げる

--bump オプションを使うと公開と同時にバージョンを上げることもできます。公開と同様に、Veryl.toml[publish] セクションに bump_commit を設定すれば自動でcommitされます。

$ 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)

設定

全設定の説明はこちら

ディレクトリ構成

Veryl は任意のディレクトリ構成をサポートしています。これは独立したプロジェクトと他のプロジェクトに組み込まれたプロジェクトでは最適なディレクトリ構成が異なるためです。

この節ではいくつかのディレクトリ構成パターンを示します。

単一のソースディレクトリ

このパターンでは全てのソースコードは src ディレクトリに配置されます。src 以下のサブディレクトリの構成は自由です。

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

2 directories, 4 files

Veryl は全ての *.veryl ファイルを収集し、デフォルトではソースと同じディレクトリにコードを生成します。この挙動は以下の設定で明示することもできます。

[build]
target = "source"

veryl build を実行するとディレクトリ構成は以下のようになります。

$ 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

単一のソースとターゲットディレクトリ

生成されたコードを1つのディレクトリに入れたい場合、Veryl.toml[build] セクションで target を以下のように設定します。

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

ディレクトリ構成は以下のようになります。

$ 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

マルチソースディレクトリ

既存の SystemVerilog プロジェクトに Veryl のプロジェクトを組み込む場合、以下のような構成にすることもできます。

$ 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

生成された prj.f は生成されたソースコードを全てリストアップしているので、既存の SystemVerilog ファイルリストと一緒に使うことができます。

.gitignore について

Veryl はデフォルトの .gitignore を提供しません。これはプロジェクト毎にどのファイルを無視する必要があるかが変わるためです。

.gitignore の候補としては以下が考えられます。

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

フォーマッタ

veryl fmt コマンドでソースコードをフォーマットできます。あるいは言語サーバの textDocument/formatting 要求によるフォーマットにも対応しています。

全設定の説明はこちら

リンタ

veryl check あるいは veryl build でリントチェックができます。あるいは言語サーバはリアルタイムでのチェックを行います。

全設定の説明はこちら

シミュレータ

RTLシミュレータによるテストは veryl test で実行することができます。サポートされているシミュレータは以下の通りです。

Verilatorはデフォルトのシミュレータです。Veryl.tomlやコマンドラインオプションでシミュレータが指定されていない場合に使用されます。

全設定の説明はこちら

cocotb

cocotb テストを実行するには cocotb がインストールされた python3 の環境が必要です。サポートされている cocotb のバージョンは 1.9.0 のみです。

例えば以下のコマンドでインストールすることができます。

$ pip3 install cocotb==1.9.0

シミュレータバックエンドとしては Verilator のみサポートされています。

言語サーバ

veryl-ls は言語サーバのバイナリです。使用するにはエディタの設定やプラグインが必要です。

設定可能な項目は以下の通りです。これは各エディタの設定から指定できます。

設定設定値デフォルト説明
useOperatorCompletionブーリアンfalse演算子(例 ‘>:’, ‘>>’)の補完を有効にする

互換性

いくつかのツールはサポートしていない SystemVerilog 構文があります。これをサポートするために、 Veryl.toml の設定でコード生成をカスタマイズすることができます。

Vivado

文字列パラメータ

Vivadoは string 型の parameter をサポートしていません。

parameter string a = "A";

その場合は implicit_parameter_types を設定してください。

[build]
implicit_parameter_types = ["string"]

設定すると生成コードは以下のようになります。

parameter a = "A";

Quartus

inside 演算子

Quartus は inside 演算子をサポートしていません。その場合は expand_inside_operation を設定してください。

[build]
expand_inside_operation = true

設定すると、 inside 演算子を使った演算は ==? 演算子を使った論理に展開されます。

ドキュメンテーション

プロジェクトのドキュメントは veryl doc コマンドで生成することができます。全てのパブリックなモジュールとインターフェース、パッケージがリストアップされます。(参照 可視性

詳細な説明を書きたい場合はドキュメンテーションコメントを追加することもできます。ドキュメンテーションコメントではマークダウン記法を使えます。

以下のフォーマットもサポートされています。

それぞれの構文は wavedrommermaid コードブロック内で使用できます。

詳細な構文は以下を参照してください。

/// ModuleAの詳細説明
///
/// * リスト要素0
/// * リスト要素1
///
/// ```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 #(
    /// データ幅
    param ParamA: u32 = 1,
    local ParamB: u32 = 1,
) (
    i_clk : input  clock        , /// クロック
    i_rst : input  reset        , /// リセット
    i_data: input  logic<ParamA>, /// データ入力
    o_data: output logic<ParamA>, /// データ出力
) {
    assign o_data = 0;
}

設定可能な項目は以下の通りです。これは Veryl.toml[doc] セクションで指定できます。

[doc]
path = "document"
設定設定値デフォルト説明
path文字列“doc”出力ディレクトリへのパス

GitHub Action

ビルド済みのVerylバイナリをダウンロードするための公式GitHub actionが提供されています。

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

GitHub actionスクリプトの例は以下の通りです。

  • フォーマットとビルドチェック
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
  • 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

このために 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

ソースマップ

ソースマップはSystemVerilogからVerylへ位置情報を追跡するために使用されるファイルです。このファイルによって、SystemVerilogのファイルパス・行・列をVeryl上の位置に変換することができます。

デフォルトではVerylは生成されたSystemVerilogと同じディレクトリに拡張子 .sv.map のソースマップを生成します。ソースマップの生成は Veryl.tomlsourcemap_target フィールドで制御することができます。

ソースマップの形式は Source Map Revision 3 に従います。生成されたコードとマップのリンク方法はJavaScriptのものとほとんど同じですが、相対パスのみが使われます。

//# sourceMappingURL=<relative path>

従って、上記のコメントがSystemVerilogファイルの末尾にあればソースマップが利用可能です。

sourcemap-resolver

sourcemap-resolver は Veryl コンパイラに同梱されており、任意のテキストファイルに以下のような注釈を付けることができます。

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

最初の行が元のテキストで、2行目が sourcemap-resolver により追加された行です。使用例は以下の通りです。

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

verylup

verylup はVerylの公式ツールチェーンインストーラです。ツールチェーンの更新や切り替えを簡単に行うことができます。

ツールチェーンの更新

以下のコマンドはVerylツールチェーンとverylupを最新版に更新します。

verylup update

特定のツールチェーンをインストール

もし特定のバージョンのVerylを使用したい場合は、verylup install を使うことができます。

verylup install 0.12.0

インストール後、veryl コマンドで + によるバージョン指定が可能になります。

veryl +0.12.0 build

ディレクトリ毎のツールチェーンオーバーライド

もし特定にディレクトリで特定のバージョンのVerylを使用したい場合は、verylup override を使うことができます。

verylup override set 0.12.0

verylup override はVerylプロジェクト内の任意のディレクトリで実行できます。このコマンドを実行すると、そのプロジェクトのデフォルトのツールチェーンは 0.12.0 になります。

オフラインインストール

インターネットアクセスのない環境にインストールしたい場合は、オフラインインストールが利用できます。オフラインインストールの手順は以下の通りです。

  • 最新のツールチェーンパッケージをVeryl リリースページからダウンロードする
  • 以下のようにveryl setup--pkg 指定付きで実行する
verylup setup --offline --pkg veryl-x86_64-linux.zip

ツールチェーンの更新やインストールもセットアップと同様に --pkg 指定が必要です。

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

Veryl開発者向け

Veryl の開発者向けに、特別なツールチェーン local が用意されています。verylup install local をローカルのVerylリポジトリ上で実行すると、ビルドしたツールチェーンを local ツールチェーンとしてインストールします。local ツールチェーンが存在するときはデフォルトのツールチェーンになります。

// ローカルのVerylリポジトリでビルドしたツールチェーンをインストール
verylup install local

// ビルドしたツールチェーンを使う
veryl build

// 最新のツールチェーンを使う
veryl +latest build

補遺

構文

Veryl のパーサはパーサジェネレータ parolを使っています。以下の parol の構文定義が正式な構文です。


%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
           ;

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

ClockDomain: BackQuote Identifier;

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

StatementBlock: LBrace { StatementBlockItem } RBrace;

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 [ 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 };

セマンティックエラー

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