ハードウェア記述言語 Veryl

Veryl は SystemVerilog をベースに設計されたハードウェア記述言語であり、以下のような特徴があります。
最適化された構文
Verylは、SystemVerilogの経験者にとって親しみやすい基本構文に基づきながら、論理設計に最適化された構文を採用しています。この最適化には、たとえば合成可能性の保証やシミュレーション結果の一致の保証、頻出する定型文を簡素化する多数の構文などの提供が含まれます。このアプローチにより、学習の容易さ、設計プロセスの信頼性と効率の向上、およびコードの記述の容易さが実現されます。
相互運用性
VerylはSystemVerilogとの相互運用性を考慮して設計されており、既存のSystemVerilogコンポーネントやプロジェクトとの組み合わせや部分的な置き換えをスムーズに行うことができます。さらに、VerylからトランスパイルされたSystemVerilogソースコードは、その高い可読性により、シームレスな統合やデバッグを可能にします。
生産性
Verylはパッケージマネージャ、ビルドツール、そしてVSCode、Vim、Emacsなどの主要なエディタに対応するリアルタイムチェッカー、自動補完機能、自動フォーマッタなど、豊富な開発支援ツールを備えています。これらのツールは、開発プロセスを加速し、生産性を大幅に向上させることができます。
これらの特性により、Verylは設計者が高品質なハードウェア設計をより効率的かつ生産的に行うための強力なサポートを提供します。
特徴
この章ではVerylの特徴的な機能をわかりやすい例とともに紹介します。
- リアルタイム診断
- 自動フォーマット
- 組み込みテスト
- 論理合成
- 依存関係管理
- ジェネリクス
- 型推論
- クロックドメインアノテーション
- 末尾カンマ
- クロックとリセットの抽象化
- ドキュメンテーションコメント
always_ffでの複合代入演算子- 独立した名前空間を持つenumバリアント
- ビット連結における
repeat if/case式- 範囲
for/inside/outside msb記法let文<>演算子- 名前付きブロック
- 可視性制御
リアルタイム診断
変数の未定義・未使用・未代入といった問題はエディタでの編集中にリアルタイムに通知されます。次の例では、未使用変数として通知された変数に _ プレフィックスを付加することで未使用であることを明示し、警告を抑制しています。
自動フォーマット
エディタと連携した自動フォーマット機能のほか、コマンドラインでのフォーマットやCIでのフォーマットチェックも可能です。
組み込みテスト
テストは Veryl で直接記述し、veryl test コマンドで実行できます。基本となるのは ネイティブテスト で、Veryl 自体で記述したテストベンチを組み込みシミュレータで実行します。SystemVerilog を埋め込んだり外部フレームワークに依存したりする必要はありません。外部シミュレータのインストールは不要です。
#[test(test_example)]
module test_example {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
initial {
rst.assert();
clk.next (10);
$finish ();
}
}
外部の RTL シミュレータが必要な場合は、SystemVerilog や cocotb で書いたテストコードを Veryl コードに埋め込み、同じ veryl test コマンドで実行することもできます。
#[test(test1)]
embed (inline) sv{{{
module test1;
initial begin
assert (0) else $error("error");
end
endmodule
}}}
論理合成
Veryl はプロジェクトに対して簡易的な論理合成を行い、概算の面積・タイミング・電力を報告できます。これは設計検討中に素早くフィードバックを得るためのツールであり、本格的な合成フローの代替を意図したものではありません。
レポートは1行のサマリで始まり、続いて面積・タイミング・電力の詳細が示されます。
synth: TopModule — 123 gates, 17 FFs
library: sky130_fd_sc_hd ...
summary:
area: 1234.56 um² (comb 1000.00, seq 134.56, mem 100.00)
timing: 2.345 ns 8 levels in_dat → out_dat
power: 0.1234 mW (leak 0.0123 mW, dyn 0.1111 mW)
@ f_clk = 100 MHz, activity = 0.10
依存関係管理
Verylには依存関係の管理機能が組み込まれており、プロジェクト設定に以下のようにライブラリのリポジトリパスとバージョンを追加するだけで、簡単にライブラリを組み込むことができます。
[dependencies]
veryl_sample = {git = "https://github.com/veryl-lang/veryl_sample", version = "0.1.0"}
ジェネリクス
ジェネリクスによるコード生成は従来のパラメータオーバーライドよりさらに再利用性の高いコードを記述することができます。以下の例のような関数のパラメータだけでなく、インスタンスのモジュール名や構造体定義の型名もパラメータ化することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
型推論
var、let、const の型注釈は、右辺や最初の代入から型が導出できる場合に省略できます。関数呼び出しのジェネリック引数も、実引数の宣言型から推論できます。ポートや関数のシグネチャはインターフェースの可読性を保つために、引き続き明示的な型指定が必要です。
| SystemVerilog | Veryl |
|---|---|
|
|
クロックドメインアノテーション
モジュール内に複数のクロックがある場合、明示的なクロックドメインアノテーションとクロックドメイン境界への unsafe (cdc) ブロックが必要です。Veryl コンパイラは意図しないクロックドメインクロッシングをエラーとして検出し、明示的な unsafe (cdc) ブロックによりレビューが容易になります。
| SystemVerilog | Veryl |
|---|---|
|
|
末尾カンマ
末尾カンマは、リストの最後の要素の後ろにカンマが置かれる構文です。これにより、要素の追加や削除が容易になり、バージョン管理システムにおける不必要な差異を減らすことができます。
| SystemVerilog | Veryl |
|---|---|
|
|
クロックとリセットの抽象化
クロックの極性やリセットの極性と同期性を構文上指定する必要はなく、ビルド時の設定で指定することができます。これにより同じVerylのコードからASIC向けの負極性・非同期リセットとFPGA向けの正極性・同期リセットのそれぞれのコードを生成することができます。
さらに、明示的な clock と reset 型により、レジスタへのクロック・リセット接続が正しく行われているかどうかを確認することができます。モジュール内にクロックとリセットが1つだけの場合、レジスタへの接続を省略することもできます。
| SystemVerilog | Veryl |
|---|---|
|
|
ドキュメンテーションコメント
ドキュメンテーションコメントとしてモジュールの説明を書いておくとドキュメントを自動生成することができます。単なるテキストだけでなく、以下のフォーマットを使用することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
always_ff での複合代入演算子
ノンブロッキング専用の代入演算子はなく、always_ff 内ではノンブロッキング代入が、 always_comb 内ではブロッキング代入が推論されます。そのため always_ff 内でも always_comb 内と同様に様々な複合代入演算子を使用することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
独立した名前空間を持つenumバリアント
enumのバリアントはenum毎に独立した名前空間を持っており意図しない名前の衝突を防ぐことができます。
| SystemVerilog | Veryl |
|---|---|
|
|
ビット連結における repeat
ビット連結における繰り返し記述として明示的な repeat 記法を採用し、 複雑な {} の組み合わせより可読性が向上しています。
| SystemVerilog | Veryl |
|---|---|
|
|
if / case 式
三項演算子の代わりに if 式と case 式を採用することで、比較するアイテム数が多い場合の可読性が向上します。
| SystemVerilog | Veryl |
|---|---|
|
|
範囲 for / inside / outside
閉区間 ..= と半開区間 .. を表す記法を導入し、 for 、inside で範囲を統一的に記述できるようにしました。また、inside の逆を意味する outside も導入しました。
| SystemVerilog | Veryl |
|---|---|
|
|
msb 記法
最上位ビットを示す msb 記法により、パラメータから最上位ビットを計算する必要がなくなり、より意図を明確にすることができます。
| SystemVerilog | Veryl |
|---|---|
|
|
let 文
変数宣言と同時に値を束縛する専用の let 文が用意されており、SystemVerilogではサポートされていなかった様々な場所で使用することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
<> 演算子
<> 演算子は2つのインターフェースを接続します。SystemVerilogではインターフェースを接続するためにぞれぞれのメンバーを代入する必要がありましたが、簡単に接続することができるようになります。
| SystemVerilog | Veryl |
|---|---|
|
|
名前付きブロック
変数のスコープを限定するための名前付きブロックを定義することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
可視性制御
pub キーワードの付かないモジュールはプロジェクト外から参照できず、ドキュメントの自動生成にも含まれません。これによりプロジェクト外に公開したいものと内部実装とを区別することができます。
| SystemVerilog | Veryl |
|---|---|
|
|
はじめに
Veryl を使ってみましょう。この章では Veryl のインストール、サンプルプロジェクトの作成とビルドまでを行います。
インストール
Veryl は公式のツールチェーンインストーラ verylup を使ってインストールできます。ツールチェーンのアップデートなど便利な機能があるので verylup の使用を推奨します。
注: インターネットアクセスのない環境にインストールしたい場合は オフラインインストール が利用できます。
要件
git
Veryl は git ベースの依存を取得するために、既定で組み込みの gitoxide バックエンドを使用するため、外部の git コマンドは必須ではありません。gitoxide が失敗した場合 (たとえば対応していない認証方式が必要なときなど) は、Veryl は自動的にシステムの git コマンドへフォールバックするため、git をインストールしておくことを推奨します。バックエンドの選択方法については Git バックエンド を参照してください。
cc
veryl test の既定のネイティブシミュレータバックエンド (--backend=cc) は C を出力し、PATH 上の cc コマンドでコンパイルします。cc が利用できない場合、シミュレータは Cranelift JIT へ透過的にフォールバックするため、必須ではありませんが最高のパフォーマンスを得るためにインストールしておくことを推奨します。バックエンドの選択方法については シミュレータ を参照してください。
verylup のインストール
バイナリのダウンロード
リリースページからダウンロードして、パスの通ったところに展開してください。
Cargo
cargo コマンドからインストールすることもできます。
cargo install verylup
verylup のセットアップ
verylup をインストールした後、以下のコマンドを1回実行してください。最新のツールチェーンをダウンロードし、veryl と veryl-ls コマンドをverylupと同じ場所に作成します。
verylup setup
これで veryl コマンドが使えるようになりました。
エディタ設定
公式には Visual Studio Code、Vim / Neovim、Zed がサポートされています。
Visual Studio Code
Visual Studio Code 向けに Veryl 拡張が提供されています。拡張はファイルタイプの検出とシンタックスハイライト、言語サーバの組み込みを提供します。拡張パネルから “Veryl” で検索するか、以下の URL からインストールしてください。
Veryl extension for Visual Studio Code
Vim / Neovim
Vim / Neovim 向けに Veryl プラグインが提供されています。プラグインはファイルタイプの検出とシンタックスハイライトを提供します。プラグインのインストールと言語サーバの組み込みは以下の URL を参照してください。
Zed
Zed 向けに Veryl 拡張が提供されています。拡張はファイルタイプの検出とシンタックスハイライト、言語サーバの組み込みを提供します。拡張パネルから “Veryl” で検索するか、以下の URL からインストールしてください。
そのほかのエディタ
Veryl は言語サーバを提供しているので、言語サーバをサポートしているエディタ(例えば Emacs)であれば利用できます。
シェル補完
veryl と verylup のシェル補完スクリプトは verylup completion によって提供されます。例えば以下のコマンドはzsh向けの補完スクリプトを生成します。
verylup completion zsh veryl > _veryl
verylup completion zsh verylup > _verylup
サポートされているシェルは以下の通りです。
- Bash
- Elvish
- Fish
- PowerShell
- Zsh
生成されたスクリプトの使い方は各シェルのドキュメントを参照してください。
Hello, World!
プロジェクトを作る
まず始めに、新しい Veryl プロジェクトを作りましょう。
veryl new hello
コマンドを実行すると、以下のディレクトリとファイルが作成されます。もし git コマンドが利用できれば、ディレクトリは Git リポジトリとして初期化され、デフォルトの .gitignore ファイルも追加されます。
$ veryl new hello
[INFO ] Created "hello" project
$ cd hello
$ tree
.
├── src
└── Veryl.toml
1 directory, 1 file
Veryl.toml はプロジェクトの設定ファイルです。
[project]
name = "hello"
version = "0.1.0"
[build]
source = "src"
target = {type = "directory", path = "target"}
全設定の説明はこちら。
コードを書く
ソースコードはプロジェクトディレクトリ内のどこに書いても構いません。これは 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.veryl
├── target
│ ├── hello.sv
│ └── hello.sv.map
├── Veryl.lock
└── Veryl.toml
3 directories, 6 files
デフォルトでは SystemVerilog のコードは Veryl のコードと同じディレクトリに生成されます。つまり src/hello.sv です。
module hello_ModuleA;
initial begin
$display("Hello, world!");
end
endmodule
//# sourceMappingURL=hello.sv.map
さらに、生成されたコードのファイルリスト hello.f も生成されます。これは SystemVerilog コンパイラで使用できます。Verilator で使用するには以下のようにします。
$ verilator --binary -f hello.f
生成されたコードを片づける
生成されたコードは veryl clean コマンドで削除することができます。
$ veryl clean
[INFO ] Removing file ([path to hello]/src/hello.sv)
[INFO ] Removing file ([path to hello]/src/hello.sv.map)
[INFO ] Removing dir ([path to hello]/dependencies)
[INFO ] Removing file ([path to hello]/hello.f)
コード例
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 と同様に、module、interface、package を持ちます。この章ではそれらの例を示します。
モジュール
// モジュール定義
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[0];
// リセット付き 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 のソースコードはいくつかの module、interface、package からなります。
module ModuleA {}
module ModuleB {}
interface InterfaceA {}
package PackageA {}
トランスパイルされたコードにおける module、interface、package の名前には先頭にプロジェクト名が付きます。このサンプルコードでは 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 ** 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: logic<32> = 1'0;
const a1: logic<32> = 2'1;
const a2: logic<32> = 3'x;
const a3: logic<32> = 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値(0、1、x、z)のデータ型です。幅は logic のあとの <> で指定できます。<X, Y, Z,,,> のように多次元指定も可能です。
module ModuleA {
let _a: logic = 1;
let _b: logic<10> = 1;
let _c: logic<10, 10> = 1;
}
幅指定可能な2値データ型
bit は2値(0、1)のデータ型です。幅は logic のあとの <> で指定できます。<X, Y, Z,,,> のように多次元指定も可能です。
module ModuleA {
let _a: bit = 1;
let _b: bit<10> = 1;
let _c: bit<10, 10> = 1;
}
型修飾子
logic と bit 型には以下の型修飾子を付けることができます。
signed: MSBは符号ビットとして扱われるtri: トライステート型
module ModuleA {
let _a: signed logic<10> = 1;
let _b: tri logic <10> = 1;
let _c: signed bit <10> = 1;
let _d: tri bit <10> = 1;
}
整数型
整数型にはいくつかの種類があります。
u8:8ビットの符号なし整数u16:16ビットの符号なし整数u32:32ビットの符号なし整数u64:64ビットの符号なし整数i8:8ビットの符号付き整数i16:16ビットの符号付き整数i32:32ビットの符号付き整数i64:64ビットの符号付き整数
module ModuleA {
let _a: u8 = 1;
let _b: u16 = 1;
let _c: u32 = 1;
let _d: u64 = 1;
let _e: i8 = 1;
let _f: i16 = 1;
let _g: i32 = 1;
let _h: i64 = 1;
}
正の整数型
正の整数型にはいくつかの種類があります。
p8:8ビットの正の符号なし整数p16:16ビットの正の符号なし整数p32:32ビットの正の符号なし整数p64:64ビットの正の符号なし整数
正の整数型はコンパイル時に代入値がゼロより大きいことを検証します。正のリテラル値および正の値に評価される式のみが受け入れられます。
module ModuleA {
let _a: p8 = 1;
let _b: p16 = 100;
let _c: p32 = 65536;
let _d: p64 = 1000000;
}
浮動小数点数型
浮動小数点数型にもいくつかの種類があります。
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 型の変数は param か const としてのみ定義可能です。
module ModuleA {
const a: type = logic;
const b: type = logic<10>;
const c: type = u32;
}
ブーリアン型
bbool と lbool は ブーリアンを表す bit<1> と logic<1> の型エイリアスです。1'b1 と 1'b0 を表す true と false も使用できます。
module ModuleA {
const A: bbool = true;
const B: bbool = false;
const C: lbool = true;
const D: lbool = false;
}
ユーザ定義型
構造体
struct は複合データ型です。いくつかのフィールドを持つことができ、. 演算子を通してアクセスできます。
module ModuleA {
struct StructA {
member_a: bit ,
member_b: bit<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 の型が省略された場合、デフォルトの型として logic を使い、幅はバリアントから自動的に推論します。logic<_> と bit<_> は型のみ明示的に指定する場合に利用できます。
module A {
// 型: logic
// 幅: 2
enum EnumA {
member_a,
member_b,
member_c = 3,
}
// 型: bit
// 幅: 3
enum EnumB: bit<_> {
member_d = 4,
member_e,
member_f,
}
}
列挙型エンコーディング
デフォルトでは各バリアントの値が省略されたときは0から順に割り当てられます。この割り当てのエンコードを指定したい場合は、#[enum_encoding] アトリビュートを指定できます。使用できるエンコードは以下の通りです。
sequentialonehotgray
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: 負極性の同期リセット型
特別な要件がなければ、コードの再利用を高めるため clock と reset の使用を推奨します。
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;
}
}
}
デフォルトクロックとリセット
クロックが複数あるものの、 always_ff では単一のクロックしか使われない場合があります。このような場合に default 型修飾子を使ってデフォルトのクロックとリセットを明示することができます。
module ModuleA (
i_clk : input clock,
i_clk_en: input logic,
) {
let clk: '_ default clock = i_clk & i_clk_en;
var a: logic;
always_ff {
a = 0;
}
}
式
この章では式について説明します。式は変数や演算子、関数呼び出しなどを組み合わせたもので、評価して値を得ることができます。
演算子の優先順位
式内での演算子の優先順位は SystemVerilog とほとんど同じです。
| 演算子 | 結合性 | 優先順位 |
|---|---|---|
() [] :: . | 左 | 高い |
+ - ! ~ & ~& | ~| ^ ~^ (単項) | 左 | |
** | 左 | |
* / % | 左 | |
+ - (二項) | 左 | |
<< >> <<< >>> | 左 | |
<: <= >: >= | 左 | |
== != ==? !=? | 左 | |
& (二項) | 左 | |
^ ~^ (二項) | 左 | |
| (二項) | 左 | |
&& | 左 | |
|| | 左 | |
= += -= *= /= %= &= ^= |= <<= >>= <<<= >>>= | なし | |
{} inside outside if case switch | なし | 低い |
関数呼び出し
関数は function_name(argument) の形式で呼び出すことができます。$clog2 のような SystemVerilog のシステム関数も使えます。
package PackageA {
function FunctionA (
a: input logic,
b: input logic,
) {}
}
module ModuleA {
let _a: logic = PackageA::FunctionA(1, 1);
let _b: logic = $clog2(1);
}
名前付き引数
多くの引数を持つ関数では、名前付き引数による関数呼び出しが便利です。名前付き引数と位置引数を混在させることはできません。
module ModuleA {
function FunctionA (
a: input logic,
b: input logic,
c: input logic,
d: input logic,
) {}
let _a: logic = FunctionA(
a: 1,
b: 1,
c: 1,
d: 1,
);
// 位置引数と名前付き引数の混在はエラー
//let _a: logic = FunctionA(
// 1,
// 2,
// a: 1,
// b: 1,
//);
}
連結
{} はビット連結を表します。{} の中では 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 キーワードの後に条件を示す節を置きますが、() で囲む必要はありません。? のあとに条件が真である場合の式を、: のあとに条件が偽である場合の式を書きます。
module ModuleA {
let a: logic<10> = 1;
var b: logic<10>;
assign b = if a == 0 ? 1 : if a >: 1 ? 2 : 3;
}
Case / Switch
もう一つの条件式が case です。case は 値: 式 という形式の条件を複数持ちます。もし case キーワードの後の式と条件の左側の値が一致すれば、その条件の右側の式が返されます。値としては ..= のような範囲も指定できます。さらに x と z はワイルドカードとして扱われ、任意のビットにマッチします。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,
10'b00_0000_011x: 5, // 6 か 7 にマッチ
default : 6,
};
}
switch は case のもう一つの形式です。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 in 0..10 {}
for _j in 0..=10 {}
}
}
msb / lsb
msb と lsb は [] によるビット選択で使用できます。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 は 指定された式が {} で与えられた条件内にあるかどうかを調べます。条件は単一の式または範囲を指定できます。条件を満たすとき inside は 1 を、そうでなければ 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};
}
型キャスト
as は型キャスト演算子です。基数付きあるいは基数なしの数値で指定するビット幅やユーザ定義型の型名をオペランドにとることができます。
module ModuleA {
var a: EnumA ;
var b: logic<2>;
let x: logic = 0;
enum EnumA: logic {
A,
B,
}
assign a = x as EnumA;
assign b = x as 2;
}
構造体コンストラクタ
構造体を初期化するために各メンバーにそれぞれ代入する代わりに構造体コンストラクタを使用することができます。特に const は各メンバーに代入することができないためコンストラクタによる初期化が必要です。
..default 指定子は未指定のメンバーのためのデフォルト値を指定することができます。
module ModuleA {
struct Param {
a: bit<10>,
b: bit<10>,
}
const p: Param = Param'{a: 10, b: 10,};
const q: Param = Param'{
a: 1,
..default(0) // すなわち `b: 0`
};
}
文
この章では文について説明します。文は always_ff や always_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>;
always_comb {
b = a + 1;
}
always_ff (i_clk) {
c += a + 1;
d -= 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
case と switch は文として使うこともできます。各アームの右辺が文になる点を除けば 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;
}
}
}
cond_type アトリビュート
SystemVerilogにおける unique unique0 priority を指定するために、cond_type アトリビュートを使うことができます。これらのアトリビュートは case あるいは if 文に付けることができます。
unique: アイテムは重複しない。マッチするアイテムがなければエラー。unique0: アイテムは重複しない。マッチするアイテムがなくてもエラーではない。priority: 最初にマッチしたアイテムが使われる。マッチするアイテムがなければエラー。
module ModuleA {
let a: logic<10> = 1;
var b: logic<10>;
always_comb {
#[cond_type(unique)]
case a {
0: b = 1;
1: b = 2;
}
}
}
これらのアトリビュートは合成時により積極的な最適化を可能にしますが、期待される条件を満たさない場合に合成結果が不正になる可能性があります。そのためデフォルトではアトリビュートは無視され、以下の設定がある場合のみ出力されます。
[build]
emit_cond_type = true
左辺でのビット連結
case/switchの左辺でビット連結を使用する場合、文が1つだけであっても {} で囲む必要があります。
module ModuleA {
let a: logic<10> = 1;
var b: logic<10>;
var c: logic<10>;
always_comb {
case a {
// これは禁止
//0: {b, c} = 1;
//default: {b, c} = 2;
0: {
{b, c} = 1;
}
default: {
{b, c} = 2;
}
}
}
}
for
for 文は繰り返しを表します。in キーワードの前にループ変数を、後に範囲を書きます。
break を使ってループを中断することもできます。
module ModuleA {
var a: logic<10>;
always_comb {
for i in 0..10 {
a = i;
if i == 5 {
break;
}
}
}
}
in キーワードの後に rev キーワードを指定することで、ループを降順にすることができます。
module ModuleA {
var a: logic<10>;
always_comb {
for i in rev 0..10 {
a = i;
if i == 5 {
break;
}
}
}
}
return
return 文は関数からの戻りを示します。return キーワードの後の式は関数の戻り値です。
module ModuleA {
function FunctionA () -> u32 {
return 0;
}
}
let
let 文はある名前に値を束縛します。これは always_ff 、 always_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_reset は always_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_ff 宣言の左辺には連結も使用することができます。
module ModuleA (
i_clk: input clock,
) {
var a: logic<10>;
var b: logic<10>;
always_ff {
{a, b} = 1;
}
}
組み合わせ回路
always_comb 宣言で代入される変数は組み合わせ回路を表します。組み合わせ変数はブロッキング代入セマンティクスを使用し、入力が変化したときに再評価されます。評価の詳細については実行モデルを参照してください。
module ModuleA {
let a: logic<10> = 1;
var b: logic<10>;
always_comb {
b = a + 1;
}
}
always_comb 宣言の左辺には連結も使用することができます。
module ModuleA {
var a: logic<10>;
var b: logic<10>;
always_comb {
{a, b} = 1;
}
}
assign
assign 宣言は変数に式を代入します。assign 宣言は組み合わせロジックとして扱われます。評価のセマンティクスについては実行モデルを参照してください。
module ModuleA {
var a: logic<10>;
assign a = 1;
}
assign 宣言の左辺には連結も使用することができます。
module ModuleA {
var a: logic<10>;
var b: logic<10>;
assign {a, b} = 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);
}
}
インターフェースのmodportは引数の型としても使用できます。与えられたmodportはSystemVerilog生成時にVerilogポートに展開されます。
interface InterfaceA::<W: u32> {
var ready: logic ;
var valid: logic ;
var data : logic<W>;
modport master {
ready: input ,
valid: output,
data : output,
}
modport slave {
..converse(master)
}
}
module ModuleA {
inst a_if: InterfaceA::<8>;
inst b_if: InterfaceA::<8>;
function FunctionA (
a_if: modport InterfaceA::<8>::slave ,
b_if: modport InterfaceA::<8>::master,
) {
a_if <> b_if;
}
always_comb {
FunctionA(a_if, b_if);
}
}
グローバル関数
プロジェクト名前空間レベル(module、interface、package の外側)で定義された関数はグローバル関数と呼ばれます。グローバル関数がモジュールから呼び出されると、生成される SystemVerilog では呼び出し元モジュールの名前空間に展開されます。
グローバル関数はジェネリックにすることができ、pub を付与することでプロジェクト間で公開することもできます。
pub function add::<W: u32> (
a: input logic<W>,
b: input logic<W>,
) -> logic<W> {
return a + b;
}
module ModuleA #(
param WIDTH: u32 = 8,
) (
i_a: input logic<WIDTH>,
i_b: input logic<WIDTH>,
o_c: output logic<WIDTH>,
) {
assign o_c = add::<WIDTH>(i_a, i_b);
}
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_variableunassign_variablemissing_reset_statementmissing_port
ifdef/ifndef/elsif/else アトリビュート
ifdef と ifndef アトリビュートは定義された値によってコードブロックを有効にするかどうかを制御するために使用することができます。さらに、ifdef と ifndef のついたコードブロックに続けてオプションとして elsif と else アトリビュートの付いたブロックを書くこともできます。
以下の例はこれらのアトリビュートの使用方法と、各コードブロックが定義された値によって有効になる様子を示しています。
ifdef/elsif/elseの順に宣言されたアトリビュート- もし
DEFINE_Aが定義されていれば、#[ifdef(DEFINE_A)]のついたコードブロック(コードブロックa)が有効になり、#[ifndef(DEFINE_B)]と#[else]のついたコードブロック(コードブロックbとc)は無効になります。 DEFINE_Aが定義されておらず、DEFINE_Bが定義されていれば、#[elfif(DEFINE_B)]のついたコードブロック(コードブロックb)が有効になり、#[ifndef(DEFINE_A)]と#[else]のついたコードブロック(コードブロックaとc)は無効になります。DEFINE_AとDEFINE_Bが定義されていなければ、#[else]のついたコードブロックが有効になり、#[ifndef(DEFINE_A)]と#[elsif(DEFINE_B)]のついたコードブロック(コードブロックaとb)は無効になります。
- もし
ifndef/elseの順に宣言されたアトリビュート- もし
DEFINE_Dが定義されていなければ、#[ifndef(DEFINE_D)]のついたコードブロック(コードブロックd)が有効になり、#[else]のついたコードブロック(コードブロックe)は無効になります。 DEFINE_Dが定義されていれば、#[else]のついたコードブロック(コードブロックe)が有効になり、#[ifndef(DEFINE_D)]のついたコードブロック(コードブロックd)は無効になります。
- もし
module ModuleA {
#[ifdef(DEFINE_A)]
{
// コードブロック a
let _a: logic<10> = 1;
}
#[elsif(DEFINE_B)]
{
// コードブロック b
let _a: logic<10> = 2;
}
#[else]
{
// コードブロック c
let _a: logic<10> = 3;
}
#[ifndef(DEFINE_D)]
{
// コードブロック d
let _b: logic<10> = 4;
}
#[else]
{
// コードブロック e
let _b: logic<10> = 5;
}
}
生成されたコードにおける末尾カンマ周りの複雑な調整を回避するため、カンマ区切りリストの最後のアイテムにifdefをつけることは禁止されています。
expand アトリビュート
expand アトリビュートが設定されているとき、modport のような構造化されたポートはVerilog のポートに展開されます。合成ツールによってはトップモジュールがそのようなポートを含んではならない場合があり、そのような場合にこのアトリビュートを使うことができます。使用可能な引数は以下の通りです。
modport: ポート方向がmodportのポートを展開する
interface InterfaceA::<W: u32> {
var ready: logic ;
var valid: logic ;
var data : logic<W>;
modport master {
ready: input ,
valid: output,
data : output,
}
modport slave {
ready: output,
valid: input ,
data : input ,
}
}
#[expand(modport)]
module ModuleA (
slave_if : modport InterfaceA::<8>::slave [4],
master_if: modport InterfaceA::<8>::master [4],
) {
for i in 0..4 :g {
connect slave_if[i] <> master_if[i];
}
}
module ModuleB {
inst a_if: InterfaceA::<8> [4];
inst b_if: InterfaceA::<8> [4];
inst u: ModuleA (
slave_if : a_if,
master_if: b_if,
);
}
align アトリビュート
align アトリビュートはフォーマッタの垂直方向の整列を制御することができます。number が align の引数として指定されたとき、全ての数値は整列されます。identifier も使用可能です。
module ModuleA {
let a : logic<32> = 1;
let aa : logic<32> = 1;
let aaa: logic<32> = 1;
let _b: logic = {
a[0] repeat 1, a[0] repeat 1, aa[1] repeat 8, aa[1] repeat 8, aaa[2] repeat 16, aaa[2] repeat 16, a[0] repeat 1,
aa[1] repeat 8, aaa[2] repeat 16, a[0] repeat 1,
};
#[align(number, identifier)]
let _c : logic = {
a [0 ] repeat 1 , a [0 ] repeat 1 , aa [1 ] repeat 8 , aa [1 ] repeat 8 , aaa[2 ] repeat 16,
aaa[2 ] repeat 16, a [0 ] repeat 1 , aa [1 ] repeat 8 , aaa[2 ] repeat 16, a [0 ] repeat 1 ,
};
}
fmt アトリビュート
fmt アトリビュートはフォーマットの方法を制御することができます。以下の引数がサポートされています。
compact: 改行なしのコンパクトなフォーマット
module ModuleA {
#[fmt(compact)]
{
inst u1: $sv::Module #( A: 1, B: 2 ) ( x: 1, y: _ );
inst u2: $sv::Module #( A: 1, B: 2 ) ( x: 1, y: _ );
inst u3: $sv::Module #( A: 1, B: 2 ) ( x: 1, y: _ );
inst u4: $sv::Module #( A: 1, B: 2 ) ( x: 1, y: _ );
}
}
skip:veryl fmtはモジュール、インターフェース、パッケージをフォーマットしない
生成
宣言や for や if を使って生成することができます。: で示すラベルは生成された複数の宣言を識別するのに必要です。
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;
}
}
}
in キーワードの後に rev キーワードを付けることで、ループを降順にすることができます。
module ModuleA (
i_a: input logic,
o_a: output logic,
i_b: input logic,
o_b: output logic,
) {
var a: logic<4>;
var b: logic<4>;
always_comb {
a[lsb] = i_a;
o_a = a[msb];
}
for i in 0..4 :g_a {
if i != 3 :g {
assign a[i + 1] = a[i];
}
}
always_comb {
b[msb] = i_b;
o_b = b[lsb];
}
for i in rev 0..4 :g_b {
if i != 0 :g {
assign b[i - 1] = b[i];
}
}
}
インスタンス
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>,
) {}
bind 宣言もサポートされており、SystemVerilogのbind宣言に変換されます。SystemVerilogと異なり、ターゲットスコープとしてインスタンスを指定することはできず、モジュールかインターフェースのみ指定することができます。
interface InterfaceA {
var a: logic;
modport mp {
a: input,
}
}
module ModuleA (
i_clk: input clock,
i_rst: input reset,
) {
inst a_if: InterfaceA;
}
module ModuleB (
i_clk: input clock ,
i_rst: input reset ,
a_if : modport InterfaceA::mp,
) {}
module ModuleC {
bind ModuleA <- u0: ModuleB ( i_clk, i_rst, a_if );
}
bind ModuleA <- u1: ModuleB (
i_clk: i_clk,
i_rst: i_rst,
a_if : a_if ,
);
名前付きブロック
{} ブロックにラベルを付けることができます。そのような名前付きブロックは独立した名前空間を持ちます。
module ModuleA {
:labelA {
let _a: logic<10> = 1;
}
:labelB {
let _a: logic<10> = 1;
}
}
インポート
import 宣言は他のパッケージからシンボルをインポートします。モジュール、インターフェース、パッケージの要素としてだけでなくトップレベルにも配置することができます。import 宣言の引数には package::* のようなワイルドカードパターンを使用することができます。
// ファイルスコープインポート
import $sv::SvPackage::*;
package PackageA {
const paramA: u32 = 1;
}
module ModuleA {
import PackageA::*;
import PackageA::paramA;
}
package::{a, b} のように波括弧の中に列挙することで、1つのパッケージから複数のシンボルを一度に import できます。
package PackageA {
const paramA: u32 = 1;
const paramB: u32 = 2;
}
module ModuleA {
import PackageA::{paramA, paramB};
}
インポート宣言によるシンボルのインポートは、そのインポート宣言が置かれた名前空間内の任意の場所から参照できます。
package PackageA {
const WIDTH: u32 = 8;
}
module ModuleA (
i_d: input logic<WIDTH>, // 有効な参照
o_d: output logic<WIDTH>, // 有効な参照
) {
import PackageA::WIDTH;
let d: logic<WIDTH> = i_d; // 有効な参照
assign o_d = d;
}
enumのメンバーも、個別に、波括弧のリストとして、あるいは wildcard により import できます。これはパッケージ内で定義された enum だけでなく、モジュールやインターフェース内でローカルに宣言された enum にも適用されます。
package PackageB {
enum Color: logic<3> {
Red,
Green,
Blue,
White,
Black,
}
}
module ModuleB {
// import a single enum member
import PackageB::Color::Red;
// import multiple enum members
import PackageB::Color::{Green, Blue};
// import all remaining members of the enum
import PackageB::Color::*;
var c: PackageB::Color;
// members can be referenced without qualification
assign c = Green;
}
Connect
あるインターフェースをほかのインターフェースに接続するために各メンバーを代入する代わりに connect 宣言を使用することができます。connect 宣言はインターフェースの全てのメンバーを自動的に接続します。
代入の方向はmodportによって決まります。つまり output メンバーが input メンバーに代入されます。connect の引数がインターフェースインスタンスの場合、方向を決定するためにmodportの指定が必要です。
接続演算子 <> は always_comb 中でも使用することができます。
interface InterfaceA {
var cmd : logic;
var ready: logic;
modport master {
cmd : output,
ready: input ,
}
modport slave {
..converse(master)
}
}
module ModuleA (
mst_if0: modport InterfaceA::master,
slv_if0: modport InterfaceA::slave ,
mst_if1: modport InterfaceA::master,
slv_if1: modport InterfaceA::slave ,
) {
inst bus_if0: InterfaceA;
inst bus_if1: InterfaceA;
connect mst_if0 <> bus_if0.slave;
connect slv_if0 <> bus_if0.master;
always_comb {
mst_if1 <> bus_if1.slave;
}
always_comb {
slv_if1 <> bus_if1.master;
}
}
ブロック
always_comb と always_ff では、block キーワードによって複数の文をグループ化することができます。
module ModuleA {
var a: logic<10>;
var b: logic<10>;
always_comb {
block {
a = 1;
b = 2;
}
}
}
block 宣言は複数の文にアトリビュートを付与するために利用できます。
module ModuleA {
var a: logic<10>;
var b: logic<10>;
always_comb {
#[ifdef(A)]
block {
a = 1;
b = 2;
}
#[else]
block {
a = 3;
b = 4;
}
}
}
モジュール
モジュールはソースコードの最上位コンポーネントの1つです。モジュールはオーバーライド可能なパラメータ、接続ポート、内部ロジックを持ちます。
オーバーライド可能なパラメータは #() 内で宣言できます。それぞれのパラメータ宣言は param キーワードで始まり、識別子、:、パラメータの型、デフォルト値で構成されます。
接続ポートは () 内で宣言できます。それぞれのポート宣言は識別子、:、ポートの方向、ポートの型で構成されます。利用可能なポート方向は以下の通りです。
input:入力ポートoutput:出力ポートinout:双方向ポートmodport:インターフェースのmodportinterface: ジェネリックインターフェース
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;
}
}
}
ポートのデフォルト値
モジュールのポートはデフォルトを持つことができます。デフォルト値を持つポートはインスタンス時に省略することができ、省略されたポートにはデフォルト値が割り当てられます。デフォルト値としては以下の値を取ることができます。
- 入力ポート: リテラル、パッケージ内の
const - 出力ポート:
_(無名識別子)
module ModuleA (
a: input logic ,
b: input logic = 1,
x: output logic ,
y: output logic = _,
) {
assign x = a;
assign y = b;
}
module ModubeB {
inst instA: ModuleA (
a: 1,
// b は省略
x: _,
// y は省略
);
}
ジェネリックインターフェース
ジェネリックインターフェースは特別なポート方向指定です。interface が指定されたとき、そのポートには任意のインターフェースを接続可能です。interface::ModPort のように modport を付けることもできます。この場合、ModPort を持つインターフェースだけが接続できます。
module ModuleA (
bus_if : interface,
slave_if: interface::slave,
) {}
インターフェース
インターフェースはソースコードの最上位コンポーネントの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,
}
}
さらに、import キーワードを付けて指定された関数は modport を通して呼び出すことができます。
interface InterfaceA {
var a: logic;
var b: logic;
function a_and_b -> logic<2> {
return {a, b};
}
modport mp {
a : input ,
b : input ,
a_and_b: import,
}
}
module ModuleA (
ab_if: modport InterfaceA::mp,
) {
let _ab: logic<2> = ab_if.a_and_b();
}
modportのデフォルトメンバー
modportの全てのメンバーを指定する代わりに、以下のようにデフォルトメンバーを指定することができます。
..input: インターフェース内の全ての変数をinputとして追加..output: インターフェース内の全ての変数をoutputとして追加..same(modport_name, ...): 指定されたmodportと同じメンバーを同じ方向で追加(インポートされた関数を含む)..converse(modport_name, ...): 指定されたmodportと同じメンバーを、方向を逆にして追加(インポートされた関数はそのまま維持)
デフォルトメンバーの指定は通常の明示的なメンバーと一緒に使うこともできます。
interface InterfaceA {
var a: logic;
var b: logic;
var c: logic;
modport mp_a {
a: output,
}
modport mp_b {
b: input,
c: input,
}
modport master {
..same(mp_a, mp_b)
}
modport slave {
..converse(mp_a, mp_b)
}
modport monitor {
..input
}
modport driver {
b: input,
..output
}
}
ミックスイン
インターフェースは mixin 宣言によって、他のインターフェースのメンバを取り込めます。ミックスインされたインターフェースの全メンバ(変数、関数、modport)は、そのインターフェースで直接宣言されたかのように展開されます。これは複数のインターフェースで共通の定義を共有するのに便利です。
例えばバスプロトコルは、コマンドチャネルとレスポンスチャネルのような独立したチャネルから構成されることがよくあります。各チャネルを別々のインターフェースとして定義してミックスインすることで、それらのチャネルを複数のバスインターフェースで再利用できます。ジェネリックなインターフェースは、mixin 宣言でジェネリック引数を与えることでミックスインできます。
// Command channel: a master issues read/write commands to a slave
interface Command::<ADDR_WIDTH: u32, DATA_WIDTH: u32> {
var cmd_ready: logic ;
var cmd_valid: logic ;
var cmd_write: logic ;
var cmd_addr : logic<ADDR_WIDTH>;
var cmd_data : logic<DATA_WIDTH>;
modport mp_cmd {
cmd_ready: input ,
cmd_valid: output,
cmd_write: output,
cmd_addr : output,
cmd_data : output,
}
}
// Response channel: a slave returns read data to a master
interface Response::<DATA_WIDTH: u32> {
var rsp_ready: logic ;
var rsp_valid: logic ;
var rsp_data : logic<DATA_WIDTH>;
modport mp_rsp {
rsp_ready: output,
rsp_valid: input ,
rsp_data : input ,
}
}
// A memory bus composed from the command and response channels
interface MemoryBus::<ADDR_WIDTH: u32, DATA_WIDTH: u32> {
mixin Command::<ADDR_WIDTH, DATA_WIDTH>;
mixin Response::<DATA_WIDTH>;
modport master {
..same(mp_cmd, mp_rsp)
}
modport slave {
..converse(mp_cmd, mp_rsp)
}
}
alias interface MemoryBus32 = MemoryBus::<32, 32>;
ミックスインされるインターフェースには以下の制限があります。
- ミックスインの対象はインターフェースでなければなりません。モジュールやプロトタイプはミックスインできません。
- インターフェースは自分自身をミックスインできません。
- 上書き可能なパラメータ(
param/const)を持つインターフェースはミックスインできません。(ジェネリックなインターフェースは、上記のようにジェネリック引数を与えることでミックスインできます。) - 自身が
mixin宣言を持つインターフェースはミックスインできません。(mixinはネストできません。) - メンバ名は、そのインターフェースとミックスインされる全インターフェースを通じて一意でなければなりません。名前の衝突はエラーになります。
インターフェースインスタンスとmodportポートの接続
インターフェースインスタンスとmodportポートは、SystemVerilogと同様に、互換性のあるモジュールポートあるいは generic インターフェースに接続することができます。
interface InterfaceA {
var a: logic;
modport mp {
a: output,
}
}
module ModuleA (
foo_if: modport InterfaceA::mp,
bar_if: modport InterfaceA::mp,
) {
always_comb {
foo_if.a = '0;
bar_if.a = '0;
}
}
module ModuleB (
foo_if: modport InterfaceA::mp,
) {
inst bar_if: InterfaceA;
inst u: ModuleA (
foo_if: foo_if,
bar_if: bar_if,
);
}
パッケージ
パッケージはソースコードの最上位コンポーネントの1つです。パッケージはパラメータや関数などいくつかの宣言をまとめることができます。
パッケージ内の要素にアクセスするには、:: 記号を使って PackageA::ParamA のようにします。
package PackageA {
const ParamA: u32 = 0;
}
SystemVerilogとの相互運用
SystemVerilogの要素にアクセスする場合は $sv 名前空間を使えます。例えば、SystemVerilogソースコードの “ModuleA” は $sv::ModuleA です。Veryl はこれらの要素が実際に存在するかどうかは確認しません。
Verylコンパイラは $sv:: を除いたパス名をそのまま出力します。そのため Verilog や VHDL のような他のHDLのシンボルも参照できます。それぞれのシンボルが解決できるかどうかは実装(シミュレータや合成ツール)に依存します。
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: SystemVerilogpy: Python
embed (inline) sv{{{
module ModuleSv;
endmodule
}}}
inline かつ sv 指定された embed 宣言はモジュール宣言、インターフェース宣言及びパッケージ宣言の中に配置することができます。これはSystemVerilogテストベンチとの統合に使用できます。
#[allow(unused_variable)]
interface bus_monitor_if {
var clk : clock ;
var ready : logic ;
var valid : logic ;
var payload: logic<8>;
embed (inline) sv{{{
clocking monitor_cb @(posedge clk);
input ready;
input valid;
input payload;
endclocking
}}}
}
Verylコード内で定義された識別子は embed コードブロック内に \{ と \} を用いて記述することができます。これらの識別子はコンパイル時に解決され、解決された名前がその場所に挿入されます。
module Module47A {}
module Module47B::<V: u32> {}
module Module47C {
inst u_a: Module47A;
embed (inline) sv{{{
bind u_a \{ Module47B::<32> \} u_b32 ();
bind u_a \{ Module47B::<64> \} u_b64 ();
}}}
}
include 宣言
include 宣言により他言語のファイルを含めることができます。include 宣言の第一引数は embed 宣言と同じです。第二引数はソースコードからの相対ファイルパスです。
include(inline, "module.sv");
組み込みテスト
組み込みテストは #[test(test_name)] アトリビュートでマークすることができます。マークされたブロックはテストとして認識され、 veryl test コマンドによって実行されます。
組み込みテストの記述方法は3種類あります。
- ネイティブテスト
- SystemVerilogテスト
- cocotb テスト
ネイティブテストはVerylの組み込みシミュレータを使用し、SystemVerilogテストとcocotbテストは外部のRTLシミュレータを使用します。veryl test で使用される外部RTLシミュレータについては シミュレータ を参照してください。すべてのテスト種別で、--wave オプションを指定すると波形を生成できます。
#[ignore] 属性を追加することでデフォルトでは無視するテストを指定できます。無視されたテストは --ignored オプションで実行できます。--include-ignored オプションを使うと通常のテストと無視されたテストの両方を実行します。
テスト時のみ有効になるコードパスは、#[ifdef]/#[ifndef] 属性と、テスト用に定義された名前を組み合わせることで有効化できます。名前は Veryl.toml の [test].defines フィールド、または veryl test の --define NAME (-D NAME) オプションで定義できます。両者はマージされ、SystemVerilog テストと cocotb テストでは同じ名前が外部シミュレータにも渡されます。
#[test(test_ignored)]
#[ignore]
module test_ignored {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
initial {
rst.assert();
$finish();
}
}
ネイティブテスト
ネイティブテストでは、SystemVerilogの埋め込みや外部フレームワークを使わずに、Verylで直接テストベンチを記述できます。#[test(test_name)] 属性が付けられたモジュール(embed 宣言なし)がネイティブテストとして扱われます。
以下のテストベンチコンポーネントが利用可能です。
$tb::clock_gen— クロック信号生成器(オプションで#(period: N)パラメータを指定可能)$tb::reset_gen— リセット信号生成器(オプションで#(cycles: N)パラメータを指定可能)$tb::file— 出力ファイルを書き込むためのファイルハンドル$tb::random— 乱数生成器(値の型をジェネリック引数として指定)
組み込みのコンポーネントに加えて、Rust で記述した検証コンポーネントを $comp 名前空間を通じて使用できます。コンポーネントを使う を参照してください。
また、以下のシステム関数が initial ブロック内で使用できます。
$assert(condition)、$assert(condition, format, args...)— シミュレーション中にアサーションを検査します。失敗するとシミュレーションは即座に停止し、テストは失敗として報告されます。formatは$displayと同じ書式のフォーマット文字列で、argsがそこへ埋め込む値を与えます。$assert_continue(condition)、$assert_continue(condition, format, args...)—$assertと同じですが、失敗後もシミュレーションが続行されるため、1回の実行で複数の失敗をまとめて収集できます。テストは引き続き失敗として報告されます。$finish()— シミュレーションを終了
基本的な例
module Counter (
clk: input clock ,
rst: input reset ,
cnt: output logic<32>,
) {
always_ff {
if_reset {
cnt = 0;
} else {
cnt += 1;
}
}
}
#[test(test_counter)]
module test_counter {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
var cnt: logic<32>;
inst dut: Counter (
clk: clk,
rst: rst,
cnt: cnt,
);
initial {
rst.assert();
clk.next(10);
$assert(cnt == 32'd10);
$finish();
}
}
テストベンチメソッド
clock_gen はクロックサイクルを進める next メソッドを提供します。
clk.next()— クロックを1サイクル進めるclk.next(N)— クロックをNサイクル進める
reset_gen はリセットをアサートする assert メソッドを提供します。
rst.assert()— クロックに同期してリセットをアサートrst.assert(duration)— 指定された期間リセットをアサート
リセット期間はインスタンス化時にも設定できます。
#[test(test_reset_cycles_param)]
module test_reset_cycles_param {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen #( cycles: 5 ) ( clk );
// ...
initial {
rst.assert();
// ...
}
}
テストベンチ内の関数呼び出し
clk.next などのテストベンチメソッドはユーザー定義関数から呼び出すことができます。
#[test(test_function_call)]
module test_function_call {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
var cnt: logic<32>;
// inst dut: Counter (clk, rst, cnt);
function step_n (
n: input logic<32>,
) {
clk.next(n);
}
initial {
rst.assert();
step_n(5);
step_n(5);
$assert(cnt == 32'd10);
$finish();
}
}
階層参照
DUT 内部の信号は、階層パスを通して initial ブロックから読み出せます。パスはテストモジュールのインスタンスから始まり、. でネストしたインスタンスを辿ります。観測のために内部信号をトップレベルまで引き出す必要はありません。
module Sub (
clk: input clock ,
rst: input reset ,
din: input logic<4>,
) {
var internal_reg: logic<4>;
always_ff {
if_reset {
internal_reg = 0;
} else {
internal_reg = din + 1;
}
}
}
module Top (
clk: input clock ,
rst: input reset ,
din: input logic<4>,
) {
inst u_sub: Sub ( clk, rst, din );
}
#[test(test_hier)]
module test_hier {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
var din: logic<4>;
inst dut: Top ( clk, rst, din );
initial {
rst.assert();
din = 4'b0001;
clk.next();
$assert(dut.u_sub.internal_reg == 4'h2, "unexpected value");
$display("internal_reg = %h", dut.u_sub.internal_reg);
$finish();
}
}
参照した値は他の式と同じように使えます。例えば $assert や $display の引数、if の条件、ビット選択の対象などです。
以下の制限があります。
- 階層参照はテストモジュールの
initialブロックの中でのみ使えます。always_combのような RTL のコンテキストや、テストモジュールでないモジュールの中で使うとinvisible_identifierになります。 - 関数の本体は RTL からの呼び出しと共有されるため、関数の中では使えません。
- インスタンス配列は階層パスの一部にできません。
階層参照はその信号への参照として数えられません。そのため、階層参照でしか読まれない信号は unused_variable として報告されます。これは #[allow(unused_variable)] 属性で抑制できます。
ファイル出力
$tb::file はネイティブテスト中に出力ファイルを書き込むためのファイルハンドルです。clock_gen や reset_gen とは異なり var で宣言し、initial ブロック内で開いて書き込み、閉じます。
f.open(name)— ファイルnameを書き込み用に開く(既存の内容は切り詰める)f.append(name)— ファイルnameを書き込み用に開く(既存の内容に追記する)f.write(format, args...)—$displayと同じ書式でフォーマットしたテキストを書き込むf.flush()— バッファされた出力をディスクへフラッシュするf.close()— ファイルを閉じる
#[test(test_file)]
module test_file {
var f: $tb::file;
initial {
f.open("out.txt");
f.write("hex=%h dec=%d\n", 8'hAB, 8'd42);
f.close();
f.append("out.txt");
f.write("appended\n");
f.flush();
f.close();
$finish();
}
}
他の $tb::* コンポーネントと同様に、$tb::file は #[test] モジュール内でのみ使用できます。
乱数の生成
$tb::random はネイティブテスト用の乱数生成器です。値の型は宣言時にジェネリック引数として与えます。型は64ビット以下の2値の整数型でなければなりません。例えば u8–u64、i8–i64、bbool、または N が64以下の bit<N> です。4値の型(logic / lbool)、浮動小数点型、64ビットを超える幅は拒否されます。
var で宣言し、initial ブロックの中で使います。
r.seed(value)— シードを設定するr.get()— 要素型の全範囲にわたる一様乱数を返すr.get_range(min, max)— 両端を含む範囲min..=maxの一様乱数を返すr.get_seed()— 現在のシードを返す
#[test(test_random)]
module test_random {
var r: $tb::random::<u32>;
var x: u32 ;
initial {
r.seed(42);
x = r.get();
x = r.get_range(0, 99);
$finish();
}
}
特定のビット幅を使う場合は、先に型に束縛します(bit<N> をジェネリック引数として直接書くことはできません)。
#[test(test_random_width)]
module test_random_width {
gen my_t: type = bit<12>;
var r : $tb::random::<my_t>;
var x : my_t ;
initial {
x = r.get(); // 12-bit random
$finish();
}
}
--seed と [test].seed のどちらも指定しない場合、各生成器は実行ごとにランダムな基準シードから初期化されるため、実行を跨いだ再現性はありません。--seed、[test].seed、または r.seed(...) で明示的にシードを与えると、生成される系列が再現可能になります。他の $tb::* コンポーネントと同様に、$tb::random は #[test] モジュールの中でのみ使えます。
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
}}}
コンポーネントを使う
組み込みの $tb コンポーネントに加えて、Rust で書いた検証コンポーネントをネイティブテストで使えます。コンポーネントは、バスファンクショナルモデル(BFM)・プロトコルチェッカー・ゴールデンモデルなどとして振る舞う Rust の型で、Veryl の組み込みシミュレータが駆動します。
コンポーネントは always_ff プロセスのように振る舞います。クロックエッジで更新され、入力はエッジ直前の値を観測し、出力はフリップフロップと同時にコミットされます。また、メモリイメージの読み込みや値のチェックといった、initial ブロックから呼び出せるゼロ時間メソッドを公開することもできます。
依存パッケージのコンポーネントは、自分で Rust を書かずにテストベンチで使えます。このページではその使い方を説明します。自分でコンポーネントを書いて登録する方法は コンポーネントを書く を参照してください。
Rust のインストール
コンポーネントはコンパイルされた Rust コードとして実行されるため、Rust ツールチェイン のインストールを推奨します。インストールしておけば、コンポーネントはソースからビルドされ、ネイティブコードとして実行されます(重いモデルほど明確に高速です)。
Rust が無くても、依存でビルド済み wasm バイナリとして配布されているコンポーネント(多くは単純なチェッカやモデル)は使えます。cargo が利用できない場合、veryl test は自動的にビルド済みのものを使用します。
コンポーネントを追加する
あるパッケージが検証コンポーネントを宣言していれば、それを依存に追加するだけで $comp::<依存名>::<コンポーネント名> として利用できます。
[dependencies]
axi_vip = {github = "example/axi_vip", version = "1.0.0"}
#[test(test_axi)]
module test_axi {
inst clk: $tb::clock_gen;
inst bus: AxiIf;
inst bfm: $comp::axi_vip::axi_master (
clk ,
bus: bus.master,
);
// ...
}
テストでインスタンス化する
コンポーネントは #[test] モジュール内でのみインスタンス化できます。
クロック駆動コンポーネント
クロック駆動コンポーネントは inst でインスタンス化し、モジュールと同じようにパラメータを #()(ジェネリック引数ではなく)で受け取り、ポートを接続します。
#[test(test_req_ack)]
module test_req_ack {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen ( clk );
var req: logic;
var ack: logic;
inst dut: Peripheral ( clk, rst, req, ack );
inst chk: $comp::my_checker #( LIMIT: 8 ) ( clk, req, ack );
initial {
rst.assert();
req = 1;
clk.next(16);
$finish();
}
}
ポート接続では DUT 内部の信号を階層的に参照することもできます(例: val: dut.u_sub.internal_reg)。そのため、観測のために内部信号をトップレベルまで引き出す必要はありません。インターフェースインスタンスの modport をコンポーネントのインターフェースポートに接続することもできます(bus: bus.master)。コンポーネントが宣言するすべてのポートは接続されている必要があり、接続漏れは解析時に報告されます。
メソッド専用コンポーネント
メソッド専用コンポーネントはポートを持たず、var で宣言します。
#[test(test_golden)]
module test_golden {
var g: $comp::golden ;
var x: logic <8>;
initial {
g.set(42);
x = g.get();
g.check(x);
$finish();
}
}
var 形式では、パラメータをジェネリック引数として、コンポーネントが宣言した順に位置指定で受け取ります。任意の定数式を指定できます。
#[test(test_wide)]
module test_wide {
const W: u32 = 96;
var w: $comp::wide_model::<W> ;
var x: logic <96>;
initial {
w.put(96'h55);
x = w.get();
$finish();
}
}
メソッド呼び出し
メソッドは文の位置および式の中で呼び出せます。
#[test(test_method_call)]
module test_method_call {
var g: $comp::golden ;
var x: logic <8>;
initial {
g.set(42); // 文の位置
x = g.get();
$assert(g.get() + 1 == 43, "expression"); // 式の位置
$finish();
}
}
式の位置での呼び出しは、それを含む文の直前に、独立したゼロ時間のステップとして実行されます。メソッドの引数は位置指定で、名前付き引数はサポートされません。
メソッドは var 形式に限りません。inst したコンポーネントもゼロ時間の呼び出しを受け付けます(例: iss.load("test.elf");)。
乱数シード
乱数を用いるコンポーネントは、インスタンスごとに決定的なシードから乱数を導出するため、実行はそのベースシードから再現できます。既定では veryl test は実行ごとに新しいランダムなベースシードを引き、それを表示します(Test seed: ...)。実行を再現するには、その値を --seed で渡すか、[test] セクションの seed フィールドで固定します。
[test]
seed = 42
コンポーネントを書く
要件に合う既存のコンポーネントがない場合は、Rust で独自のコンポーネントを作成できます。コンポーネントは cargo でビルドされ、Veryl の組み込みシミュレータで駆動されます。テストベンチでの使い方は コンポーネントを使う を参照してください。
コンポーネントの作成
veryl new --component <path> はコンポーネントの雛形を作成し、実行した場所に応じて振る舞いを変えます。
既存の Veryl プロジェクトの中で実行すると、<path> に cargo パッケージを作成し、プロジェクトの Veryl.toml に登録するので、$comp::<name> が最初から解決できます:
$ veryl new --component my_checker
[INFO ] Created "my_checker" component
[INFO ] Registered $comp::my_checker in Veryl.toml
プロジェクトの外で実行すると、cargo パッケージであり Veryl プロジェクトでもある自己完結型のプロジェクトを作成します。すぐに veryl test でき、依存として publish もできます:
$ veryl new --component my_checker
[INFO ] Created "my_checker" component project
[INFO ] Run `veryl test` in my_checker to try it
my_checker/
├── Veryl.toml # クレートをコンポーネントとして登録
├── component/ # cargo パッケージ(crate-type = ["cdylib"])
│ ├── Cargo.toml
│ └── src/lib.rs
└── examples/
└── my_checker.veryl # 使用例。`veryl test` で実行される
コンポーネントの定義
パッケージは veryl-component クレートに依存する cargo クレートで、3 つのマクロで定義します。構造体に付ける #[derive(Component)] がインターフェースを、(トレイトを伴わない)impl ブロックに付ける #[component_impl] が振る舞いを宣言し、veryl_component_export! がコンポーネント名を付けて型をエクスポートします。
use veryl_component::*;
/// `req` から `LIMIT` サイクル以内に `ack` が続くことを検査します。
#[derive(Component)]
pub struct ReqAckChecker {
/// サンプリングクロック。
clk: ClockPort,
req: InputPort,
ack: InputPort,
/// リクエストからアクノリッジまでに許容するサイクル数。
#[param(name = "LIMIT")]
limit: u64,
waiting: u64,
}
#[component_impl]
impl ReqAckChecker {
fn on_clock(&mut self, ctx: &mut SimCtx) -> Result<()> {
if ctx.read(self.ack).as_bool() {
self.waiting = 0;
} else if ctx.read(self.req).as_bool() {
self.waiting += 1;
if self.waiting > self.limit {
ctx.fail(format!("no ack within {} cycles", self.limit));
}
}
Ok(())
}
}
veryl_component_export!("my_checker" => ReqAckChecker);
#[derive(Component)] は構造体のフィールドを次のように分類します。
InputPort/OutputPort型のフィールドはデータポートになります。その幅は接続された信号の幅に従います。幅に制約を課すコンポーネントはon_buildの中でport.width()を使って自分で検査します。ポート名は既定でフィールド名になり、#[port(name = "...")]で上書きできます。ClockPort/ResetPort型のフィールドはクロック・リセットポートになります(常に 1 ビット)。これらはclock/reset型の式で接続されなければならず、on_clock/on_resetフックを発火させるのはこれらのポートだけです。#[param]が付いたフィールドはエラボレーション時のパラメータになります。サポートされる型は整数、bool、String、Valueで、これらのOptionにするとパラメータは省略可能になります。#[param(name = "LIMIT")]は Veryl 側の名前を上書きします。- その他のフィールドは通常の状態変数で、
Defaultで初期化されます。
宣言されたすべてのポートはインスタンス化箇所で接続されている必要があり、接続漏れは解析時エラーになります。
ClockPort フィールドを持つコンポーネントは clocked コンポーネントで、inst でインスタンス化します。method_only コンポーネントは var で宣言します。任意の #[component(kind = ...)] 属性は種別を明示し、フィールドと一致していなければなりません。ClockPort フィールドのない clocked 宣言や、method_only コンポーネントのクロック/リセットポートはコンパイルエラーです。構造体・ポート・パラメータ・メソッドに付けた doc コメントは、veryl doc とエディタのホバーに表示されます。
#[component_impl] は、予約された関数名 on_build、on_init、on_reset、on_clock、on_finish をシミュレーションのフックに、それ以外のすべての関数をテストベンチメソッドにします。
veryl_component_export! はコンポーネント名を付けて型をエクスポートします。このマクロはライブラリの ABI エントリポイントを生成するため、呼び出すのは crate ごとに 1 回だけです(struct ごとではありません)。複数の型をエクスポートするライブラリは、その 1 回の呼び出しにすべてを列挙します。
veryl_component_export!(
"rv_iss" => RvIss,
"monitor" => Monitor,
);
インターフェースポート
インターフェースの modport 全体を接続するには、そのポート群を VerylInterface を derive した struct として一度だけ宣言し、コンポーネントに #[interface] フィールドとして埋め込みます。
#[derive(VerylInterface)]
#[interface(path = "$std::axi4_if", modport = "monitor")]
pub struct AxiMonitorPorts {
awvalid: InputPort, // メンバ `awvalid` に束縛される
awready: InputPort,
// ... 残りの modport メンバ ...
}
#[derive(Component)]
#[component(kind = clocked)]
pub struct AxiChecker {
clk: ClockPort,
rst: ResetPort,
#[interface]
axi: AxiMonitorPorts, // インターフェースポート `axi`: `axi: bus.monitor` と接続する
}
この struct はインターフェース(path)とその modport の1つ(modport)を指定し、各フィールドは同名のインターフェースメンバに束縛されて、コンポーネントからは階層的に読めます(self.axi.awvalid)。名前が Rust の予約語であるメンバは raw identifier(r#in: InputPort)と書きます。
各 #[interface] フィールドはコンポーネントの 1 つのインターフェースポートになり、その名前はフィールド名——テストベンチが接続する名前——です(axi: bus.monitor)。複数宣言することもできます(例えば上流の slave と下流の master を持つブリッジや、同じ struct を 2 回埋め込むデュアルバスのモニタ axi0, axi1)。クロック・リセット・通常のポートとも共存できます。
struct が宣言したメンバはすべて、接続されたインターフェースに存在しなければなりません(欠けていれば解析時エラー)。一方で、コンポーネントが観測するメンバだけを宣言するのは問題ありません。
名前の対応
コンポーネントの周辺にはいくつもの名前が登場しますが、契約に含まれるのは 1 つだけです。
| 名前 | 記述する場所 | 一致すべき相手 |
|---|---|---|
$comp::foo | Veryl ソース | export 名 veryl_component_export!("foo" => ...)。依存のコンポーネントは [dependencies] の名前 vip を前置した $comp::vip::foo |
Rust の型名(ReqAckChecker) | Rust ソース | なし — 契約になるのはエクスポート名の文字列だけ |
| cargo のクレート名 | コンポーネントの Cargo.toml | なし — 成果物は path = から特定される |
[[components]] エントリ
veryl new --component が [[components]] エントリをすでに追加しており、これによりライブラリが export するすべての名前が $comp::<name> として利用可能になります。手で Veryl.toml を編集するのは、別の方法で作成したパッケージを登録する場合や、コミット済みのプリビルドバイナリを指す場合だけです:
[[components]]
path = "my_checker"
# wasm = "prebuilt/my_checker.wasm" # 任意: コミットするビルド済みバイナリ
path— コンポーネントの cargo パッケージへのパス。Veryl.tomlのあるディレクトリからの相対パスwasm— コミットしておくビルド済み wasm バイナリ(オプション)。veryl publishで生成される
パッケージ自身のコンポーネントを動かすテストベンチは、examples ディレクトリ に置くのが最適です。パッケージ自身の veryl test では実行されますが、利用側で解析されることはありません。通常のソースに置かれたテストモジュールも、パッケージが依存として利用される際にはスキップされます。
フック
フックはすべてオプションで、引数として &mut self, ctx: &mut SimCtx(on_build では &mut BuildCtx)を取ります。
on_build— ポートとパラメータの解決後、インスタンスごとに1回呼ばれるon_init—initialブロックの実行前、時刻0に1回呼ばれる。出力への書き込みは初期値になるon_reset— 接続されたResetPortのリセットアサート時に呼ばれるon_clock— 接続されたClockPortの各エッジで呼ばれるon_finish— テスト終了時(正常終了またはfinish/fail経由)に呼ばれる
SimCtx は、フックとメソッドの中で利用できるホストのサービスを提供します。
ctx.read(port)/ctx.write(port, value)— 入力のエッジ直前の値を読む、出力へ書き込む(フリップフロップと同時にコミットされる)ctx.fail(msg)/ctx.finish()— テストを失敗としてマークする / 現在のサイクル終了時の正常終了を要求するctx.log(msg)— テストの出力にメッセージを書き出しますctx.cycle()/ctx.time()/ctx.clock()— サイクル数、シミュレーション時刻、起動したクロックポートctx.fired(port)— 指定したClockPortが今回のフックを発火させたかどうか。複数のクロックに接続されたコンポーネント向けctx.open(path)/ctx.create(path)/ctx.append(path)— ファイル I/O。返されるハンドルはstd::io::Read/Write/Seekを実装しているので、BufReaderやwriteln!などがそのまま使えますctx.trace(var, value)— 波形トレース変数を更新するctx.is_4state()— シミュレーションが4値(X/Z)を扱うかどうか
シミュレーションは既定で2値です。veryl test --4state(または [test].four_state = true)で4値になります。4値実行ではポート値が X/Z マスクを持ち、value.has_x()・value.has_z()・value.unknown_at(bit) で検査でき、出力書き込みもマスクを駆動します。X/Z チェックは ctx.is_4state() でガードしてください(2値実行では観測すべき X/Z がありません)。メソッドの引数と戻り値は常に2値です。
BuildCtx(on_build で利用可能)は追加で ctx.param(name)、ctx.input(name)、ctx.output(name)、ctx.clock(name)、ctx.reset(name)、ctx.seed()、および波形ダンプ用にコンポーネント内部の信号を登録する ctx.trace_var(name, width) を提供します。
ctx.seed() は、ベースとなるテストシードとインスタンスパスから計算される、インスタンスごとに決定的なシードを返します。乱数を用いるコンポーネントは、そのすべての乱数をこの値から導出してください。そうすれば、ベースシードを固定することで実行を正確に再現できます。ベースシードは既定で実行ごとにランダムで(veryl test が表示し、--seed で再現可能)、[test] セクションの seed フィールドで固定できます。
ctx のファイル I/O は可搬な手段です。std::fs を直接使うコンポーネントは native ライブラリとしてのみ動作し、ビルド済み wasm バイナリでは動作しません。
テストベンチメソッド
#[component_impl] ブロック内のフック以外の関数は、テストベンチから呼び出せるゼロ時間のメソッドになります。メソッドは &mut self, ctx: &mut SimCtx に続けて引数を取り、Result<T> を返します。
#[derive(Component)]
#[component(kind = method_only)]
pub struct Golden {
stored: u64,
}
#[component_impl]
impl Golden {
fn set(&mut self, _ctx: &mut SimCtx, value: u64) -> Result<()> {
self.stored = value;
Ok(())
}
fn get(&mut self, _ctx: &mut SimCtx) -> Result<u64> {
Ok(self.stored)
}
fn check(&mut self, ctx: &mut SimCtx, value: u64) -> Result<()> {
if value != self.stored {
ctx.fail(format!("expected {}, got {value}", self.stored));
}
Ok(())
}
}
サポートされる引数の型は &str、String、整数、bool、Value で、サポートされる戻り値の型は ()、整数、bool、Value です。
Value 引数は呼び出し側の式の幅をそのまま持つため、宣言は不要です。Value 返却値は #[ret_width(...)] で幅を宣言しなければなりません(整数の返却型は幅が自明です)。#[ret_width(...)] に指定する式には、整数・パラメータ名・インターフェースポートで修飾したインターフェース定数(axi.DATA_WIDTH_BYTES)・それらの算術式が使えます:
#[derive(Component)]
#[component(kind = method_only)]
pub struct WideModel {
#[param(name = "WIDTH")]
width: u64,
stored: Option<Value>,
}
#[component_impl]
impl WideModel {
fn put(&mut self, _ctx: &mut SimCtx, v: &Value) -> Result<()> {
self.stored = Some(v.clone());
Ok(())
}
#[ret_width(WIDTH)]
fn get(&mut self, _ctx: &mut SimCtx) -> Result<Value> {
Ok(self.stored.clone().unwrap())
}
}
インターフェースに束縛されたコンポーネントは、冗長なパラメータを設ける代わりに接続先のバス幅に追従できます。<port>.<NAME> は、そのポートが接続されたインターフェースから見える定数(インターフェース本体で宣言された定数、またはジェネリックパッケージ引数のメンバ)を参照します:
#[ret_width(axi.DATA_WIDTH_BYTES * 8)]
fn pop_read(&mut self, _ctx: &mut SimCtx) -> Result<Value> {
/* ... */
}
幅はインスタンスごとに解決されるので、同じコンポーネントが 32 ビットのバスでは 32 ビット、128 ビットのバスでは 128 ビットの値を返します。
MockSim によるユニットテスト
veryl_component::testing モジュールは、シミュレータホストのインプロセスな代替である MockSim を提供します。MockSim は同じ SimCtx/BuildCtx API を通じてコンポーネントを駆動するため、Veryl のシミュレータなしに、通常の cargo test でコンポーネントをユニットテストできます。
#[cfg(test)]
mod tests {
use super::*;
use veryl_component::testing::MockSim;
#[test]
fn fails_without_ack() {
let mut sim = MockSim::new()
.param("LIMIT", 2u64)
.input("clk", 1)
.input("req", 1)
.input("ack", 1);
let mut c = sim.build::<ReqAckChecker>().unwrap();
sim.set("req", 1u64);
for _ in 0..3 {
sim.clock(&mut c).unwrap();
}
assert!(sim.failed());
}
}
MockSim はビルダーメソッドでパラメータとポートを宣言し、build でコンポーネントを構築し、set で入力を、clock/reset/init/finish でフックを駆動し、call でメソッドを呼び出し、get、failed、failures、logs、finish_requested を通じて結果を公開します。
インターフェースマニフェストをコミットする
テストベンチの型検査には、コンポーネントのインターフェース(ポート・パラメータ・メソッド)が必要で、その動作は必要ありません。veryl publish はそのインターフェースを、コンポーネントの Cargo.toml と同じ場所にある veryl.manifest.json ファイルに記録し、コンポーネントのソースが変わると再生成します。ソースと一緒にコミットしてください。
マニフェストをコミットしておくと、利用者はコンポーネントをビルドせずに $comp::<name> の使用箇所を解析できます。特に Rust ツールチェインを持たない利用者にとっては、native コンポーネントの場合これが唯一のインターフェース源になります(ビルド済み wasm バイナリは同じインターフェースを埋め込んでいます)。
ビルド済み wasm を publish する
既定では、利用者はコンポーネントを cargo でソースからビルドします。ビルド済み wasm バイナリをコミットしておくと、Rust ツールチェインを持たない利用者でも使えます。[[components]] エントリに wasm = を宣言すると、パッケージはこの配布形態を選択します。
[[components]]
path = "my_checker"
wasm = "prebuilt/my_checker.wasm"
veryl publish はコンポーネントを wasm32-unknown-unknown ターゲット向けにビルドし、宣言されたパスにバイナリを書き出します。プリビルト wasm のビルドにはそのターゲットのインストールが必要で、rustup target add wasm32-unknown-unknown で追加します。プリビルトがソースと一致しなくなると、veryl publish が再生成し、veryl test は警告を報告します。
バックエンドの選択
テスト時にどちらの形式が使われるかは、次のように選択されます。
- cargo が利用可能な場合、コンポーネントはソースからビルドされます。
- そうでない場合、宣言されていればコミット済みのビルド済み wasm が使われます。
[test] セクションの component_backend フィールドで選択を固定できます。
[test]
component_backend = "wasm" # または "native"
wasm に固定するのは、publish する側が利用者の実行するビルド済みバイナリを検証する手段であり(既定則は cargo がある限り wasm を選びません)、信頼しないコンポーネントをサンドボックス内に留める手段でもあります。native に固定すると、cargo が見つからないときに黙って wasm へフォールバックせず、失敗します。
ネイティブと wasm の比較
API とシミュレーションの意味論は両バックエンドで同一で、違いはコンポーネントに何ができるか、そしてどう実行されるかにあります。
| ソースからビルド(ネイティブ) | ビルド済み wasm | |
|---|---|---|
| 利用者に必要なもの | Rust ツールチェイン | 不要 — コミット済みバイナリ |
| 使える API | 任意の Rust crate(ネットワーク、ネイティブライブラリ、GUI、…) | 計算とホストサービスのみ |
| 隔離 | サンドボックスなし | サンドボックスあり。256 MiB メモリ上限、ファイルアクセスは requires(file) でゲート、暴走したフックはトラップ |
| 速度 | ネイティブコード | フル ISS のような計算量の多いモデルでは遅い |
ホストの capability は #[component] 属性で宣言します。requires(file) はホスト経由のファイル入出力を許可し、requires(native) はコンポーネントをネイティブ専用にします。計算とホストサービスを超えるもの — 直接の std::fs、ネットワーク、ネイティブライブラリ、GUI — はコンポーネントをネイティブ専用にし、wasm として配布できなくなります。
制限事項
- メソッドの引数と戻り値は2値で、X/Z は失われます(ポート値は4値実行では X/Z を運びます)。
- メソッドの戻り値は ABI により512ビットに制限されます。
- インスタンス配列、およびコンポーネントポートへの unpacked 配列の接続はサポートされません。
ジェネリクス
ジェネリクスはパラメータオーバーライドでは実現できないアイテムのパラメータ化を可能にします。以下のアイテムがジェネリクスをサポートしています。
- 関数
- モジュール
- インターフェース
- パッケージ
- 構造体
- ユニオン
それぞれのジェネリック定義はジェネリックパラメータ(T のような大文字1文字がよく使われます)を持ち、定義内で識別子や式として配置できます。ジェネリックパラメータはアイテムの識別子の後に ::<> を用いて宣言します。
各ジェネリックパラメータにはコロンの後に T: TypeName のようなジェネリック境界が必要です。ジェネリック境界はどのような値をそのパラメータに渡すことができるかを示します。使用可能なジェネリック境界は以下の通りです。
type: 任意の型を渡すことができるinst: X:Xのインスタンス- 名前付きプロトタイプ、ユーザ定義型、組み込みのデータ型
名前付きプロトタイプは特別なジェネリック境界です。詳細は プロトタイプ を参照してください。
ジェネリクスを使用するためには ::<> を用いて実パラメータを与えます。実パラメータとしては数値リテラルと :: で連結された識別子を使用することができます。
さらに、実パラメータはジェネリクス定義位置から参照できなければなりません。例えば、モジュール名はプロジェクト全体から参照できるので、実パラメータとして使用できます。一方、ローカルパラメータは多くの場合、実パラメータとして使用できません。これはローカルパラメータがジェネリクス定義位置からは参照できない場合に発生します。
例
ジェネリック関数
module ModuleA {
function FuncA::<T: u32> (
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 {}
ジェネリックパッケージ
package PackageA::<T: u32> {
const X: u32 = T;
}
module ModuleA {
const A: u32 = PackageA::<1>::X;
const B: u32 = PackageA::<2>::X;
}
ジェネリック構造体
package PackageA {
type TypeB = u32;
type TypeC = u64;
}
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>;
}
gen 宣言
gen 宣言はジェネリックパラメータから派生する定数や型を定義します。定義された値はジェネリック引数として使用できます。
module ModuleA::<W: u32, T: type> (
a: output logic<W>,
b: output T ,
) {
always_comb {
a = '0;
b = '0;
}
}
module ModuleB::<A: u32, B: u32, C: u32> {
gen W: u32 = A + B;
gen T: type = logic<C>;
var a: logic<W>;
var b: T ;
inst u: ModuleA::<W, T> ( a, b );
}
module ModuleC {
inst u: ModuleB::<1, 2, 3>;
}
ModuleB::<1, 2, 3> では W = 1 + 2 = 3 と T = logic<3> が導出され、ModuleA::<3, logic<3>> がインスタンス化されます。
gen 宣言の右辺式は、その結果の値自体がジェネリック引数として使われるため、実ジェネリック引数と同じ制約を受けます。式に使用できるのは以下のものだけです。
- リテラル
- パッケージ内で定義されたシンボル
- ジェネリックパラメータ
- 他の
gen宣言
ジェネリック引数の推論
ジェネリック関数では、呼び出し引数からジェネリック引数を推論できる場合、::<> 後の実引数を省略できます。詳細は 型推論 を参照してください。
デフォルトパラメータ
ジェネリックパラメータはその後に = を加えることでデフォルト値を指定することができます。呼び出し側でパラメータ指定が省略された場合にデフォルト値が使われます。
module ModuleA {
function FuncA::<T: u32 = 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: u32, U: u32 = 1> (
a: input logic<T>,
) -> logic<T> {
return a + U;
}
// エラー
//function FuncA::<T: u32 = 1, U: u32> (
// 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 による実装が必要です。ModuleC と ModuleD は for 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;
}
インターフェースプロトタイプ
以下の例では、ProtoA が定数 A と変数 raedy/valid/data、関数 ack、modport masterを持つインターフェースプロトタイプです。BUS_IF は ProtoA で制約されているので、それらのメンバーを持つことが保証されており、参照することができます。
proto interface ProtoA {
const WIDTH: u32;
var ready: logic ;
var valid: logic ;
var data : logic<WIDTH>;
function ack() -> logic ;
modport master {
ready: input ,
valid: output,
data : output,
ack : import,
}
}
interface InterfaceA::<W: u32> for ProtoA {
const WIDTH: u32 = W;
var ready: logic ;
var valid: logic ;
var data : logic<WIDTH>;
function ack () -> logic {
return ready && valid;
}
modport master {
ready: input ,
valid: output,
data : output,
ack : import,
}
}
module ModuleA::<BUS_IF: ProtoA> (
bus_if: modport BUS_IF::master,
) {
connect bus_if <> 0;
}
module ModuleB {
inst bus_if: InterfaceA::<8>;
inst u: ModuleA::<InterfaceA::<8>> (
bus_if: bus_if,
);
}
パッケージプロトタイプ
以下の例では、ProtoA が型 data_a と型 data_b を持つパッケージプロトタイプです。PKG は ProtoA で制約されているので、data_a と data_b を持つことが保証されており、それらを参照することができます。
proto package ProtoA {
type data_a;
type data_b;
}
package PackageA::<A: u32, B: u32> for ProtoA {
type data_a = logic<A>;
type data_b = logic<B>;
}
module ModuleA::<PKG: ProtoA> {
let _a: PKG::data_a = 0;
}
プロトタイプの要素
以下の要素はモジュール、インターフェース、パッケージのプロトタイプ内で宣言することができます。
テーブルにどのプロトタイプがどの要素を持てるかをまとめます。
| プロトタイプ | パラメータ | ポート | 定数 | 変数 | 型定義 | 構造体/列挙型/ユニオン | 関数 | エイリアス | modport |
|---|---|---|---|---|---|---|---|---|---|
| モジュール | v | v | |||||||
| インターフェース | v | v | v | v | v | v | |||
| パッケージ | v | v | v | v | v |
パラメータ
パラメータプロトタイプは識別子とデータ型を指定します。デフォルト値の指定は省略できます。
proto module ModuleA #(
param A: u32 = 0,
param B: u32,
);
ポート
ポートプロトタイプは識別子と方向、データ型を指定します。
proto module ModuleA (
i_d: input logic,
o_d: output logic,
);
定数
定数プロトタイプは識別子とデータ型を指定します。ジェネリックなパラメータのプレースホルダとして使用できます。
proto package ProtoPkg {
const WIDTH: u32;
}
package PkgA::<W: u32> for ProtoPkg {
const WIDTH: u32 = W;
}
変数
変数プロトタイプは識別子とデータ型を指定します。これらは var 及び let 宣言で使用することができます。
proto interface ProtoA {
var a: logic;
var b: logic;
}
interface InterfaceA for ProtoA {
var a: logic;
let b: lgoic = 0;
}
型定義
Typedefプロトタイプは型エイリアスの識別子を指定します。ジェネリックなパラメータのプレースホルダとして使用できます。
proto package ProtoPkg {
type data_t;
}
package PkgA::<W: u32> for ProtoPkg {
type data_t = logic<W>;
}
さらに、typedefプロトタイプは右辺に実際の型を指定することができます。これにより他のパッケージで定義された型をそのプロトタイプパッケージに導入し、他のコンポーネントから参照することができます。
package FooPkg {
struct Foo {
foo: logic,
}
}
proto package BarProtoPkg {
type Foo = FooPkg::Foo;
}
package BarPkg for BarProtoPkg {
type Foo = FooPkg::Foo;
}
module ModuleA::<PKG: BarProtoPkg> {
var _foo: PKG::Foo;
assign _foo.foo = 0;
}
module ModuleB {
inst u: ModuleA::<BarPkg>;
}
構造体/列挙型/ユニオン
構造体、列挙型、ユニオンプロトタイプは型の識別子と各メンバーの識別子、データ型を指定します。
proto package ProtoPkg {
struct Foo {
a: logic,
b: logic,
}
enum Bar {
C,
D,
}
union Baz {
e: logic,
f: logic,
}
}
関数
関数プロトタイプは識別子と戻り値のデータ型、引数の方向とデータ型を指定します。
proto package ProtoPkg {
function foo (a: input logic, b: input logic) -> logic;
}
モジュール/インターフェース/パッケージ エイリアス
モジュール、インターフェース、パッケージのエイリアス プロトタイプは識別子とモジュール、インターフェース、パッケージのプロトタイプを指定します。そのエイリアスの実際の型は与えられたプロトタイプに制約されます。
proto module ProtoRamWrapper;
proto package ProtoPkg {
alias module ram: ProtoRamWrapper;
}
package Pkg::<RAM: ProtoRamWrapper> for ProtoPkg {
alias module ram = RAM;
}
module RamWrapper for ProtoRamWrapper {}
module top {
inst u_ram: Pkg::<RamWrapper>::ram;
}
modport
modport プロトタイプはmodport の識別子と各メンバーの識別子と方向を指定します。
proto interface ProtoA {
var a: logic;
var b: logic;
modport mp {
a: input ,
b: output,
}
}
クロックドメインアノテーション
モジュール内に複数のクロックがある場合、'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;
}
インターフェースインスタンスもクロックドメインアノテーションを指定可能です。
module ModuleA {
inst intf: 'a InterfaceA;
}
interface InterfaceA {}
クロックドメインの推論
クロックドメインアノテーションなしで宣言された変数は、ドメインを自動的に推論できます。ドメインは assign 文の右辺または always_ff ブロックのクロックから推論されます。
ポート宣言は公開インターフェースであり明示的であるべきなので、推論が可能な場合でもポート宣言のクロックドメインアノテーションは省略できません。
module ModuleA (
i_clk_a: input 'a clock,
i_rst_a: input 'a reset,
i_dat_a: input 'a logic,
o_dat_a: output 'a logic,
i_clk_b: input 'b clock,
i_rst_b: input 'b reset,
i_dat_b: input 'b logic,
o_dat_b: output 'b logic,
) {
// assignの右辺から 'a と推論
var x: logic;
assign x = i_dat_a;
assign o_dat_a = x;
// always_ffのクロックから 'b と推論
var y: logic;
always_ff (i_clk_b, i_rst_b) {
if_reset {
y = 0;
} else {
y = i_dat_b;
}
}
assign o_dat_b = y;
}
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 にあります。
エイリアス
ジェネリック引数を持つモジュール、インターフェース、パッケージの名前は非常に長くなる場合があります。alias はそのような要素に短い名前をつけることができます。
package PkgA::<X: u32, Y: u32, Z: u32> {}
alias package PkgA123 = PkgA::<1, 2, 3>;
実行モデル
Verylの実行モデルはSystemVerilog (IEEE 1800) のスケジューリングセマンティクスに基づいています。Verylは言語レベルの制約により、標準で定義されている複雑なスケジューリング領域を設計者が理解する必要なく、決定論的なシミュレーションを保証するベストプラクティスを強制します。
Verylの組み込みシミュレータはこのモデルに準拠しています。トランスパイルされたSystemVerilog出力もこれらのセマンティクスを保持するため、IEEE 1800準拠のシミュレータであれば同等の結果が得られます。
変数の分類
変数は代入コンテキストによって分類され、これによりストレージと代入セマンティクスが決定されます。
FF変数(レジスタ)
モジュールレベルの var が always_ff ブロック内で代入されると、フリップフロップ(FF)変数になります。FF変数は二重バッファモデルを使用します:すべてのブロックから読み取れるcurrent値と、ノンブロッキング代入により書き込まれるnext値です。next値はFFコミットフェーズの後にのみcurrent値になります(シミュレーションサイクルを参照)。
組み合わせ変数
モジュールレベルの var が always_comb ブロック内または assign 宣言を通じて代入されると、組み合わせ変数になります。組み合わせ変数への書き込みは即座に反映され(ブロッキング代入セマンティクス)、評価順序における後続の読み取りから参照可能です。
モジュールレベルの let 宣言は var + assign の省略形であるため、同様に組み合わせ変数を生成します。
定数
const 宣言はコンパイル時定数を定義します。定数はコンパイル時に評価され、不変であり、シミュレーションサイクルには参加しません。
ローカル束縛
always_ff または always_comb ブロック内の let 宣言はローカル束縛を作成します。ローカル束縛は always_ff 内であっても常にブロッキング代入セマンティクスを使用します。フリップフロップにマップされることはなく、囲むブロックの外からは参照できません。
module ModuleA (
i_clk: input clock,
i_rst: input reset,
) {
var a: logic<8>;
var b: logic<8>;
// 組み合わせ変数(var + assign の省略形)
let c: logic<8> = a + 1;
// コンパイル時定数
const INIT: logic<8> = 0;
always_ff {
if_reset {
// FF変数(ノンブロッキング)
a = 0;
} else {
// ローカル束縛(ブロッキング、FFではない)
let temp: logic<8> = b + 1;
// FF変数(ノンブロッキング)
a = temp;
}
}
always_comb {
// 組み合わせ変数(ブロッキング)
b = c + 1;
}
}
シミュレーションサイクル
Verylのシミュレーションステップは、厳密な順序で実行される3つのフェーズで構成されます。このモデルはVerylの言語制約により実現された、SystemVerilogスケジューリング領域の簡略化されたビューに対応します。
3つのフェーズ
┌──────────────────────────┐
│ Phase 1: Combinational │ always_comb / assign blocks
│ Settlement │ re-triggered on input changes
│ (SV Active region) │ until all signals stabilize
└────────────┬─────────────┘
│
┌────────────▼─────────────┐
│ Phase 2: Event │ always_ff blocks executed
│ Evaluation │ Reads: current FF values
│ (SV Active + NBA) │ Writes: scheduled as NBA
└────────────┬─────────────┘
│
┌────────────▼─────────────┐
│ Phase 3: FF Commit │ All NBA writes take effect
│ (SV NBA Update) │ Re-triggers Phase 1
└──────────────────────────┘
フェーズ1:組み合わせロジックの収束
すべての組み合わせロジック(always_comb ブロックと assign 宣言)が評価されます。各ブロックはその入力信号に対してセンシティブです:あるブロックが信号を更新すると、その信号に依存するブロックが再評価されます。この伝播はすべての信号が安定するまで続きます。
フェーズ2:イベント評価
トリガーされたイベント(例:クロックエッジ)に対してセンシティブなすべての always_ff ブロックが実行されます。このフェーズでは:
- FF変数からの読み取りはcurrent(コミット前)の値を参照します。
- FF変数への書き込みはノンブロッキングセマンティクスを使用します:値はNBA更新にスケジュールされ、即座には反映されません。
フェーズ3:FFコミット(NBA更新)
フェーズ2からのすべてのノンブロッキング代入がアトミックに反映されます。すべてのFF変数の「next」値が新しい「current」値になります。コミット後、更新された値で組み合わせロジックが再評価されます(フェーズ1に戻る)。
SystemVerilogスケジューリング領域との対応
Verylの言語制約により、SystemVerilogのスケジューリング領域のうち関連するのは2つだけです:
| SV領域 | Verylフェーズ | 何が起こるか |
|---|---|---|
| Active | 組み合わせロジックの収束 | always_comb の評価と伝播 |
| Active | イベント評価(読み取り) | always_ff がcurrent値を読む |
| NBA更新 | イベント評価(書き込み)+ FFコミット | always_ff の書き込みが反映される |
Inactive、Observed、Reactive領域は不要です。Verylはこれらを必要とするコーディングパターン(例:#0 ディレイ、programブロック、同一変数へのブロッキング代入とノンブロッキング代入の混在)を許可しないためです。
代入のセマンティクス
Verylはすべての代入に単一の = 演算子を使用します。実際のセマンティクス — ブロッキングかノンブロッキングか — は代入コンテキストにより自動的に決定されます。これにより、設計者が手動で従うべきSystemVerilogのベストプラクティスが強制されます。
ブロッキング代入
always_comb ブロックと assign 宣言では、= はブロッキング代入を行います。代入された値は同一評価パス内の後続の読み取りから即座に参照可能です。
module ModuleA {
let a: logic<8> = 1;
var b: logic<8>;
var c: logic<8>;
always_comb {
b = a + 1; // b は即座に 2 に更新される
c = b + 1; // c は b の新しい値 (2) を読むため、c = 3
}
}
ノンブロッキング代入
always_ff ブロック内では、モジュールレベル変数に対する = はノンブロッキング代入を行います。代入された値はNBA更新フェーズにスケジュールされ、FFコミットまで反映されません。
module ModuleA (
i_clk: input clock,
) {
var a: logic<8>;
var b: logic<8>;
always_ff {
// a.next = 1(このフェーズ中 current の a は変化しない)
a = 1;
// a.current を読む(a.next ではない)ため、b.next = a.current + 1
b = a + 1;
}
}
always_ff 内のローカル束縛
always_ff 内の let 束縛は常にブロッキングセマンティクスを使用します。ローカル束縛はブロック内の一時的な値であり、フリップフロップにマップされることはありません。
module ModuleA (
i_clk: input clock,
) {
var a : logic<8>;
var result: logic<8>;
always_ff {
// ブロッキング:doubled は即座に利用可能
let doubled: logic<8> = a * 2;
// ノンブロッキング:result.next = doubled + 1
result = doubled + 1;
}
}
SystemVerilogへの対応
| Verylコンテキスト | Veryl構文 | SystemVerilog相当 |
|---|---|---|
always_comb | x = expr; | x = expr;(ブロッキング) |
assign | assign x = expr; | assign x = expr;(continuous assignment) |
always_ff、モジュールレベル変数 | x = expr; | x <= expr;(ノンブロッキング) |
always_ff、ローカル let | let x: T = expr; | x = expr;(ブロッキング) |
always_ff、複合代入 | x += expr; | x <= x + expr;(ノンブロッキング) |
組み合わせロジックの評価
Verylの組み合わせロジック(always_comb ブロックと assign 宣言)はSystemVerilogと同じ評価セマンティクスに従います:各ブロックは暗黙的にその入力信号に対してセンシティブであり、入力が変化するたびに再評価されます。
ブロックが信号を更新すると、依存するブロックがすべての信号が安定するまで自動的に再トリガーされます。
module ModuleA {
var a: logic<8>;
var b: logic<8>;
var c: logic<8>;
assign a = 1;
always_comb {
b = a + 1; // a が変化すると再評価される
}
always_comb {
c = b + 1; // b が変化すると再評価される
}
}
この例では、初期評価順序に関係なく、値は常に a = 1、b = 2、c = 3 です。
組み合わせループ
組み合わせループは、変数がレジスタを介さずに組み合わせロジックを通じて自身に依存する場合に発生します。Verylは組み合わせループをコンパイル時に検出し、エラーとして報告します。
// エラー:組み合わせループ
var x: logic<8>;
always_comb {
x = x + 1; // x が自身に依存している
}
実装に関する注記
Verylの組み込みシミュレータは、データ依存関係のトポロジカルソートを使用して効率的な評価順序を決定し、再評価パスの回数を最小化します。これはパフォーマンスの最適化であり、観測可能な結果は上述のイベント駆動再トリガーモデルと同等です。
イベント評価
クロックエッジまたはリセットイベントが発生すると、そのイベントに対してセンシティブなすべての always_ff ブロックが実行されます。イベント評価中:
- FF変数からの読み取りはcurrent(コミット前)の値を参照します。
- FF変数へのすべての書き込みはノンブロッキングです — NBA更新にスケジュールされ、FFコミットフェーズまで反映されません。
すべてのブロックが同じスナップショットから読み取り、独立した「next」スロットに書き込むため、always_ff ブロックの実行順序はシミュレーション結果に影響しません。
module ModuleA (
i_clk: input clock,
) {
var a: logic<8>;
var b: logic<8>;
// These two blocks produce the same result regardless of evaluation order:
always_ff {
a = b; // a.next = b.current
}
always_ff {
b = a; // b.next = a.current
}
// コミット後:a と b の値が入れ替わる。
}
ソース順序
同一イベントに対する always_ff ブロックはソース順序で評価されます。
initialブロックとfinalブロック
initial ブロックはシミュレーション開始時に独立したイベントとして一度だけ評価されます。final ブロックはシミュレーション終了時に一度だけ評価されます。これらのブロックは合成不可であり、テストベンチやデバッグ目的で使用されます。
マルチクロックドメイン
Verylは複数のクロックドメインを持つデザインをサポートしています。各クロックドメインは対応するクロックイベントにより独立してトリガーされます。
独立したドメイン評価
クロックエッジが発生すると、そのクロックに対してセンシティブな always_ff ブロックのみが評価されます。FFコミットフェーズはそれらのブロックにより書き込まれた変数にのみ適用されます。他のクロックドメインは影響を受けません。
同時クロックエッジ
2つのクロックが同時にエッジを持つ場合、各ドメインのイベント評価とFFコミットは一度に1ドメインずつ順次実行されます。FFの二重バッファモデル(current/next分離)により、各ドメインは自身の変数の一貫したスナップショットを参照するため、ドメインの処理順序に関係なく単一ドメインの動作は決定論的です。
クロスドメイン信号
異なるクロックドメインの信号へのアクセスはクロックドメインクロッシング(CDC)です。Verylはクロスドメイン信号アクセスに unsafe (cdc) による明示的なアノテーションを要求します(クロックドメインアノテーションを参照)。
同時エッジを持つドメイン間で unsafe (cdc) 信号が共有されている場合、ドメインの評価順序は共有信号値を通じて観測可能です。Verylはこの場合の特定の順序を保証しません。設計者は適切な同期回路(例:2段FFシンクロナイザ、ハンドシェイクプロトコル)により正確性を確保する必要があります。
module ModuleA (
i_clk_a: input 'a clock ,
i_rst_a: input 'a reset ,
i_clk_b: input 'b clock ,
i_dat_a: input 'a logic<8>,
o_dat_b: output 'b logic<8>,
) {
var data_a: 'a logic<8>;
always_ff (i_clk_a, i_rst_a) {
if_reset {
data_a = 0;
} else {
data_a = i_dat_a;
}
}
// クロスドメインアクセスには unsafe (cdc) が必要
unsafe (cdc) {
assign o_dat_b = data_a;
}
}
決定性
Verylは決定論的シミュレーションを保証します:同一のソースコードと同一の刺激シーケンスが与えられた場合、準拠するシミュレータはすべてのシミュレーションステップで同一の信号値を生成します。
Verylが決定性を実現する方法
Verylの言語制約はSystemVerilogにおける一般的な非決定性の原因を排除します:
- Verylは単一の
=演算子を使用し、コンテキストに基づいて正しいセマンティクスを自動推論します。これにより、誤った代入タイプを誤って使用することが不可能になります。 - すべてのブロックが同じスナップショットから読み取り、独立した「next」スロットに書き込むため、
always_ffブロックの実行順序はシミュレーション結果に影響しません。 - 決定論的な組み合わせ評価 — ブロックは安定するまで伝播し、組み合わせループはコンパイル時に検出されます。
- 明示的なクロックドメインクロッシング —
unsafe (cdc)なしのクロスドメインアクセスはコンパイル時エラーとなり、意図しないレースを防止します。
保証の範囲
決定性の保証はシミュレーションステップ境界での観測可能な信号値(各FFコミット後)に適用されます。単一フェーズ内の内部評価順序(例:収束フェーズ内での組み合わせブロック再評価の順序)は実装定義ですが、観測可能な結果は実装間で同一です。
SystemVerilogとの関係
トランスパイルされたSystemVerilog出力は、IEEE 1800準拠のシミュレータが決定論的に処理する確立されたコーディングパターンに従います:
- 順序ロジックには
always_ffと<=(ノンブロッキング) - 組み合わせロジックには
always_combと=(ブロッキング) - 同一変数へのブロッキング/ノンブロッキング代入の混在なし
型推論
型注釈やジェネリック引数は、周辺のコードから導出できる場合に省略できることがあります。この章ではサポートされるパターンをまとめます。
モジュール/インターフェース/関数のポート宣言や関数の戻り値型は、型が推論可能であっても、インターフェースを明確に保つため引き続き型注釈が必要です。
変数の型推論
var や let 宣言、および const 宣言の型注釈は、型が推論可能な場合に省略できます。
型を持たない var 宣言では、assign、always_comb、always_ff を通じた最初の代入から型が推論されます。let および const 宣言では、右辺の式から型が推論されます。
module ModuleA {
let _a: logic<8> = 0;
// `_a` の型から推論される。
let _b = _a;
// ビット幅付きリテラルから推論される。
let _c = 8'd255;
// 最初の代入から推論される。
var _d;
assign _d = _a;
// always_comb 内の最初の代入から推論される。
var _e;
always_comb {
_e = _a;
}
// `const` も推論をサポートする。
const _F = 16'd100;
}
以下の式は型推論の元として使用できます。
- 変数参照
- ビット幅付き数値リテラル(例:
8'd10) - 括弧で囲まれた式(内側の式へ再帰的に推論)
- 関数呼び出し(戻り値の型から推論)
以下の式は SystemVerilog のビット幅評価規則に依存し、ユーザーにとって予期しにくい結果になるため、サポートされません。
- ビット幅なし数値リテラル(例:
10、'0) - 演算子を含む式(例:
_a + 1) - 連結(例:
{_a, _a}) if/case式
同一の var に対する複数の代入で推論される型が一致しない場合、type_inference_conflict として報告されます。サポートされていない式の場合は type_inference_not_supported が報告されます。いずれの場合も、明示的な型注釈を加えればエラーは解消されます。
ジェネリック引数の推論
ジェネリック関数では、関数の引数からジェネリック引数を推論できる場合、::<> 後の実引数を省略できます。推論は各実引数の宣言型を用いてジェネリックパラメータを解決します。
module ModuleA {
function FuncId::<T: u32> (
x: input logic<T>,
) -> logic<T> {
return x;
}
function FuncWide::<T: u32> (
x: input logic<T>,
) -> logic<T + 1> {
return {1'b0, x};
}
let _a: logic<8> = 0;
let _b: logic<16> = 0;
// 実引数の宣言された幅から T が 8 / 16 に推論される。
let _r1: logic<8> = FuncId(_a);
let _r2: logic<16> = FuncId(_b);
// `T + 1` パターン: 実引数の幅 8 から T = 8 が解決される。
let _rw: logic<9> = FuncWide(_a);
}
実引数の幅を変数宣言から決定できない場合(例: ビット幅付きリテラルを渡した場合)、推論は失敗します。その場合は明示的なジェネリック引数が必要です。詳細は generic_inference_failed を参照してください。
プロジェクトプロパティ
プロジェクトプロパティは、ソースコードではなく Veryl.toml から与えられるコンパイル時定数です。ソースコードの外側からプロジェクトを設定するのに便利です。例えば、ライブラリを依存するプロジェクトごとに異なるデータ幅でビルドできます。
定義
プロジェクトプロパティは Veryl.toml の [properties] セクションで定義します。プロパティの値は整数または真偽値です。
[project]
name = "veryl_sample"
version = "0.1.0"
[properties]
DATA_WIDTH = 32
ENABLE_DEBUG = false
詳細はプロジェクトの設定を参照してください。
参照
定義したプロパティは $prop 名前空間を通して参照できます。整数のプロパティは i64 型、真偽値のプロパティは bbool 型になります。
package sample_pkg {
const DATA_WIDTH : i64 = $prop::DATA_WIDTH;
const ENABLE_DEBUG: bbool = $prop::ENABLE_DEBUG;
}
$prop は常に、そのソースコードが属するプロジェクトのプロパティを指します。したがって、依存プロジェクトのプロパティを、それに依存する側のプロジェクトから参照することはできません。
プロジェクトプロパティはコンパイル時定数なので、定数式が使える場所であればどこでも使えます。値はコンパイル時に解決され、解決された値が生成コードに現れます。
module ModuleA {
var a: logic<$prop::DATA_WIDTH>;
if $prop::ENABLE_DEBUG :g_debug {
// debug logic
}
}
依存する側のプロジェクトからの設定
プロジェクトプロパティの値は、それに依存する側のプロジェクトが [dependencies] セクションの properties フィールドで上書きできます。
[dependencies]
veryl_sample = {github = "veryl-lang/veryl_sample", version = "0.1.0", properties = {DATA_WIDTH = 8}}
上の例では、veryl_sample の $prop::DATA_WIDTH は 8 になります。ここで指定しなかったプロパティは、依存プロジェクトの Veryl.toml で定義された既定値のままなので、$prop::ENABLE_DEBUG は false のままです。
詳細は依存関係を参照してください。
開発環境
この章ではプロジェクト設定や開発ツールなど開発環境について説明します。
プロジェクト設定
[project]— プロジェクト定義name— プロジェクトの名前version— プロジェクトのバージョンauthors— プロジェクトの作者description— プロジェクトの説明license— プロジェクトのライセンスrepository— プロジェクトのリポジトリの URLcategories— プロジェクトのカテゴリ
[build]— ビルド設定[format]— フォーマット設定[lint]— リント設定[test]— テスト設定[publish]— 公開設定[synth]— 論理合成設定[properties]— プロジェクトプロパティ[dependencies]— ライブラリの依存関係[[components]]— 検証コンポーネントパッケージ
[project] セクション
Veryl.toml の最初のセクションは [project] です。name と version は必須です。
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"
categories フィールド
任意の categories フィールドは、プロジェクトが属するカテゴリを列挙します。Veryl レジストリがプロジェクトを分類するために使われます。
[project]
categories = ["interconnect", "verification"]
認識されるカテゴリは Veryl コンパイラではなくレジストリ自身が定義しています。レジストリが認識しないカテゴリは veryl register で警告として報告され、無視されます。
[build] セクション
[build] セクションはコード生成の設定です。詳細はこちら。
[format] セクション
[format] セクションはコードフォーマッターの設定です。詳細はこちら。
[lint] セクション
[lint] セクションはリンタの設定です。詳細はこちら。
[test] セクション
[test] セクションはRTLシミュレータによるテストの設定です。詳細はこちら。
[publish] セクション
[publish] セクションはプロジェクト公開の設定です。詳細はこちら。
[synth] セクション
[synth] セクションはシンセサイザの設定です。詳細はこちら。
[properties] セクション
[properties] セクションはプロジェクトプロパティを含みます。プロジェクトプロパティは、そのプロジェクトのソースコードから $prop 名前空間を通して参照できるコンパイル時定数です。
プロパティ名はソースコード中で識別子として参照されるため、Veryl の有効な識別子である必要があります。プロパティの値は整数または真偽値です。プロパティの型は与えた値によって決まり、ソースコード中では整数のプロパティは i64 型、真偽値のプロパティは bbool 型になります。
[properties]
DATA_WIDTH = 32
ENABLE_DEBUG = false
ここで定義した値はプロパティの既定値です。この値は、このプロジェクトに依存する側のプロジェクトから上書きできます。ソースコード中での使い方はプロジェクトプロパティを、上書きについては依存関係を参照してください。
[dependencies] セクション
[dependencies] セクションはライブラリの依存関係です。詳細はこちら。
[[components]] エントリ
[[components]] エントリは Rust で書かれた検証コンポーネントの cargo パッケージを登録します。詳細はこちら。
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– プレーンテキスト形式の相対パスのリストflgen– flgen 形式のファイルリスト
sources フィールド
デフォルトではVerylコンパイラはプロジェクトルートから見える全ての *.veryl ファイルを処理します。特定のディレクトリだけを処理するために sources フィールドを使用することができます。
[build]
sources = ["rtl/foo_module", "rtl/bar_module"]
上記の例では、Verylコンパイラは rtl/foo_module と rtl/bar_module の*.veryl ファイルを処理します。
examples/ は予約されており、sources に指定することはできません。ルートプロジェクトでは自動的に解析されます。ディレクトリ構成 を参照してください。
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_prefix が true のとき、モジュール・インターフェース・パッケージ名のプロジェクトプレフィックスは省略されます。この値はデフォルトで false です。
[build]
omit_project_prefix = true
strip_comments フィールド
strip_comments が true のとき、コメント出力は省略されます。この値はデフォルトで 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_operation が true のとき、inside 演算子を使った演算は==? 演算子を使った論理に展開されます。これはいくつかのEDAツールがinside 演算子をサポートしていないためです。この値はデフォルトで false です。
[build]
expand_inside_operation = true
hashed_mangled_name フィールド
hashed_mangled_name が true のとき、出力されるコンポーネント名のうちジェネリック引数を示す部分がハッシュ化されます。この設定はジェネリック引数が多いときにマングリングされた名前が長くなりすぎるのを防ぎます。この値はデフォルトで false です。
[build]
hashed_mangled_name = true
例:
- ハッシュ化されていない名前:
prj___PkgA__0__1__2__3 - ハッシュ化された名前:
prj___PkgA__3894375d1deadabb
flatten_array_interface フィールド
flatten_array_interface が true のとき、多次元の配列インスタンスやmodportは1次元に展開されます。この設定は多次元配列をサポートしていないEDAツールをサポートするためのものです。この値はデフォルトで false です。
[build]
flatten_array_interface = true
例:
Veryl コード
module ModuleA (
a_if: modport InterfaceA::mp [2, 3],
) {
for i in 0..2 :g {
for j in 0..3 :g {
assign a_if[i][j].a = 0;
}
}
}
flatten_array_interface = true で生成された SystemVerilog コード
module veryl_testcase_ModuleA (
veryl_testcase_InterfaceA.mp a_if [0:(2)*(3)-1]
);
for (genvar i = 0; i < 2; i++) begin :g
for (genvar j = 0; j < 3; j++) begin :g
always_comb a_if[(i)*(3)+(j)].a = 0;
end
end
endmodule
exclude_std フィールド
exclude_std が true のとき、標準ライブラリはインクルードされません。
[build]
exclude_std = true
emit_cond_type フィールド
emit_cond_type が true のとき、unique unique0 priority といった指定が出力されます。
[build]
emit_cond_type = true
instance_depth_limit フィールド
instance_depth_limit はインスタンス階層の最大深さです。デフォルト値は 128 です。
[build]
instance_depth_limit = 256
instance_total_limi フィールド
instance_total_limit は1つのモジュール内のサブインスタンスの最大数です。デフォルト値は 1048576 です。
[build]
instance_total_limit = 256
evaluate_size_limit フィールド
evaluate_size_limit はセマンティックアナライザーによって評価される最大サイズです。デフォルト値は 1048576 です。この値は bit / logic のサイズやループ回数などに適用されます。
[build]
evaluate_size_limit = 256
evaluate_array_limit フィールド
evaluate_array_limit は未割り当ての追跡を有効にする配列の最大サイズです。デフォルト値は 128 です。このサイズを超える配列は未割り当てチェックの対象となりません。
[build]
evaluate_size_limit = 256
error_count_limit フィールド
表示するエラーメッセージの最大数を指定します。全てのメッセージを表示するにはフィールドを設定しないか、0を指定します。
[build]
error_count_limit = 10
incremental フィールド
incremental が true のとき、Verylコンパイラは更新されたファイルに関連したファイルのみを再生成します。デフォルト値は false です。
[build]
incremental = true
Format
[format] セクションはフォーマッタの設定です。
[format]
indent_width = 4
設定
| 設定 | 設定値 | デフォルト | 説明 |
|---|---|---|---|
| indent_width | 整数 | 4 | インデントのスペース幅 |
| max_width | 整数 | 120 | 改行挿入前に保とうとする 1 行の最大幅 |
| vertical_align | ブーリアン | true | 垂直方向の調整を有効にする |
| newline_style | auto / native / unix / windows | auto | 改行コードのスタイル |
フォーマッタは、適切な位置に改行を挿入することで各行を max_width カラム以内に収めようとします。改行できない箇所 (非常に長い識別子やコメントなど) ではこの幅を超える可能性があります。
newline_style はフォーマッタ、ビルドのエミッタ、マイグレータが出力する改行コードを制御します。利用可能な値は以下のとおりです。
auto— 入力ファイルから検出します。改行が存在しない場合は実行プラットフォームのネイティブスタイルが使用されます。native— 実行プラットフォームのネイティブスタイル(Windows では\r\n、それ以外では\n)を使用します。unix— 常に\nを使用します。windows— 常に\r\nを使用します。
Lint
[lint] セクションはリンターの設定です。
[lint.naming]
case_enum = "snake"
設定
[lint.naming] セクション
このセクションは命名規則の設定です。
| 設定 | 設定値 | 説明 |
|---|---|---|
| case_enum | ケースタイプ1 | enum のケーススタイル |
| case_function | ケースタイプ1 | function のケーススタイル |
| case_function_inout | ケースタイプ1 | inout 引数のケーススタイル |
| case_function_input | ケースタイプ1 | input 引数のケーススタイル |
| case_function_output | ケースタイプ1 | output 引数のケーススタイル |
| case_instance | ケースタイプ1 | インスタンスのケーススタイル |
| case_interface | ケースタイプ1 | interface のケーススタイル |
| case_modport | ケースタイプ1 | modport のケーススタイル |
| case_module | ケースタイプ1 | module のケーススタイル |
| case_package | ケースタイプ1 | package のケーススタイル |
| case_parameter | ケースタイプ1 | parameter のケーススタイル |
| case_port_inout | ケースタイプ1 | inout ポートのケーススタイル |
| case_port_input | ケースタイプ1 | input ポートのケーススタイル |
| case_port_modport | ケースタイプ1 | modport ポートのケーススタイル |
| case_port_output | ケースタイプ1 | output ポートのケーススタイル |
| case_reg | ケースタイプ1 | レジスタ変数2のケーススタイル |
| case_struct | ケースタイプ1 | struct のケーススタイル |
| case_union | ケースタイプ1 | union のケーススタイル |
| 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_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_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 | 正規表現4 | enum の禁止正規表現 |
| re_forbidden_function | 正規表現4 | function の禁止正規表現 |
| re_forbidden_function_inout | 正規表現4 | inout 引数の禁止正規表現 |
| re_forbidden_function_input | 正規表現4 | input 引数の禁止正規表現 |
| re_forbidden_function_output | 正規表現4 | output 引数の禁止正規表現 |
| re_forbidden_instance | 正規表現4 | インスタンスの禁止正規表現 |
| re_forbidden_interface | 正規表現4 | interface の禁止正規表現 |
| re_forbidden_modport | 正規表現4 | modport の禁止正規表現 |
| re_forbidden_module | 正規表現4 | module の禁止正規表現 |
| re_forbidden_package | 正規表現4 | package の禁止正規表現 |
| re_forbidden_parameter | 正規表現4 | parameter の禁止正規表現 |
| re_forbidden_port_inout | 正規表現4 | inout ポートの禁止正規表現 |
| re_forbidden_port_input | 正規表現4 | input ポートの禁止正規表現 |
| re_forbidden_port_modport | 正規表現4 | modport ポートの禁止正規表現 |
| re_forbidden_port_output | 正規表現4 | output ポートの禁止正規表現 |
| re_forbidden_reg | 正規表現4 | レジスタ変数2の禁止正規表現 |
| re_forbidden_struct | 正規表現4 | struct の禁止正規表現 |
| re_forbidden_union | 正規表現4 | union の禁止正規表現 |
| re_forbidden_var | 正規表現4 | 変数の禁止正規表現 |
| re_forbidden_wire | 正規表現4 | ワイヤ変数3の禁止正規表現 |
| re_required_enum | 正規表現4 | enum の必須正規表現 |
| re_required_function | 正規表現4 | function の必須正規表現 |
| re_required_function_inout | 正規表現4 | inout 引数の必須正規表現 |
| re_required_function_input | 正規表現4 | input 引数の必須正規表現 |
| re_required_function_output | 正規表現4 | output 引数の必須正規表現 |
| re_required_instance | 正規表現4 | インスタンスの必須正規表現 |
| re_required_interface | 正規表現4 | interface の必須正規表現 |
| re_required_modport | 正規表現4 | modport の必須正規表現 |
| re_required_module | 正規表現4 | module の必須正規表現 |
| re_required_package | 正規表現4 | package の必須正規表現 |
| re_required_parameter | 正規表現4 | parameter の必須正規表現 |
| re_required_port_inout | 正規表現4 | inout ポートの必須正規表現 |
| re_required_port_input | 正規表現4 | input ポートの必須正規表現 |
| re_required_port_modport | 正規表現4 | modport ポートの必須正規表現 |
| re_required_port_output | 正規表現4 | output ポートの必須正規表現 |
| re_required_reg | 正規表現4 | レジスタ変数2の必須正規表現 |
| re_required_struct | 正規表現4 | struct の必須正規表現 |
| re_required_union | 正規表現4 | union の必須正規表現 |
| re_required_var | 正規表現4 | 変数の必須正規表現 |
| re_required_wire | 正規表現4 | ワイヤ変数3の必須正規表現 |
"snake"– snake_case"screaming_snake"– SCREAMING_SNAKE_CASE"lower_camel"– lowerCamelCase"upper_camel"– UpperCamelCase
-
設定可能な値は以下です。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20
-
レジスタ変数とは
always_ffで代入される変数です。合成フェーズでフリップフロップにマップされます。 ↩ ↩2 ↩3 ↩4 ↩5 -
ワイヤ変数とは
always_combで代入される変数です。合成フェーズでワイヤにマップされます。 ↩ ↩2 ↩3 ↩4 ↩5 -
".*"のような正規表現です。使用可能な構文はこちら. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24 ↩25 ↩26 ↩27 ↩28 ↩29 ↩30 ↩31 ↩32 ↩33 ↩34 ↩35 ↩36 ↩37 ↩38 ↩39 ↩40
Test
[test] セクションは以下のように組み込みユニットテストの設定を指定します。
[test]
simulator = "vcs"
設定
[test] セクション
simulator フィールド
simulator フィールドはデフォルトのシミュレータを指定します。以下のタイプを指定できます。
"verilator""vcs""dsim""vivado"
include_files フィールド
include_files フィールドはシミュレーションに使う追加のファイルを指定します。
[test]
include_files = ["test/mem.hex"]
waveform_target フィールド
waveform_target フィールドは波形の生成先を指定します。
target– ターゲットコードと同じディレクトリdirectory– 特定のディレクトリ
directory を指定する場合は、ターゲットパスを path キーで指定します。
[test]
waveform_target = {type = "directory", path = "[dst dir]"}
waveform_format フィールド
waveform_format フィールドはダンプされる波形のフォーマットを指定します。指定できる値は以下の通りです。
vcd– デフォルト値であり、多くのベンダーでサポートされていますが機能は限定的です。fst– 整数の代わりにenum値を表示するなどいくつかの機能をサポートしています。gtkwaveとsurferで扱うことができます。
defines フィールド
defines フィールドは、veryl test の実行中に定義済みとして扱われる名前のリストを指定します。これらの名前は #[ifdef]/#[ifndef] 属性から参照でき、テスト専用のコードパスを有効化するために利用できます。SystemVerilog テストと cocotb テストでは、同じ名前が +define+NAME (Vivado の場合は -d NAME) として外部シミュレータにも渡されます。
[test]
defines = ["DEBUG", "ENABLE_ASSERTIONS"]
追加の名前はコマンドラインから veryl test --define NAME (または -D NAME) で渡すこともできます。CLI で指定した値はこのフィールドの内容にマージされます。
seed フィールド
seed フィールドは、ランダム化された検証コンポーネントテストのベースシードを設定します。未設定の場合、veryl test の実行ごとに新しいランダムシードが引かれて表示されます。特定の実行を再現するには、値を設定する(または --seed を渡す)ようにします。
four_state フィールド
four_state フィールドは、ネイティブテストを4値(X/Z)モードで実行します。既定値は false で、この場合、代入されていない値は 0 として読み出されます。4値実行では、そのような値は代わりに x として読み出されます。--4state コマンドラインオプションでも有効化できます。
[test]
four_state = true
component_backend フィールド
component_backend フィールドは、検証コンポーネントの実行方法を固定します。
"native"– cargo でソースからビルド"wasm"– コミット済みのビルド済みバイナリ
未設定の場合、veryl test は cargo が利用可能ならソースからビルドし、そうでなければコミット済みのビルド済み wasm にフォールバックします。
[test.verilator] セクション
このセクションはVerilatorによるテストの設定です。
| 設定 | 設定値 | 説明 |
|---|---|---|
| compile_args | [文字列] | verilator コマンドへの追加の引数 |
| simulate_args | [文字列] | シミュレーションバイナリへの追加の引数 |
[test.vcs] セクション
このセクションはVCSによるテストの設定です。
| 設定 | 設定値 | 説明 |
|---|---|---|
| compile_args | [文字列] | vcs コマンドへの追加の引数 |
| simulate_args | [文字列] | シミュレーションバイナリへの追加の引数 |
[test.dsim] セクション
このセクションはDSimによるテストの設定です。
| 設定 | 設定値 | 説明 |
|---|---|---|
| compile_args | [文字列] | dsim コマンドへの追加の引数 |
| simulate_args | [文字列] | dsim コマンドへの追加の引数 |
[test.vivado] セクション
このセクションはVivadoによるテストの設定です。
| 設定 | 設定値 | 説明 |
|---|---|---|
| compile_args | [文字列] | xvlog コマンドへの追加の引数 |
| elaborate_args | [文字列] | xelab コマンドへの追加の引数 |
| simulate_args | [文字列] | xsim コマンドへの追加の引数 |
Publish
[publish] セクションは以下のようにプロジェクト公開の設定を指定します。
[publish]
bump_commit = true
bump_commit_message = "Bump"
設定
| 設定 | 設定値 | デフォルト | 説明 |
|---|---|---|---|
| bump_commit | ブーリアン | false | バージョンアップ後の自動コミット |
| publish_commit | ブーリアン | false | 公開後の自動コミット |
| bump_commit_message | 文字列 | “chore: Bump version” | バージョンアップ後のコミットメッセージ |
| publish_commit_message | 文字列 | “chore: Publish” | 公開後のコミットメッセージ |
| register | ブーリアン | 未設定 | publish 後のレジストリ登録 |
Synth
[synth] セクションは、簡易的な論理合成を行って面積・タイミング・電力を見積もる veryl synth コマンドの設定を指定します。
[synth]
top = "TopModule"
library = "sky130"
clock_freq = 100.0
activity = 0.1
timing_paths = 1
設定
| 設定 | 設定値 | デフォルト | 説明 |
|---|---|---|---|
| top | 文字列 | (自動) | デフォルトのトップモジュール名。CLI の --top が指定されると上書きされます。未指定の場合、最初に見つかったユーザーモジュールが使用されます。 |
| library | sky130 / asap7 / gf180mcu / ihp-sg13g2 | sky130 | 使用する組み込みセルライブラリ/PDK。 |
| clock_freq | 浮動小数 | 100.0 | 動的消費電力見積もりで仮定するクロック周波数(MHz)。 |
| activity | 浮動小数(0.0–1.0) | 0.1 | 組み合わせネットで仮定するサイクルあたりのトグル率。 |
| timing_paths | 整数 | 1 | タイミングダンプで報告する最悪遅延エンドポイントの数。 |
RAM 推論
veryl synth は、単一の動的アドレスで書き込まれ、動的アドレスで読み出される大きな配列を、depth × width 個のフリップフロップとアドレスデコード/マルチプレクサツリーへ展開する代わりに、RAM マクロとして推論します。以下の閾値によって、これが行われる条件が制御されます。推論に失敗した配列(例えばリセットを持つもの——実際の SRAM にはリセットがありません)はフリップフロップのまま残り、さらに ram_max_ff_bits を超える場合は、合成はメモリを使い果たすのではなくエラーを報告します。
| 設定 | 設定値 | デフォルト | 説明 |
|---|---|---|---|
| ram_min_bits | 整数 | 1024 | フリップフロップではなく RAM として推論する価値がある最小の配列サイズ(ストアドビット単位)。 |
| ram_max_read_ports | 整数 | 16 | RAM として推論される配列が持ちうる、異なる読み出しアドレスの最大数。 |
| ram_max_write_ports | 整数 | 8 | RAM として推論される配列が持ちうる、異なる書き込み箇所の最大数。 |
| ram_max_ff_bits | 整数 | 65536 | 推論に失敗した動的インデックス配列がフリップフロップへ展開されうる最大の配列サイズ(ストアドビット単位)。これを超える配列は、メモリを使い果たすのではなく拒否されます。 |
組み込みライブラリ
library フィールドは面積・タイミング・電力の見積もりに使われる組み込みセルライブラリを指定します。値はすべて公開されている Liberty 特性化データから抽出または派生させたもので、サインオフ精度ではなく自己整合的な「相対コスト」として校正されています。すべてドライブ強度1のセルを用いています。
library | プロセス | セルライブラリ/コーナー | 電源電圧 | ソース(ライセンス) |
|---|---|---|---|---|
sky130 | SkyWater 130nm プレーナ CMOS | sky130_fd_sc_hd / tt_025C_1v80 | 1.8 V | skywater-pdk(Apache 2.0) |
asap7 | ASU 7nm 予測 FinFET | asap7sc7p5t RVT / tt_0p7V | 0.7 V | asap7(BSD 3-Clause) |
gf180mcu | GlobalFoundries 180nm MCU プレーナ CMOS | gf180mcu_fd_sc_mcu7t5v0 / tt_025C_1v80 | 1.8 V | gf180mcu-pdk(Apache 2.0) |
ihp-sg13g2 | IHP 130nm SiGe BiCMOS | sg13g2_stdcell / typ_1p20V_25C | 1.2 V | IHP-Open-PDK(Apache 2.0) |
Veryl はこれらの PDK の Liberty ソース、回路図、レイアウトを再配布していません。参考データとして、ごく少数のセル単位の面積・遅延・漏れ電力・エネルギーの値のみを利用しています。
依存関係
他の Veryl プロジェクトへの依存関係をプロジェクトに追加したい場合、Veryl.toml に [dependencies] セクションを追加します。エントリの左辺は依存関係のプロジェクト名、右辺はソースパスとバージョンです。github はGitHub上のリポジトリを参照する糖衣構文です。代わりに git を用いてURL全体を指定することもできます。
[dependencies]
veryl_sample = {github = "veryl-lang/veryl_sample", version = "0.1.0"}
# これは上記と同じ
veryl_sample = {git = "https://github.com/veryl-lang/veryl_sample", version = "0.1.0"}
デフォルトでは依存関係の名前空間はそのプロジェクト名と同じです。もし左辺の名前を変更した場合は、project フィールドでプロジェクト名を指定する必要があります。
[dependencies]
veryl_sample_alt = {github = "veryl-lang/veryl_sample", project = "veryl_sample", version = "0.2.0"}
リポジトリの内部プロジェクトは以下のように指定できます。
[dependencies]
inner_prj1 = {github = "veryl-lang/veryl_sample", version = "0.1.0"}
inner_prj2 = {github = "veryl-lang/veryl_sample", version = "0.1.0"}
inner_prj3 = {github = "veryl-lang/veryl_sample", version = "0.1.0"}
依存関係の使用
Veryl.toml に依存関係を追加したあとは、その依存関係の module、interface、packageを使うことができます。以下は 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.0、0.1.1、0.2.0があった場合、0.1.1 が選択されます。これは以下のように決定されます。
0.1.0は0.1.0と互換性がある0.1.1は0.1.0と互換性がある0.2.0は0.1.0と互換性がない0.1.1は互換性のある最新バージョン
version フィールドは =0.1.0 のような指定も可能です。詳細は Rust のバージョン要求についてのドキュメントを参照してください。Specifying Dependencies.
相対パス依存関係
手元の環境で開発しているとき、ローカルファイルパスへの依存関係が使えると便利なことがあります。相対パス依存関係は以下のように指定することができます。
[dependencies]
veryl_sample = {path = "../../veryl_sample"}
プロジェクトに相対パス依存関係がある場合、そのプロジェクトは veryl publish で公開することはできません。
ローカルパスによる上書き
場合によってはローカルで変更されたバージョンの依存関係を使う必要があることもあります。そのような場合、以下のようにローカルパスによって依存関係を上書きすることができます。
[dependencies]
veryl_sample = {github = "veryl-lang/veryl_sample", version = "0.1.0", path = "../veryl_sample"}
これは ../veryl_sample が存在する場合はそれを使い、そうでない場合は Git から取得する、という意味です。
プロジェクトプロパティの上書き
依存プロジェクトがプロジェクトプロパティを持つ場合、その値は properties フィールドで上書きできます。
[dependencies]
veryl_sample = {github = "veryl-lang/veryl_sample", version = "0.1.0", properties = {DATA_WIDTH = 8}}
ここで指定しなかったプロパティは、依存プロジェクトの Veryl.toml で定義された既定値のままになります。上書きには以下の制限があります。
- 依存プロジェクトで定義されていないプロパティは指定できません。
- 与える値の型は既定値の型と同じでなければなりません。
上書きは直接の依存プロジェクトにのみ適用されます。したがって、依存プロジェクトのさらに依存先のプロパティは影響を受けず、それらは各依存プロジェクトの Veryl.toml によって制御されます。
同じソースを持つ依存プロジェクトは、そのプロパティ値によって区別されます。したがって、同じプロジェクトを異なるプロパティ値の複数の依存として使えます。
[dependencies]
veryl_sample_a = {github = "veryl-lang/veryl_sample", project = "veryl_sample", version = "0.1.0", properties = {DATA_WIDTH = 8}}
veryl_sample_b = {github = "veryl-lang/veryl_sample", project = "veryl_sample", version = "0.1.0", properties = {DATA_WIDTH = 16}}
上の例では、veryl_sample_a と veryl_sample_b は別々のプロジェクトとして生成されます。
Git バックエンド
Veryl は 2 つの異なるバックエンドを通して git ベースの依存を取得できます。
gitoxide— 純粋な Rust 実装の git。外部のgitバイナリを必要としません。command— システムのgitコマンドを実行します。gitがPATHに存在する必要があります。
既定では gitoxide を試し、失敗した場合 (たとえば gitoxide が対応していない認証方式が必要な場合など) に自動的に command へフォールバックします。バックエンドは VERYL_GIT_BACKEND 環境変数で明示的に選択できます。
| 設定値 | 挙動 |
|---|---|
auto | 既定値。gitoxide を試し、失敗時に command へフォールバックする。 |
gitoxide | gitoxide を強制する。フォールバックなし。 |
command | システムの git コマンドを強制する。フォールバックなし。 |
プロジェクトを公開する
プロジェクトを公開するには 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 レジストリは公開されたプロジェクトの一覧を提供します。プロジェクトは veryl register で登録できます。
$ veryl register
レジストリはプロジェクトをリポジトリで識別するため、[project] セクションの repository フィールドが必要です。origin はチェックアウトによって変わるため、git の origin リモートではなく宣言された repository が使われます。
veryl register は登録前に確認を求めます。--yes オプションで確認を省略でき、コマンドを対話的に実行しない場合はこのオプションが必要です。
veryl publish でもプロジェクトを登録できます。その挙動は Veryl.toml の [publish] セクションの register で指定します。
true— publish 後に自動的に登録するfalse— 登録しない- 未設定 — 対話的に一度だけ問い合わせる
登録はコミットの push を行いません。公開されたバージョンは、そのリビジョンが push され、レジストリがリポジトリをクロールした後に見えるようになります。
レジストリが認識しないカテゴリが指定されている場合、警告として報告されます。
使用するレジストリは VERYL_REGISTRY_URL 環境変数で変更できます。
設定
全設定の説明はこちら。
ディレクトリ構成
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 ファイルリストと一緒に使うことができます。
--out-dir による出力先の変更
デフォルトでは veryl build は生成される全ての出力(SystemVerilog ファイル、ソースマップ、ファイルリスト、dependencies)をプロジェクトパス以下に書き出します。--out-dir オプションはソースをプロジェクトパス基準で解決したまま、これらの出力先を別のディレクトリに変更します。これは生成されたファイルをソースツリーの外に配置することを期待する外部ビルドシステム(例: xmake、build.rs)と連携する場合に便利です。
$ veryl build --out-dir /path/to/output
相対パスはカレントディレクトリを基準に解決されます。
$ veryl build --out-dir output
出力ディレクトリ以下の構成は通常の構成と同じになります。例えば target = {type = "directory", path = "target"} の場合、出力ディレクトリは以下のようになります。
$ tree /path/to/output
/path/to/output
|-- dependencies
|-- prj.f
`-- target
|-- module_a.sv
|-- module_b.sv
`-- module_c.sv
2 directories, 5 files
生成されるファイルリストは変更後の出力先を参照するため、後段のツールでそのまま利用できます。--out-dir を指定しない場合のビルドの挙動は変わりません。
examples ディレクトリ
プロジェクトルートの examples ディレクトリは、使用例やテストベンチを置く場所として予約されています。
$ tree
.
|-- examples
| `-- example_top.veryl
|-- src
| `-- module_a.veryl
`-- Veryl.toml
2 directories, 3 files
examples 以下のファイルは通常のソースと同様に解析・チェックされ、その中の #[test] モジュールは veryl test で実行されます。他のソースとプロジェクトの名前空間を共有しますが、コード生成、ファイルリスト、および veryl doc が生成するドキュメントからは除外されます。プロジェクトが依存として利用される場合、その examples ディレクトリは完全に無視されます。
このディレクトリは予約されているため、[build] セクションの sources フィールドに指定することはできません。
.gitignore について
Verylは以下の .gitignore をデフォルト値として提供します。.build ディレクトリはVerylコンパイラがビルド情報を記録するために使用します。
.build/
それ以外のパターンはプロジェクトに合わせて追加できます。.gitignore の候補としては以下が考えられます。
dependencies/target/*.sv*.f
フォーマッタ
veryl fmt コマンドでソースコードをフォーマットできます。あるいは言語サーバの textDocument/formatting 要求によるフォーマットにも対応しています。
全設定の説明はこちら。
リンタ
veryl check あるいは veryl build でリントチェックができます。あるいは言語サーバはリアルタイムでのチェックを行います。
全設定の説明はこちら。
シミュレータ
テストは veryl test で実行することができます。
ネイティブテスト ではVerylの組み込みシミュレータが使用されます。外部シミュレータのインストールは不要です。
ネイティブシミュレータのコード生成バックエンドは veryl test --backend で選択します。
| バックエンド | 説明 |
|---|---|
cc | 既定値。C を出力し、外部の C コンパイラ (gcc または clang の -O3) でコンパイルした結果をロードする。 |
cranelift | インプロセスの Cranelift JIT。 |
interpret | コード生成を行わず、サイクルごとに IR を歩く。 |
cc バックエンドは cc (通常は gcc または clang) が PATH に存在することを要求します。cc が無い場合や、特定の構文がまだ cc エミッタで対応されていない場合は、その部分について Cranelift へ透過的にフォールバックします。veryl test --backend-validate を使うと cc バックエンドを Cranelift と並行実行し、差異があれば中断します。
SystemVerilogテストとcocotbテストでは、外部のRTLシミュレータが必要です。サポートされているシミュレータは以下の通りです。
Verilatorはデフォルトのシミュレータです。Veryl.tomlやコマンドラインオプションでシミュレータが指定されていない場合に使用されます。
全設定の説明はこちら。
JSON レポート
veryl test --format json は、人間向けのサマリの代わりに、テスト結果の機械可読なレポートを標準出力に書き出します。CI やその他のツールから結果を利用するのに便利です。
$ veryl test --format json
{
"format_version": 1,
"backend": "cc",
"passed": 1,
"failed": 1,
"ignored": 1,
"tests": [
{ "name": "test_a", "status": "pass", "runtime_s": 0.000405, "sim_s": 0.0000147 },
{ "name": "test_b", "status": "fail", "message": "assertion failed", "runtime_s": 0.000559, "sim_s": 0.0000206 }
]
}
各テストの status フィールドは pass または fail で、失敗したテストには message フィールドが付きます。テストが標準出力に何かを書き出した場合は output フィールドに格納されます。無視されたテストは ignored で数えられますが、tests には列挙されません。
レポートのスキーマバージョンは --format-version で指定できます。これは --format json と併用する場合のみ有効です。現在は 1 のみサポートされています。
cocotb
cocotb テストを実行するには cocotb がインストールされた python3 の環境が必要です。サポートされている cocotb のバージョンは 1.9.x あるいは 2.0.x です。
例えば以下のコマンドでインストールすることができます。
$ pip3 install cocotb==2.0.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 コマンドで生成することができます。全てのパブリックなモジュールとインターフェース、パッケージがリストアップされます。(参照 可視性)
詳細な説明を書きたい場合はドキュメンテーションコメントを追加することもできます。ドキュメンテーションコメントではマークダウン記法を使えます。
以下のフォーマットもサポートされています。
それぞれの構文は wavedrom と mermaid コードブロック内で使用できます。
詳細な構文は以下を参照してください。
/// 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” | 出力ディレクトリへのパス |
ドキュメンテーションテスト
WaveDromブロックは wavedrom の代わりに wavedrom,test コードブロックを使うことでドキュメンテーションテストとしても利用できます。波形の信号名はモジュールポートと名前で照合されます(i_/o_ プレフィックスと _n サフィックスは自動的に除去されます)。クロックとリセット信号は適切に認識・処理されます。
ドキュメンテーションテストは他の組み込みテストとともに veryl test コマンドで実行されます。
使用可能なwave文字は以下です。
p,P,n,N— クロック信号(ポジティブ/ネガティブエッジ)0,1— 論理値x,z— 不定 / ハイインピーダンス.— 前の値を繰り返し=,2-9— データ値(data配列と併用)
/// 1サイクル遅延レジスタ
///
/// ```wavedrom,test
/// {signal: [
/// {name: 'clk', wave: 'p........'},
/// {name: 'rst_n', wave: '0.1......'},
/// {name: 'din', wave: '0.01.0.1.'},
/// {name: 'dout', wave: '0...1.0.1'}
/// ]}
/// ```
pub module ModuleB (
i_clk : input 'a clock,
i_rst_n: input 'a reset,
i_din : input 'a logic,
o_dout : output 'a logic,
) {
var r_data: 'a logic;
always_ff {
if_reset {
r_data = 0;
} else {
r_data = i_din;
}
}
assign o_dout = r_data;
}
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
- Verilator によるテスト
このために 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.toml の sourcemap_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
+0.12 のように指定すると、0.12.x のうち最新バージョンが選択されます。
ディレクトリ毎のツールチェーンオーバーライド
もし特定にディレクトリで特定のバージョンの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
プロキシ
Verylupはプロキシ設定として以下の環境変数を参照します。
HTTPS_PROXYhttps_proxyALL_PROXYall_proxy
プロキシのプロトコルとしては http と socks5 がサポートされています。
プロキシ設定
環境変数の代わりにverylupだけのプロキシ設定を指定することができます。
verylup config set proxy socks5://127.0.0.1:1086
ナイトリーチャンネル
最新の機能を簡単に試すために、ナイトリーチャンネルが利用できます。ナイトリーチャンネルはマスターブランチから毎日ビルドされます。
verylup install nightly
デフォルトではナイトリーチャンネルはインストールしても有効になりません。以下の方法で有効にすることができます。
// +nightly を使う
veryl +nightly build
// デフォルトをナイトリーにする
verylup default nightly
// 特定のプロジェクトをナイトリーで上書きする
verylup override set nightly
Veryl開発者向け
Veryl の開発者向けに、特別なツールチェーン local が用意されています。verylup install local をローカルのVerylリポジトリ上で実行すると、ビルドしたツールチェーンを local ツールチェーンとしてインストールします。local ツールチェーンが存在するときはデフォルトのツールチェーンになります。
// ローカルのVerylリポジトリでビルドしたツールチェーンをインストール
verylup install local
// ビルドしたツールチェーンを使う
veryl build
// 最新のツールチェーンを使う
veryl +latest build
新バージョンへの移行
Verylの新しいバージョンが破壊的な変更を伴うことがあります。veryl migrate は既存のプロジェクトを新バージョンに自動的に移行します。--check オプションによりどのような変更が適用されるか事前に確認することもできます。
$ veryl migrate --check
$ veryl migrate
veryl migrate は単一のメジャーバージョン(1.0まではマイナーバージョン)しか移行しません。複数のバージョンを移行するには以下のようにしてください。
$ veryl +0.15.0 migrate # from v0.14.0 to v0.15.0
$ veryl +0.16.0 migrate # from v0.15.0 to v0.16.0
Docker イメージ
公式のDockerイメージはDocker Hubを通して提供されています。
https://hub.docker.com/r/veryllang/veryl
イメージはカスタムイメージのベースや、GitLab CI/CDのために使うことができます。以下に使用例をいくつか示します。
docker コマンド
イメージを veryllang/veryl からプルできます。
$ docker pull veryllang/veryl
Dockerfile
Dockerイメージのベースとして使用する場合は、以下の FROM ディレクティブを使用できます。
FROM veryllang/veryl:latest
GitLab CI/CD
GitLab CI/CD のための .gitlab-ci.yml の例は以下になります。
image: "veryllang/veryl"
build:
stage: build
script:
- veryl build
fmt:
stage: build
script:
- veryl fmt --check
トランスレータ
veryl translate は SystemVerilog のソースファイルを Veryl のソースに変換します。既存の SystemVerilog コードベースの移行を支援するベストエフォートのツールであり、結果が完全に置き換え可能であることは保証されず、手動でのレビューが必要になる場合があります。
$ veryl translate foo.sv
入力された各 .sv ファイルに対して、同じディレクトリに同名の .veryl ファイルが生成されます。変換後の出力は Veryl のフォーマッタを通すため、標準的なフォーマットスタイルに従います。
オプション
| オプション | 説明 |
|---|---|
--stdout | ファイルに書き込まず、結果を標準出力へ書き出します。パイプ処理や、デフォルト以外のパスへリダイレクトしたい場合に便利です。 |
--strict | 未サポートの構文が見つかった場合、非ゼロのステータスで終了します。 |
--no-format | Veryl フォーマッタの処理をスキップし、トランスレータの生の出力を出します。 |
未サポートの構文
SystemVerilog の中には Veryl で表現できないもの、あるいはトランスレータがまだ対応していないものがあります。そのような構文が現れた場合、構文の種別とソース行番号付きの警告が出力され、当該箇所は出力中にコメントやプレースホルダとして残されます。ファイルごとに未サポート構文の合計数も末尾にまとめて報告されます。
--strict モードでは、未サポートの構文が一つでもあれば veryl translate は非ゼロのステータスで終了します。
シンセサイザ
veryl synth はプロジェクトに対して簡易的な論理合成を行い、概算の面積・タイミング・電力を報告します。これは設計検討中に素早くフィードバックを得るためのツールであり、本格的な合成フローの代替を意図したものではありません。設計は選択したライブラリ内の代表的なセル群へマッピングされますが、配置配線・負荷容量・駆動能力・Vth の選択は考慮されません。
$ veryl synth
トップモジュールは以下の優先順位で選択されます。
- 指定されている場合、CLI の
--top <name> - 設定されている場合、
Veryl.tomlのsynth.top - プロジェクト内で最初に見つかったユーザーモジュール
セルライブラリ、クロック周波数、トグル率といった設定項目については [synth] を参照してください。
出力
デフォルトでは、veryl synth は1行のサマリと、面積/タイミング/電力の詳細ブロックを出力します。
synth: TopModule — 123 gates, 17 FFs
library: sky130_fd_sc_hd ...
summary:
area: 1234.56 um² (comb 1000.00, seq 134.56, mem 100.00)
timing: 2.345 ns 8 levels in_dat → out_dat
power: 0.1234 mW (leak 0.0123 mW, dyn 0.1111 mW)
@ f_clk = 100 MHz, activity = 0.10
area:
...
timing:
...
power:
...
RAM 推論
大きな配列は、depth × width 個のフリップフロップとアドレスデコード/マルチプレクサツリーではなく、SRAM マクロにマッピングされます。ビット配列マクロははるかに高密度かつ高速なため、直接モデル化することで、キャッシュやレジスタファイルといったメモリ主体の設計でも面積・タイミング・電力の見積もりが現実的に保たれます。
配列は、以下のすべてを満たす場合に RAM ブロックとして推論されます。
- ストアドビットが
ram_min_bits(デフォルト 1024)ビット以上で、depth が 2 以上である - リセットなしで、全ワードの動的アドレス書き込み(
mem[addr] = data)により書き込まれ、書き込み箇所が最大ram_max_write_ports(デフォルト 8)個である - 全ワードの動的アドレス(
mem[addr])で読み出され、読み出しアドレスが最大ram_max_read_ports(デフォルト 16)個である
if_reset の下で書き込まれる配列はフリップフロップのまま残ります。実際の SRAM にはリセットがないため、SRAM にしたい配列は RTL 上でリセットなしで書きます。部分書き込みやサブワード書き込みの場合も、配列はフリップフロップのまま残ります。
推論に失敗したものの ram_max_ff_bits(デフォルト 65536 ストアドビット)を超える大きさの動的インデックス配列は、メモリを使い果たさずにフリップフロップへ展開することができないため、代わりに合成がエラーを報告します。そのような配列を RAM として推論させるにはリセットなしで記述するか、ram_max_ff_bits を引き上げてください。
これらの閾値は Veryl.toml の [synth] セクションで設定できます。
推論されたメモリの面積は面積サマリの mem 項目として報告され、--dump-area は各ブロックを形状(depth × width)とポート数でグループ化して一覧表示します。
ram: 2 blocks
1024×32 1R1W ×1 32768 bits 189.40 um²
512×64 2R1W ×1 32768 bits 189.40 um²
オプション
| オプション | 説明 |
|---|---|
--top <name> | トップモジュール名。Veryl.toml の synth.top を上書きします。 |
--timing-paths <n> | タイミングダンプで報告する最悪遅延エンドポイントの数。synth.timing_paths を上書きします。 |
--dump-ir | ゲートレベル IR(ゲートとフリップフロップのネットリスト)をダンプします。 |
--dump-area | 推論された RAM ブロックを含む、セル種別ごとの面積内訳をダンプします。 |
--dump-timing | クリティカルパスのトレースをダンプします。 |
--dump-power | 電力見積もり(漏れ電力+動的電力の内訳)をダンプします。 |
--format <format> | 出力形式。pretty(デフォルト、人間向け)または json。 |
--format-version <n> | JSON レポートのスキーマバージョン。--format json と併用する場合のみ有効。現在は 1 のみ。 |
--dump-* フラグが何も指定されない場合、面積/タイミング/電力の3つすべてがダンプされます。
JSON レポート
--format json は、人間向けのサマリの代わりに、機械可読なレポートを標準出力に書き出します。
$ veryl synth --format json
{
"format_version": 1,
"top": "Counter",
"library": "sky130",
"status": "ok",
"cells": 19,
"ffs": 8,
"area": { "total": 302.5, "combinational": 122.5, "sequential": 180.0, "memory": 0.0 },
"timing": { "delay_ns": 0.38, "depth": 4, "from": "cnt[3]", "to": "cnt[6]" },
"power": { "total_mw": 0.0239, "leakage_mw": 0.0000271, "dynamic_mw": 0.0239, "clock_freq_mhz": 100.0, "activity": 0.1 }
}
合成に成功した場合、status フィールドは ok になります。そうでない場合は、シンセサイザが扱えない構文なら unsupported、トップモジュールが見つからないなら no_top、その他の失敗なら error になります。これらの場合は message フィールドが失敗の内容を示し、area・timing・power は null になります。
メタデータ
veryl metadata は、現在のプロジェクトの解決済みメタデータを、依存グラフを含めて出力します。外部ツールが Veryl の依存解決結果と依存キャッシュを、独自に再実装することなく再利用できるようにするためのものです。
$ veryl metadata
オプション
| オプション | 説明 |
|---|---|
--format <format> | 出力形式。pretty(デフォルト、人間向け)または json。 |
--format-version <n> | JSON 出力スキーマのバージョン。--format json と一緒のときのみ有効です。 |
出力フォーマットのバージョン
--format-version は JSON 出力のスキーマを選択します。
1(またはバージョン未指定)— 内部のMetadata表現。この形式は安定しておらず、Veryl のリリース間で変更される可能性があります。2— 外部ツール向けの安定したバージョン付きスキーマ。
出力をプログラムで処理するツールでは、バージョン 2 を使用してください。
$ veryl metadata --format json --format-version 2
バージョン 2 の出力は次のような形になります。
{
"format_version": 2,
"root": {
"name": "my_project",
"version": "0.1.0",
"local_path": "/abs/path/to/my_project",
"metadata": {}
},
"dependencies": [
{
"id": "dep:foo",
"name": "foo",
"project": "foo",
"source": {
"kind": "repository",
"url": "https://github.com/example/foo",
"project": "foo",
"version": "1.2.0",
"revision": "0123456789abcdef",
"path": "."
},
"local_path": "/abs/path/to/dependency/cache/foo",
"metadata": {},
"dependencies": ["dep:bar"]
}
]
}
rootは現在のプロジェクトを表し、dependenciesはグラフ内の解決済みのすべての依存を列挙します。local_pathはディスク上の場所(プロジェクトディレクトリ、または依存キャッシュディレクトリ)であり、ツールが実際のソースファイルを読み取れます。sourceは依存の取得元を示します。そのkindはpath(pathを持つ)またはrepository(url・project・version・revision・pathを持つ)のいずれかです。- 各エントリの
dependenciesフィールドは、その依存自身の依存のidを列挙するため、グラフ全体を再構築できます。
[metadata] テーブル
Veryl.toml には、外部ツール用の任意のデータを格納する [metadata] テーブルを記述できます。Veryl はその内容を解釈せず、保持したうえで veryl metadata の出力の metadata フィールドに(ルートプロジェクトと依存の両方について)再公開するだけです。
[metadata.external_tool]
files = ["src/**/*.v"]
attrs = { role = "core" }
これは外部連携データを置くための専用の場所です。Veryl.toml のトップレベルの不明なキーは拒否されるため、ツール固有のデータは [metadata] の下に置く必要があります。
外部サブコマンド
外部サブコマンドを使うと、Veryl とは別に配布されるツールで veryl コマンドを拡張できます。指定されたサブコマンドが組み込みのものでない場合、veryl は veryl-<subcommand> という名前の実行ファイルを PATH から探して実行します。これにより、そのようなツールを組み込みのサブコマンドと同じように使えます。
$ veryl import foo.sv
上のコマンドは、veryl-import が PATH 上に見つかった場合に veryl-import foo.sv を実行します。サブコマンド名より後ろの引数はそのまま渡され、外部コマンドの終了ステータスがそのまま veryl の終了ステータスになります。
対応する実行ファイルが見つからない場合はエラーになります。
コマンドの一覧表示
veryl --help は組み込みのサブコマンドのみを表示します。PATH 上で見つかった外部サブコマンドも含めて利用可能なすべてのコマンドを表示するには、veryl --list を使います。
$ veryl --list
Available Commands:
build Build the target codes corresponding to the current project
check Analyze the current project
...
import Import SystemVerilog files as a Veryl dependency
...
サブコマンドの説明
デフォルトでは、外部サブコマンドは External Veryl subcommand from PATH (veryl-import) のようなフォールバックの説明とともに一覧表示されます。実行ファイルが --info オプションをサポートしている場合は、代わりにその出力が説明として使われます。veryl-<subcommand> --info は1行の説明を標準出力に出力することが期待されており、以下の条件をすべて満たす必要があります。
- 終了ステータスが
0であること - 出力が制御文字を含まない1行であること
- 行の長さが160文字以下であること
- コマンドが500ms以内に終了すること
いずれかを満たさない場合は、フォールバックの説明が使われます。
予約された名前
veryl-ls は公式の言語サーバのバイナリであるため、ls は外部サブコマンドとしてディスパッチされません。パス区切り文字を含むサブコマンド名も拒否されます。
補遺
構文
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
%on HashLBracketTerm %push Attr
%on ColonColonLAngleTerm %push Generic
%on EscapedRBraceTerm %pop
%on EmbedTerm %enter EmbedHeader
%scanner EmbedHeader {
%on TripleLBraceTerm %enter EmbedBody
}
%scanner EmbedBody {
%auto_newline_off
%auto_ws_off
%on LBraceTerm %push EmbedBodyInner
%on EscapedLBraceTerm %push INITIAL
%on TripleRBraceTerm %enter INITIAL
}
%scanner EmbedBodyInner {
%auto_newline_off
%auto_ws_off
%on LBraceTerm %push EmbedBodyInner
%on EscapedLBraceTerm %push INITIAL
%on RBraceTerm %pop
}
%scanner Generic {
%on ColonColonLAngleTerm %push Generic
%on RAngleTerm %pop
}
%scanner Attr {
%on RBracketTerm %pop
}
%%
// ----------------------------------------------------------------------------
// Terminal
// ----------------------------------------------------------------------------
// Longest match should be first
CommentsTerm : <INITIAL, Generic, EmbedHeader, Attr>"(?:(?:(?://.*(?:\r\n|\r|\n))|(?:/\*(?:[^*]|\*+[^*/])*\*+/))\s*)+" : Token;
StringLiteralTerm : <INITIAL , Attr>"\u{0022}(?:\\[\u{0022}\\fnt]|[^\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;
LTMinusTerm : <INITIAL >'<-' : Token;
PlusColonTerm : <INITIAL >'+:' : Token;
AssignmentOperatorTerm: <INITIAL >"\+=|-=|\*=|/=|%=|&=|\|=|\^=|<<=|>>=|<<<=|>>>=" : Token;
DiamondOperatorTerm : <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;
ColonColonLAngleTerm : <INITIAL, Generic >'::<' : Token;
ColonColonTerm : <INITIAL, Generic >'::' : Token;
ColonTerm : <INITIAL, Generic >':' : Token;
CommaTerm : <INITIAL, Generic, Attr>',' : Token;
DotDotEquTerm : <INITIAL >'..=' : Token;
DotDotTerm : <INITIAL >'..' : Token;
DotTerm : <INITIAL, Generic >'.' : Token;
EquTerm : <INITIAL, Generic >'=' : Token;
HashLBracketTerm : <INITIAL >'#[' : Token;
HashTerm : <INITIAL >'#' : Token;
LAngleTerm : <INITIAL >'<' : Token;
QuestionTerm : <INITIAL >'?' : Token;
QuoteLBraceTerm : <INITIAL >"'\{" : Token;
QuoteTerm : <INITIAL >"'" : Token;
EscapedLBraceTerm : < EmbedBody, EmbedBodyInner >'\{' : Token;
TripleLBraceTerm : < EmbedHeader >'{{{' : Token;
LBraceTerm : <INITIAL , EmbedBody, EmbedBodyInner, Attr>'{' : Token;
LBracketTerm : <INITIAL, Attr>'[' : Token;
LParenTerm : <INITIAL , EmbedHeader, Attr>'(' : Token;
RAngleTerm : <INITIAL, Generic >'>' : Token;
EscapedRBraceTerm : <INITIAL >'\}' : Token;
TripleRBraceTerm : < EmbedBody >'}}}' : Token;
RBraceTerm : <INITIAL, EmbedBodyInner, Attr>'}' : Token;
RBracketTerm : <INITIAL, Attr>']' : Token;
RParenTerm : <INITIAL, EmbedHeader, Attr>')' : Token;
SemicolonTerm : <INITIAL >';' : Token;
StarTerm : <INITIAL >'*' : Token;
// Keywords are reflected to syntax highlight definitions through highlightgen tool.
// Please refer support/highlightgen/README.md if you want to add a keyword.
AliasTerm : <INITIAL, Generic >'alias' : Token; // Keyword: Statement
AlwaysCombTerm : <INITIAL, Generic >'always_comb' : Token; // Keyword: Statement
AlwaysFfTerm : <INITIAL, Generic >'always_ff' : Token; // Keyword: Statement
AssignTerm : <INITIAL, Generic >'assign' : Token; // Keyword: Statement
AsTerm : <INITIAL, Generic >'as' : Token; // Keyword: Statement
BindTerm : <INITIAL, Generic >'bind' : Token; // Keyword: Statement
BitTerm : <INITIAL, Generic >'bit' : Token; // Keyword: Type
BlockTerm : <INITIAL, Generic >'block' : Token; // Keyword: Statement
BBoolTerm : <INITIAL, Generic >'bbool' : Token; // Keyword: Type
LBoolTerm : <INITIAL, Generic >'lbool' : Token; // Keyword: Type
CaseTerm : <INITIAL, Generic >'case' : Token; // Keyword: Conditional
ClockTerm : <INITIAL, Generic >'clock' : Token; // Keyword: Type
ClockPosedgeTerm : <INITIAL, Generic >'clock_posedge' : Token; // Keyword: Type
ClockNegedgeTerm : <INITIAL, Generic >'clock_negedge' : Token; // Keyword: Type
ConnectTerm : <INITIAL, Generic >'connect' : Token; // Keyword: Statement
ConstTerm : <INITIAL, Generic >'const' : Token; // Keyword: Statement
ConverseTerm : <INITIAL, Generic >'converse' : Token; // Keyword: Direction
DefaultTerm : <INITIAL, Generic >'default' : Token; // Keyword: Conditional
ElseTerm : <INITIAL, Generic >'else' : Token; // Keyword: Conditional
EmbedTerm : <INITIAL, Generic >'embed' : Token; // Keyword: Structure
EnumTerm : <INITIAL, Generic >'enum' : Token; // Keyword: Structure
F32Term : <INITIAL, Generic >'f32' : Token; // Keyword: Type
F64Term : <INITIAL, Generic >'f64' : Token; // Keyword: Type
FalseTerm : <INITIAL, Generic >'false' : Token; // Keyword: Literal
FinalTerm : <INITIAL, Generic >'final' : Token; // Keyword: Statement
ForTerm : <INITIAL, Generic >'for' : Token; // Keyword: Repeat
FunctionTerm : <INITIAL, Generic >'function' : Token; // Keyword: Structure
GenTerm : <INITIAL, Generic >'gen' : Token; // Keyword: Statement
I8Term : <INITIAL, Generic >'i8' : Token; // Keyword: Type
I16Term : <INITIAL, Generic >'i16' : Token; // Keyword: Type
I32Term : <INITIAL, Generic >'i32' : Token; // Keyword: Type
I64Term : <INITIAL, Generic >'i64' : Token; // Keyword: Type
IfResetTerm : <INITIAL, Generic >'if_reset' : Token; // Keyword: Conditional
IfTerm : <INITIAL, Generic >'if' : Token; // Keyword: Conditional
ImportTerm : <INITIAL, Generic >'import' : Token; // Keyword: Statement
IncludeTerm : <INITIAL, Generic >'include' : Token; // Keyword: Structure
InitialTerm : <INITIAL, Generic >'initial' : Token; // Keyword: Statement
InoutTerm : <INITIAL, Generic >'inout' : Token; // Keyword: Direction
InputTerm : <INITIAL, Generic >'input' : Token; // Keyword: Direction
InsideTerm : <INITIAL, Generic >'inside' : Token; // Keyword: Conditional
InstTerm : <INITIAL, Generic >'inst' : Token; // Keyword: Statement
InterfaceTerm : <INITIAL, Generic >'interface' : Token; // Keyword: Structure
InTerm : <INITIAL, Generic >'in' : Token; // Keyword: Repeat
LetTerm : <INITIAL, Generic >'let' : Token; // Keyword: Statement
LogicTerm : <INITIAL, Generic >'logic' : Token; // Keyword: Type
LsbTerm : <INITIAL, Generic >'lsb' : Token; // Keyword: Literal
MixinTerm : <INITIAL, Generic >'mixin' : Token; // Keyword: Statement
ModportTerm : <INITIAL, Generic >'modport' : Token; // Keyword: Structure
ModuleTerm : <INITIAL, Generic >'module' : Token; // Keyword: Structure
MsbTerm : <INITIAL, Generic >'msb' : Token; // Keyword: Literal
OutputTerm : <INITIAL, Generic >'output' : Token; // Keyword: Direction
OutsideTerm : <INITIAL, Generic >'outside' : Token; // Keyword: Conditional
PackageTerm : <INITIAL, Generic >'package' : Token; // Keyword: Structure
ParamTerm : <INITIAL, Generic >'param' : Token; // Keyword: Statement
ProtoTerm : <INITIAL, Generic >'proto' : Token; // Keyword: Structure
PubTerm : <INITIAL, Generic >'pub' : Token; // Keyword: Structure
RepeatTerm : <INITIAL, Generic >'repeat' : Token; // Keyword: Repeat
ResetTerm : <INITIAL, Generic >'reset' : Token; // Keyword: Type
ResetAsyncHighTerm : <INITIAL, Generic >'reset_async_high' : Token; // Keyword: Type
ResetAsyncLowTerm : <INITIAL, Generic >'reset_async_low' : Token; // Keyword: Type
ResetSyncHighTerm : <INITIAL, Generic >'reset_sync_high' : Token; // Keyword: Type
ResetSyncLowTerm : <INITIAL, Generic >'reset_sync_low' : Token; // Keyword: Type
ReturnTerm : <INITIAL, Generic >'return' : Token; // Keyword: Statement
RevTerm : <INITIAL, Generic >'rev' : Token; // Keyword: Repeat
BreakTerm : <INITIAL, Generic >'break' : Token; // Keyword: Statement
SameTerm : <INITIAL, Generic >'same' : Token; // Keyword: Direction
SignedTerm : <INITIAL, Generic >'signed' : Token; // Keyword: Type
StepTerm : <INITIAL, Generic >'step' : Token; // Keyword: Repeat
StringTerm : <INITIAL, Generic >'string' : Token; // Keyword: Type
StructTerm : <INITIAL, Generic >'struct' : Token; // Keyword: Structure
SwitchTerm : <INITIAL, Generic >'switch' : Token; // Keyword: Conditional
TriTerm : <INITIAL, Generic >'tri' : Token; // Keyword: Type
TrueTerm : <INITIAL, Generic >'true' : Token; // Keyword: Literal
TypeTerm : <INITIAL, Generic >'type' : Token; // Keyword: Statement
P8Term : <INITIAL, Generic >'p8' : Token; // Keyword: Type
P16Term : <INITIAL, Generic >'p16' : Token; // Keyword: Type
P32Term : <INITIAL, Generic >'p32' : Token; // Keyword: Type
P64Term : <INITIAL, Generic >'p64' : Token; // Keyword: Type
U8Term : <INITIAL, Generic >'u8' : Token; // Keyword: Type
U16Term : <INITIAL, Generic >'u16' : Token; // Keyword: Type
U32Term : <INITIAL, Generic >'u32' : Token; // Keyword: Type
U64Term : <INITIAL, Generic >'u64' : Token; // Keyword: Type
UnionTerm : <INITIAL, Generic >'union' : Token; // Keyword: Structure
UnsafeTerm : <INITIAL, Generic >'unsafe' : Token; // Keyword: Structure
VarTerm : <INITIAL, Generic >'var' : Token; // Keyword: Statement
DollarIdentifierTerm : <INITIAL, Generic >/\$[a-zA-Z_][0-9a-zA-Z_$]*/ : Token;
IdentifierTerm : <INITIAL, Generic, EmbedHeader, Attr>/(?:r#)?[a-zA-Z_][0-9a-zA-Z_$]*/ : Token;
AnyTerm : < EmbedBody, EmbedBodyInner >/(?:[^{}\\]|\\[^{])+/ : 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;
DiamondOperatorToken : DiamondOperatorTerm : 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;
UnaryOperatorToken : UnaryOperatorTerm : 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;
HashLBracketToken : HashLBracketTerm : Token Comments;
HashToken : HashTerm : Token Comments;
QuestionToken : QuestionTerm : Token Comments;
QuoteLBraceToken : QuoteLBraceTerm : Token Comments;
QuoteToken : QuoteTerm : Token Comments;
LAngleToken : LAngleTerm : Token Comments;
EmbedLBraceToken : LBraceTerm : Token ;
EscapedLBraceToken : EscapedLBraceTerm : Token ;
TripleLBraceToken : TripleLBraceTerm : Token ;
LBraceToken : LBraceTerm : Token Comments;
LBracketToken : LBracketTerm : Token Comments;
LParenToken : LParenTerm : Token Comments;
LTMinusToken : LTMinusTerm : Token Comments;
MinusColonToken : MinusColonTerm : Token Comments;
MinusGTToken : MinusGTTerm : Token Comments;
PlusColonToken : PlusColonTerm : Token Comments;
RAngleToken : RAngleTerm : Token Comments;
EmbedRBraceToken : RBraceTerm : Token ;
EscapedRBraceToken : EscapedRBraceTerm : Token ;
TripleRBraceToken : TripleRBraceTerm : Token Comments;
RBraceToken : RBraceTerm : Token Comments;
RBracketToken : RBracketTerm : Token Comments;
RParenToken : RParenTerm : Token Comments;
SemicolonToken : SemicolonTerm : Token Comments;
StarToken : StarTerm : Token Comments;
AliasToken : AliasTerm : Token Comments;
AlwaysCombToken : AlwaysCombTerm : Token Comments;
AlwaysFfToken : AlwaysFfTerm : Token Comments;
AsToken : AsTerm : Token Comments;
AssignToken : AssignTerm : Token Comments;
BindToken : BindTerm : Token Comments;
BitToken : BitTerm : Token Comments;
BlockToken : BlockTerm : Token Comments;
BBoolToken : BBoolTerm : Token Comments;
LBoolToken : LBoolTerm : Token Comments;
CaseToken : CaseTerm : Token Comments;
ClockToken : ClockTerm : Token Comments;
ClockPosedgeToken : ClockPosedgeTerm : Token Comments;
ClockNegedgeToken : ClockNegedgeTerm : Token Comments;
ConnectToken : ConnectTerm : Token Comments;
ConstToken : ConstTerm : Token Comments;
ConverseToken : ConverseTerm : Token Comments;
DefaultToken : DefaultTerm : Token Comments;
ElseToken : ElseTerm : Token Comments;
EmbedToken : EmbedTerm : Token Comments;
EnumToken : EnumTerm : Token Comments;
F32Token : F32Term : Token Comments;
F64Token : F64Term : Token Comments;
FalseToken : FalseTerm : Token Comments;
FinalToken : FinalTerm : Token Comments;
ForToken : ForTerm : Token Comments;
FunctionToken : FunctionTerm : Token Comments;
GenToken : GenTerm : Token Comments;
I8Token : I8Term : Token Comments;
I16Token : I16Term : 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;
MixinToken : MixinTerm : 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;
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;
RevToken : RevTerm : Token Comments;
BreakToken : BreakTerm : Token Comments;
SameToken : SameTerm : 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;
TrueToken : TrueTerm : Token Comments;
TypeToken : TypeTerm : Token Comments;
P8Token : P8Term : Token Comments;
P16Token : P16Term : Token Comments;
P32Token : P32Term : Token Comments;
P64Token : P64Term : Token Comments;
U8Token : U8Term : Token Comments;
U16Token : U16Term : 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;
AnyToken: AnyTerm: Token;
// ----------------------------------------------------------------------------
// 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;
DiamondOperator : DiamondOperatorToken : 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;
UnaryOperator : UnaryOperatorToken : VerylToken;
// Symbol
Colon : ColonToken : VerylToken;
ColonColonLAngle: ColonColonLAngleToken: VerylToken;
ColonColon : ColonColonToken : VerylToken;
Comma : CommaToken : VerylToken;
DotDot : DotDotToken : VerylToken;
DotDotEqu : DotDotEquToken : VerylToken;
Dot : DotToken : VerylToken;
Equ : EquToken : VerylToken;
HashLBracket : HashLBracketToken : VerylToken;
Hash : HashToken : VerylToken;
Question : QuestionToken : VerylToken;
QuoteLBrace : QuoteLBraceToken : VerylToken;
Quote : QuoteToken : VerylToken;
LAngle : LAngleToken : VerylToken;
EmbedLBrace : EmbedLBraceToken : VerylToken;
EscapedLBrace : EscapedLBraceToken : VerylToken;
TripleLBrace : TripleLBraceToken : VerylToken;
LBrace : LBraceToken : VerylToken;
LBracket : LBracketToken : VerylToken;
LParen : LParenToken : VerylToken;
LTMinus : LTMinusToken : VerylToken;
MinusColon : MinusColonToken : VerylToken;
MinusGT : MinusGTToken : VerylToken;
PlusColon : PlusColonToken : VerylToken;
RAngle : RAngleToken : VerylToken;
EmbedRBrace : EmbedRBraceToken : VerylToken;
EscapedRBrace : EscapedRBraceToken : VerylToken;
TripleRBrace : TripleRBraceToken : VerylToken;
RBrace : RBraceToken : VerylToken;
RBracket : RBracketToken : VerylToken;
RParen : RParenToken : VerylToken;
Semicolon : SemicolonToken : VerylToken;
Star : StarToken : VerylToken;
// Keyword
Alias : AliasToken : VerylToken;
AlwaysComb : AlwaysCombToken : VerylToken;
AlwaysFf : AlwaysFfToken : VerylToken;
As : AsToken : VerylToken;
Assign : AssignToken : VerylToken;
Bind : BindToken : VerylToken;
Bit : BitToken : VerylToken;
Block : BlockToken : VerylToken;
BBool : BBoolToken : VerylToken;
LBool : LBoolToken : VerylToken;
Break : BreakToken : VerylToken;
Case : CaseToken : VerylToken;
Clock : ClockToken : VerylToken;
ClockPosedge : ClockPosedgeToken : VerylToken;
ClockNegedge : ClockNegedgeToken : VerylToken;
Connect : ConnectToken : VerylToken;
Const : ConstToken : VerylToken;
Converse : ConverseToken : VerylToken;
Defaul : DefaultToken : VerylToken; // avoid to conflict with Rust's Default trait
Else : ElseToken : VerylToken;
Embed : EmbedToken : VerylToken;
Enum : EnumToken : VerylToken;
F32 : F32Token : VerylToken;
F64 : F64Token : VerylToken;
False : FalseToken : VerylToken;
Final : FinalToken : VerylToken;
For : ForToken : VerylToken;
Function : FunctionToken : VerylToken;
Gen : GenToken : VerylToken;
I8 : I8Token : VerylToken;
I16 : I16Token : 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;
Mixin : MixinToken : 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;
Repeat : RepeatToken : VerylToken;
Reset : ResetToken : VerylToken;
ResetAsyncHigh: ResetAsyncHighToken: VerylToken;
ResetAsyncLow : ResetAsyncLowToken : VerylToken;
ResetSyncHigh : ResetSyncHighToken : VerylToken;
ResetSyncLow : ResetSyncLowToken : VerylToken;
Return : ReturnToken : VerylToken;
Rev : RevToken : VerylToken;
Same : SameToken : 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;
True : TrueToken : VerylToken;
Type : TypeToken : VerylToken;
P8 : P8Token : VerylToken;
P16 : P16Token : VerylToken;
P32 : P32Token : VerylToken;
P64 : P64Token : VerylToken;
U8 : U8Token : VerylToken;
U16 : U16Token : VerylToken;
U32 : U32Token : VerylToken;
U64 : U64Token : VerylToken;
Union : UnionToken : VerylToken;
Unsafe : UnsafeToken : VerylToken;
Var : VarToken : VerylToken;
// Identifier
DollarIdentifier: DollarIdentifierToken: VerylToken;
Identifier : IdentifierToken : VerylToken;
Any: AnyToken: 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 } };
GenericArgIdentifier : ScopedIdentifier { Dot Identifier };
// ----------------------------------------------------------------------------
// Expression
// ----------------------------------------------------------------------------
Expression : IfExpression;
IfExpression: { If Expression Question Expression Colon } Expression01;
Expression01: Expression02 { Expression01Op Expression02 };
Expression02: { Expression02Op } Factor [ As CastingType ];
Expression01Op: Operator01 | Operator02 | Operator03 | Operator04 | Operator05 | Operator06 | Operator07 | Star | Operator08;
Expression02Op: UnaryOperator | Operator06 | Operator05 | Operator03 | Operator04;
Factor: Number
| BooleanLiteral
| IdentifierFactor
| LParen Expression RParen
| LBrace ConcatenationList RBrace
| QuoteLBrace ArrayLiteralList RBrace
| CaseExpression
| SwitchExpression
| StringLiteral
| ( Msb | Lsb )
| InsideExpression
| OutsideExpression
| TypeExpression
| FactorTypeFactor
;
BooleanLiteral: True | False;
IdentifierFactor: ExpressionIdentifier [ FunctionCall | StructConstructor ];
FactorTypeFactor: { TypeModifier } FactorType;
FunctionCall: LParen [ ArgumentList ] RParen;
ArgumentList: ArgumentItem { Comma ArgumentItem } [ Comma ];
ArgumentItem: ArgumentExpression [ Colon Expression ];
ArgumentExpression: Expression;
StructConstructor: QuoteLBrace StructConstructorList [ DotDot Defaul LParen Expression RParen ] RBrace;
StructConstructorList: StructConstructorItem { Comma StructConstructorItem } [ Comma ];
StructConstructorItem: Identifier Colon Expression;
ConcatenationList: ConcatenationItem { Comma ConcatenationItem } [ Comma ];
ConcatenationItem: Expression [ Repeat Expression ];
ArrayLiteralList: ArrayLiteralItem { Comma ArrayLiteralItem } [ Comma ];
ArrayLiteralItem: ( Expression [ Repeat Expression ] | Defaul Colon Expression );
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: P8 | P16 | P32 | P64 | U8 | U16 | U32 | U64 | I8 | I16| I32 | I64 | F32 | F64 | BBool | LBool | Strin;
VariableType: Clock
| ClockPosedge
| ClockNegedge
| Reset
| ResetAsyncHigh
| ResetAsyncLow
| ResetSyncHigh
| ResetSyncLow
| Logic
| Bit;
UserDefinedType: ScopedIdentifier;
TypeModifier: Tri | Signed | Defaul;
FactorType: ( VariableType [ Width ] | FixedType );
ScalarType: { TypeModifier } ( UserDefinedType [ Width ] | FactorType );
ArrayType: ScalarType [ Array ];
CastingType: U8
| U16
| U32
| U64
| P8
| P16
| P32
| P64
| I8
| I16
| I32
| I64
| F32
| F64
| BBool
| LBool
| Clock
| ClockPosedge
| ClockNegedge
| Reset
| ResetAsyncHigh
| ResetAsyncLow
| ResetSyncHigh
| ResetSyncLow
| UserDefinedType
| Based
| BaseLess
;
// ----------------------------------------------------------------------------
// ClockDomain
// ----------------------------------------------------------------------------
ClockDomain: Quote Identifier;
// ----------------------------------------------------------------------------
// Statement
// ----------------------------------------------------------------------------
StatementBlock: LBrace { StatementBlockGroup } RBrace;
StatementBlockGroup: { Attribute } ( Block LBrace { StatementBlockGroup } RBrace | StatementBlockItem );
StatementBlockItem: VarDeclaration
| LetStatement
| ConstDeclaration
| GenDeclaration
| Statement
| ConcatenationAssignment
;
Statement: IdentifierStatement
| IfStatement
| IfResetStatement
| ReturnStatement
| BreakStatement
| ForStatement
| CaseStatement
| SwitchStatement
;
LetStatement: Let Identifier [ Colon [ ClockDomain ] ArrayType ] Equ Expression Semicolon;
IdentifierStatement: ExpressionIdentifier ( FunctionCall | Assignment ) Semicolon;
ConcatenationAssignment: LBrace AssignConcatenationList RBrace Equ Expression Semicolon;
Assignment: ( Equ | AssignmentOperator | DiamondOperator ) 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 In [ Rev ] 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: HashLBracket 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;
GenDeclaration: Gen Identifier Colon ( GenericProtoBound | Type ) Equ Expression Semicolon;
TypeDefDeclaration: Type Identifier Equ ArrayType Semicolon;
AlwaysFfDeclaration: AlwaysFf [ AlwaysFfEventList ] StatementBlock;
AlwaysFfEventList: LParen AlwaysFfClock [ Comma AlwaysFfReset ] RParen;
AlwaysFfClock: HierarchicalIdentifier;
AlwaysFfReset: HierarchicalIdentifier;
AlwaysCombDeclaration: AlwaysComb StatementBlock;
AssignDeclaration: Assign AssignDestination Equ Expression Semicolon;
AssignDestination: HierarchicalIdentifier
| LBrace AssignConcatenationList RBrace;
AssignConcatenationList: AssignConcatenationItem { Comma AssignConcatenationItem } [ Comma ];
AssignConcatenationItem: HierarchicalIdentifier;
ConnectDeclaration: Connect HierarchicalIdentifier DiamondOperator Expression Semicolon;
ModportDeclaration: Modport Identifier LBrace [ ModportList ] [ DotDot ModportDefault ] RBrace;
ModportList: ModportGroup { Comma ModportGroup } [ Comma ];
ModportGroup: { Attribute } ( LBrace ModportList RBrace | ModportItem );
ModportItem: Identifier Colon Direction;
ModportDefault: Input
| Output
| Same LParen ModportDefaultList RParen
| Converse LParen ModportDefaultList RParen;
ModportDefaultList: Identifier { Comma Identifier } [ Comma ];
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/BindDeclaration
// ----------------------------------------------------------------------------
InstDeclaration: Inst ComponentInstantiation Semicolon;
BindDeclaration: Bind ScopedIdentifier LTMinus ComponentInstantiation Semicolon;
ComponentInstantiation: Identifier Colon [ ClockDomain ] ScopedIdentifier [ Array ] [ InstParameter ] [ InstPort ];
InstParameter: Hash LParen [ InstParameterList ] RParen;
InstParameterList: InstParameterGroup { Comma InstParameterGroup } [ Comma ];
InstParameterGroup: { Attribute } ( LBrace InstParameterList RBrace | InstParameterItem );
InstParameterItem: Identifier [ Colon Expression ];
InstPort: LParen [ InstPortList ] RParen;
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: Type
| Inst ScopedIdentifier
| GenericProtoBound;
WithGenericParameter: ColonColonLAngle WithGenericParameterList RAngle;
WithGenericParameterList: WithGenericParameterItem { Comma WithGenericParameterItem } [ Comma ];
WithGenericParameterItem: Identifier Colon GenericBound [ Equ WithGenericArgumentItem ];
GenericProtoBound: ScopedIdentifier | FixedType;
// ----------------------------------------------------------------------------
// WithGenericArgument
// ----------------------------------------------------------------------------
WithGenericArgument: ColonColonLAngle [ WithGenericArgumentList ] RAngle;
WithGenericArgumentList: WithGenericArgumentItem { Comma WithGenericArgumentItem } [ Comma ];
WithGenericArgumentItem: GenericArgIdentifier
| FixedType
| Number
| BooleanLiteral
;
// ----------------------------------------------------------------------------
// 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 [ Equ PortDefaultValue ];
PortDefaultValue: Expression;
PortTypeAbstract: [ ClockDomain ] Interface [ ColonColon Identifier ] [ Array ];
Direction: Input
| Output
| Inout
| Modport
| Import
;
// ----------------------------------------------------------------------------
// Function
// ----------------------------------------------------------------------------
FunctionDeclaration: Function Identifier [ WithGenericParameter ] [ PortDeclaration ] [ MinusGT ScalarType ] StatementBlock;
// ----------------------------------------------------------------------------
// Import
// ----------------------------------------------------------------------------
ImportDeclaration: Import ScopedIdentifier [ ColonColon ( Star | MultipleImportList )] Semicolon;
MultipleImportList: LBrace MultipleImportItem { Comma MultipleImportItem } [ Comma ] RBrace;
MultipleImportItem: Identifier;
// ----------------------------------------------------------------------------
// Mixin
// ----------------------------------------------------------------------------
MixinDeclaration: Mixin ScopedIdentifier Semicolon;
// ----------------------------------------------------------------------------
// Unsafe
// ----------------------------------------------------------------------------
UnsafeBlock: Unsafe LParen Identifier RParen LBrace { GenerateGroup } RBrace;
// ----------------------------------------------------------------------------
// Module/Interface
// ----------------------------------------------------------------------------
ModuleDeclaration: Module Identifier [ WithGenericParameter ] [ For ScopedIdentifier ] [ WithParameter ] [ PortDeclaration ] LBrace { ModuleGroup } RBrace;
ModuleGroup: { Attribute } ( LBrace { ModuleGroup } RBrace | ModuleItem );
ModuleItem: GenerateItem;
InterfaceDeclaration: Interface Identifier [ WithGenericParameter ] [ For ScopedIdentifier ] [ WithParameter ] LBrace { InterfaceGroup } RBrace;
InterfaceGroup: { Attribute } ( LBrace { InterfaceGroup } RBrace | InterfaceItem );
InterfaceItem: GenerateItem | MixinDeclaration | ModportDeclaration;
GenerateIfDeclaration: If Expression GenerateNamedBlock { Else If Expression GenerateOptionalNamedBlock } [ Else GenerateOptionalNamedBlock ];
GenerateForDeclaration: For Identifier In [ Rev ] 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
| BindDeclaration
| ConstDeclaration
| GenDeclaration
| AlwaysFfDeclaration
| AlwaysCombDeclaration
| AssignDeclaration
| ConnectDeclaration
| FunctionDeclaration
| GenerateIfDeclaration
| GenerateForDeclaration
| GenerateBlockDeclaration
| TypeDefDeclaration
| EnumDeclaration
| StructUnionDeclaration
| ImportDeclaration
| AliasDeclaration
| InitialDeclaration
| FinalDeclaration
| UnsafeBlock
| EmbedDeclaration
;
// ----------------------------------------------------------------------------
// Package
// ----------------------------------------------------------------------------
PackageDeclaration: Package Identifier [ WithGenericParameter ] [ For ScopedIdentifier ] LBrace { PackageGroup } RBrace;
PackageGroup: { Attribute } ( LBrace { PackageGroup } RBrace | PackageItem );
PackageItem: ConstDeclaration
| GenDeclaration
| TypeDefDeclaration
| EnumDeclaration
| StructUnionDeclaration
| FunctionDeclaration
| ImportDeclaration
| AliasDeclaration
| EmbedDeclaration
;
// ----------------------------------------------------------------------------
// Alias
// ----------------------------------------------------------------------------
AliasDeclaration: Alias ( Module | Interface | Package ) Identifier Equ ScopedIdentifier Semicolon;
// ----------------------------------------------------------------------------
// Proto
// ----------------------------------------------------------------------------
ProtoDeclaration: Proto ( ProtoModuleDeclaration | ProtoInterfaceDeclaration | ProtoPackageDeclaration );
ProtoModuleDeclaration: Module Identifier [ WithParameter ] [ PortDeclaration ] Semicolon;
ProtoInterfaceDeclaration: Interface Identifier [ WithParameter ] LBrace { ProtoInterfaceItem } RBrace;
ProtoInterfaceItem: VarDeclaration
| ProtoConstDeclaration
| ProtoFunctionDeclaration
| ProtoTypeDefDeclaration
| ProtoAliasDeclaration
| ModportDeclaration
| ImportDeclaration
;
ProtoPackageDeclaration: Package Identifier LBrace { ProtoPacakgeItem } RBrace;
ProtoPacakgeItem: ProtoConstDeclaration
| ProtoTypeDefDeclaration
| EnumDeclaration
| StructUnionDeclaration
| ProtoFunctionDeclaration
| ProtoAliasDeclaration
| ImportDeclaration
;
ProtoConstDeclaration: Const Identifier Colon ( ArrayType | Type ) Semicolon;
ProtoTypeDefDeclaration: Type Identifier [ Equ ArrayType ] Semicolon;
ProtoFunctionDeclaration: Function Identifier [ WithGenericParameter ] [ PortDeclaration ] [ MinusGT ScalarType ] Semicolon;
ProtoAliasDeclaration: Alias ( Module | Interface | Package ) Identifier Colon ScopedIdentifier Semicolon;
// ----------------------------------------------------------------------------
// Embed
// ----------------------------------------------------------------------------
EmbedDeclaration: Embed LParen Identifier RParen Identifier EmbedContent;
EmbedContent: TripleLBrace { EmbedItem } TripleRBrace;
EmbedScopedIdentifier: EscapedLBrace ScopedIdentifier EscapedRBrace;
EmbedItem: EmbedLBrace { EmbedItem } EmbedRBrace
| EmbedScopedIdentifier
| Any;
// ----------------------------------------------------------------------------
// Include
// ----------------------------------------------------------------------------
IncludeDeclaration: Include LParen Identifier Comma StringLiteral RParen Semicolon;
// ----------------------------------------------------------------------------
// Description
// ----------------------------------------------------------------------------
DescriptionGroup: { Attribute } ( LBrace { DescriptionGroup } RBrace | DescriptionItem );
DescriptionItem: [ Pub ] PublicDescriptionItem
| ImportDeclaration
| BindDeclaration
| EmbedDeclaration
| IncludeDeclaration
;
PublicDescriptionItem: ModuleDeclaration
| InterfaceDeclaration
| PackageDeclaration
| AliasDeclaration
| ProtoDeclaration
| FunctionDeclaration
;
// ----------------------------------------------------------------------------
// SourceCode
// ----------------------------------------------------------------------------
Veryl: Start { DescriptionGroup };
セマンティックエラー
この付録では、Veryl のセマンティック解析器が出力する診断を一覧します。診断は、コンパイルを停止させるエラーと、コンパイルの継続を許す警告に分けて記載します。
エラー
ambiguous_elsif
このエラーは、elsif または else 属性を、先行する if 属性と一意に対応付けられない場合に報告されます。対応関係が明確になるよう周辺のコードを書き直してください。
ambiguous_identifier
このエラーは、識別子が複数のパッケージからインポートされていて、どのシンボルを指すのか決定できない場合に報告されます。識別子を明示的なスコープで修飾するか、インポートのいずれかを削除してください。
anonymous_identifier_usage
このエラーは、名前付きの識別子が必要な位置で匿名識別子(_)が使われた場合に報告されます。
call_non_function
このエラーは、関数でないシンボルを関数として呼び出した場合に報告されます。呼び出しを削除するか、実際の関数シンボルに置き換えてください。
combinational_loop
このエラーは、シーケンシャル要素を介さず、信号の値が純粋な組み合わせ論理を通じて自分自身に依存している場合に報告されます。ループは assign 宣言、always_comb ブロック、関数呼び出し、またはインスタンス化されたモジュールやインターフェースによって形成され得ます。少なくとも 1 つのパス上にレジスタ (always_ff) を挿入するか、依存関係の連鎖が終了するように設計を再構成してループを解消してください。
component_interface_mismatch
このエラーは、$comp::* の検証コンポーネントの使い方が、そのコンポーネントが宣言しているインターフェースと一致しない場合に報告されます。チェックされるのは、宣言の形式(クロック付きコンポーネントは inst、メソッドのみのコンポーネントは var)、パラメータ、ポート、インターフェースポート、および戻り値の幅を含むメソッド呼び出しです。使い方を修正するか、インターフェースが一致するようコンポーネントを更新してください。
cyclic_type_dependency
このエラーは、複数の型定義が循環的に互いを参照している場合に報告されます。参照のいずれかを削除または再構成して循環を断ち切ってください。
duplicate_argument
このエラーは、インスタンス化や関数呼び出しで同じ名前付き引数が複数回接続された場合に報告されます。重複した接続を削除してください。
duplicate_enum_variant
このエラーは、enum のバリアントが同じ enum の別のバリアントと同じ値を持つ場合に報告されます。そのバリアントに他と異なる値を与えてください。
duplicated_identifier
このエラーは、同じ識別子が同一スコープ内で複数回宣言された場合に報告されます。いずれかの宣言を別名にリネームしてください。
exceed_limit
このエラーは、インスタンスの深さ、総インスタンス数、エラボレーション評価サイズなどの内部制限を超えた場合に報告されます。Veryl.toml の [build] セクションで該当する制限を引き上げるか、設計を簡略化してください。
fixed_type_with_signed_modifier
このエラーは、固定幅の型(例:u32)に signed 修飾子を付けた場合に報告されます。signed 修飾子を削除してください。
generic_inference_failed
このエラーは、関数呼び出しの実引数が省略されたが、コンパイラが呼び出し時の引数から推論できなかった場合に報告されます。::<> で明示的にジェネリック引数を指定するか、変数宣言から幅を決定できる引数を渡してください。
implicit_clock_conversion
このエラーは、clock/reset 型でない値が clock/reset ポートに接続された場合、またはその逆の場合に報告されます。clock 性・reset 性は宣言でのみ与えられるため、その値を親側で clock/reset 型の信号に束縛し(例: let g: '_ clock = expr;)、その信号を接続してください。
include_failure
このエラーは、include 宣言が参照するファイルを読み込めない場合に報告されます。パスとファイルパーミッションを確認してください。
incompat_proto
このエラーは、for {proto} を持つモジュール/インターフェース/パッケージがそのプロトタイプの契約を満たさない場合に報告されます。プロトタイプに合うように実装を調整してください。
infinite_recursion
このエラーは、モジュールが直接的または間接的に自身をインスタンス化し、再帰を止める条件が無い場合に報告されます。再帰を終端させる条件を加える(例:ジェネリックパラメータと if による生成で打ち切る)か、モジュールが自身を参照しないように設計を組み直してください。
invalid_assignment
このエラーは、定数・パラメータ・ジェネリックパラメータなど代入できない種類のシンボルが代入の対象になった場合に報告されます。代入を削除してください。
invalid_cast
このエラーは、型キャストの元と先の型が互換でない場合に報告されます。互換性のある型を指定してください。
invalid_clock
このエラーは、clock 型でも単一ビット信号でもない信号がクロックとして接続された場合に報告されます。clock 型の信号を使用してください。
invalid_clock_assignment
このエラーは、clock/reset 型の信号が always_ff の中で代入された場合に報告されます。代わりに always_ff では logic の変数をトグルし、それを clock/reset 型の信号に束縛してください(例: let d: '_ clock = t;)。
invalid_clock_domain
このエラーは、モジュールインスタンスにクロックドメインアノテーションが付与された場合に報告されます。アノテーションを削除してください。
invalid_connect_operand
このエラーは、<> 接続演算子のオペランドとして有効なインターフェースオペランドが指定されていない場合に報告されます。
invalid_direction
このエラーは、ポート方向指定(input、output、inout など)がそれを許可していない位置に書かれた場合に報告されます。方向修飾子を削除してください。
invalid_embed
このエラーは、現在位置で許可されていない way/言語の組み合わせの embed 宣言が使われた場合に報告されます。
invalid_embed_identifier
このエラーは、embed 識別子の参照が (way: inline / lang: sv) 以外の embed ブロックに現れた場合に報告されます。
invalid_enum_variant
このエラーは、enum バリアントの値がその enum に指定されたエンコーディング(one-hot、gray など)と一致しない場合に報告されます。
invalid_factor
このエラーは、値ではないシンボル(例:モジュール名)が式の因子として使われた場合に報告されます。
invalid_for_range
このエラーは、for ループの範囲が不正な場合に報告されます。範囲は .. または ..= で書く必要があり、単なる式は範囲になりません。また範囲の境界は符号なしとして評価されるため、負の値にはできません。
invalid_for_step
このエラーは、for ループのステップが誘導変数を範囲の終端に向けて進めず、ループが終了しない場合に報告されます。誘導変数が確実に進むようにステップを修正してください。
invalid_import
このエラーは、参照先がインポート可能でない(private、またはインポート対象として無効)な場合に報告されます。
invalid_lsb
このエラーは、対応するビット幅を特定できない位置で lsb キーワードが使われた場合に報告されます。lsb を具体的なインデックスに置き換えてください。
invalid_mixin
このエラーは、mixin 宣言の対象をミックスインできない場合に報告されます。対象は、プロトタイプでなく、上書き可能なパラメータを持たず、自身が mixin 宣言を持たないインターフェースでなければなりません。また対象のメンバ名は、そのインターフェースに既に定義されているメンバと衝突してはいけません。
invalid_modifier
このエラーは、型修飾子が許可されていない位置で使われた場合に報告されます。修飾子を削除してください。
invalid_modport_item
このエラーは、modport の項目が期待する種類でない識別子を参照している場合に報告されます。
invalid_msb
このエラーは、対応するビット幅を特定できない位置で msb キーワードが使われた場合に報告されます。msb を具体的なインデックスに置き換えてください。
invalid_number_character
このエラーは、数値リテラルが基数に対して不正な文字を含む場合(例:2進リテラル中の16進数字)に報告されます。
invalid_operand
このエラーは、演算子と互換性のない種類のオペランドが使われた場合に報告されます。
invalid_port_default_value
このエラーは、ポートのデフォルト値がそのポートの型や方向に対して無効な場合に報告されます。
invalid_range
このエラーは、範囲が不正な場合に報告されます。例えば、終端を含まない範囲の下限が上限より小さくない場合です。
invalid_range_assign
このエラーは、代入の左辺の配列範囲選択が、定数で・範囲内で・昇順のスライスになっていない場合に報告されます。
invalid_reset
このエラーは、reset 型でも単一ビット信号でもない信号がリセットとして接続された場合に報告されます。reset 型の信号を使用してください。
invalid_statement
このエラーは、文がそれを許可していない位置に書かれた場合(例:関数外の return)に報告されます。文を削除するか、適切な位置に移動してください。
invalid_tb_usage
このエラーは、$tb::* または $comp::* のコンポーネントが #[test] モジュールの外で使われた場合に報告されます。テストモジュール内に移動してください。
invalid_test
このエラーは、#[test] 宣言が不正な形をしている場合に報告されます。
invalid_type_declaration
このエラーは struct、enum、union のデータ型がインターフェース宣言内で定義された場合に報告されます。
invalid_unsized_literal
このエラーは、幅指定のないリテラルが自己決定のコンテキストで使われ、周囲から幅を決定できない場合に報告されます。1'b0 のようにリテラルへ明示的な幅を与えてください。
invalid_wavedrom
このエラーは、ドキュメンテーションコメント中の WaveDrom ブロックが不正な場合に報告されます。
invisible_identifier
このエラーは、スコープ外(可視性境界をまたぐなど)から識別子を参照した場合に報告されます。
last_item_with_define
このエラーは、カンマ区切りリストの最後の要素に ifdef / ifndef / elsif / else 属性が付与され、境界が曖昧になる場合に報告されます。
member_access_on_array
このエラーは、配列に対して直接メンバーアクセスを行った場合に報告されます。先に配列をインデックスしてください。
mismatch_attribute_args
このエラーは、属性に渡された引数が期待される形式と一致しない場合に報告されます。属性が期待するシグネチャに合うよう引数を調整してください。
mismatch_clock_domain
このエラーは、明示的な unsafe (cdc) ブロックなしに信号がクロックドメイン境界を越えて使われた場合に報告されます。
mismatch_function_arity
このエラーは、関数呼び出し時の引数の個数が関数の宣言と一致しない場合に報告されます。
mismatch_generics_arity
このエラーは、呼び出し時のジェネリック引数の個数が宣言と一致しない場合に報告されます。
mismatch_type
このエラーは、ある値が別の型を要求する位置で使われた場合に報告されます。代入の型不一致や、関数引数の型不一致などが含まれます。
missing_clock_domain
このエラーは、モジュールが複数のクロックを持つのに、ポートにクロックドメインアノテーションが付いていない場合に報告されます。アノテーションを追加してください。
missing_clock_signal
このエラーは、囲っているモジュールにクロック信号が無いまま always_ff ブロックが使われた場合に報告されます。clock ポートを追加してください。
missing_default_argument
このエラーは、デフォルト値を持つジェネリックパラメータの後に続くパラメータがデフォルト値を持たない場合に報告されます。デフォルトパラメータはリストの末尾に置く必要があります。
missing_if_reset
このエラーは、always_ff ブロックがリセット信号を持つにもかかわらず if_reset 文が無い場合に報告されます。リセット動作を記述する if_reset ブロックを追加してください。
missing_reset_signal
このエラーは、always_ff ブロックで if_reset を使用しているにもかかわらず、囲っているモジュールにリセット信号が無い場合に報告されます。reset ポートを追加するか、リセット信号を接続してください。
missing_tb_port
このエラーは、$tb::* のテストベンチコンポーネントが必要なポート(例:$tb::reset_gen の clk ポート)を接続せずにインスタンス化された場合に報告されます。インスタンス化時に必要なポート接続を追加してください。
missing_tri
このエラーは、inout ポートが tri 型修飾子なしで宣言された場合に報告されます。tri 修飾子を追加してください。
mixed_function_argument
このエラーは、同じ関数呼び出しの中で位置引数と名前付き引数が混在している場合に報告されます。どちらか一方に統一してください。
multiple_assignment
このエラーは、単一の信号が複数のプロシージャルブロックや assign 文から代入される場合に報告されます。各信号は単一の駆動元から駆動してください。
multiple_default
このエラーは、同じモジュール内でデフォルトのクロックまたはリセットが複数回指定された場合に報告されます。
non_constant_select_width
このエラーは、+: / -: / step の部分選択の幅が定数でない場合に報告されます。幅には定数式を使ってください。
non_positive_value
このエラーは、正の型(p8 / p16 / p32 / p64)にゼロ以下の値を代入した場合に報告されます。0 より大きい値を使用してください。
private_member
このエラーは、private として宣言されたメンバーが、その宣言スコープの外からアクセスされた場合に報告されます。
private_namespace
このエラーは、private として宣言された名前空間が、その宣言スコープの外から参照された場合に報告されます。
referring_before_definition
このエラーは、前方宣言が必要な位置で識別子が宣言前に使われた場合に報告されます。定義を参照位置より前に移動してください。
reserved_identifier
このエラーは、予約された __ 接頭辞を持つ識別子が使われた場合に報告されます。別の名前を選んでください。
sv_keyword_usage
このエラーは、SystemVerilog のキーワードが Veryl の識別子として使われた場合に報告されます。生成された SystemVerilog で衝突しないようにリネームしてください。
sv_with_implicit_reset
このエラーは、同期性と極性が暗黙の reset 型ポートが SystemVerilog モジュールのポートに接続された場合に報告されます。reset_async_low や reset_sync_high のような明示的な型を使用してください。
too_large_enum_variant
このエラーは、enum バリアントに与えられた明示的な値が enum のビット幅で表現できない場合に報告されます。値を小さくするか、enum のビット幅を広げてください。
too_large_number
このエラーは、数値リテラルが指定されたビット幅で表現可能な最大値を超えている場合に報告されます。ビット幅を広げるか、値を小さくしてください。
too_much_enum_variant
このエラーは、enum のバリアント数がそのビット幅で符号化可能な数を超える場合に報告されます。enum のビット幅を広げるか、バリアント数を減らしてください。
type_inference_conflict
このエラーは、型を持たない var への複数の代入で推論される型が一致しない場合に報告されます。型を持たない変数へのすべての代入で型が一致する必要があるか、または明示的な型を付けて変数を宣言する必要があります。
type_inference_not_supported
このエラーは、型を持たない let または const 宣言の右辺式が型推論をサポートしていない場合に報告されます。演算子を含む式、ビット幅なしリテラル、連結、if / case 式は推論できません。明示的な型注釈を付けることでこのエラーを解消できます。
unassignable_output
このエラーは、読み取り専用などで代入できない式が出力ポートに接続された場合に報告されます。
undefined_identifier
このエラーは、宣言される前の識別子を参照した場合に報告されます。識別子を宣言するか、綴りを修正してください。
unevaluatable_value
このエラーは、エラボレーション時に解決できない値が、定数を必要とする位置(配列サイズなど)で使われた場合に報告されます。
unexpandable_modport
このエラーは、modport 参照を個別のポート群に展開できない場合に報告されます。
unknown_attribute
このエラーは、属性名がコンパイラに認識されない場合に報告されます。属性を削除するか、属性名を修正してください。
unknown_embed_lang
このエラーは、embed 宣言で未サポートの言語識別子が使われた場合に報告されます。
unknown_embed_way
このエラーは、embed 宣言で未サポートの way 識別子が使われた場合に報告されます。
unknown_include_way
このエラーは、include 宣言で未サポートの way 識別子が使われた場合に報告されます。
unknown_member
このエラーは、struct、union、interface が宣言していないメンバーへのアクセスが行われた場合に報告されます。メンバー名を修正してください。
unknown_msb
このエラーは、msb が参照するビット幅をコンパイラが解決できない場合に報告されます。代わりに具体的なインデックスを指定してください。
unknown_param
このエラーは、モジュールが宣言していないパラメータをインスタンス化時に上書きしようとした場合に報告されます。上書きを削除するか、パラメータ名を修正してください。
unknown_port
このエラーは、モジュールが宣言していないポート名で接続が指定された場合に報告されます。接続を削除するか、ポート名を修正してください。
unknown_tb_port
このエラーは、$tb::* のテストベンチコンポーネントに存在しないポート名で接続が指定された場合に報告されます。未知の接続を削除するか、ポート名を修正してください。
unknown_unsafe
このエラーは、unsafe(...) ブロックで有効な unsafe カテゴリでない識別子が使われた場合に報告されます。
unresolvable_generic_expression
このエラーは、ジェネリック定義位置からジェネリックパラメータ中の式を解決できない場合に報告されます。
wrong_seperator
このエラーは、識別子間で誤った区切り文字(:: と .)が使われた場合に報告されます。正しい区切り文字に置き換えてください。
zero_width_number
このエラーは、数値の幅が0として宣言された場合に報告されます。
警告
invalid_identifier
この警告は、識別子が設定された命名規則(Lint を参照)に違反している場合に報告されます。規則に従うように識別子をリネームしてください。
invalid_logical_operand
この警告は、複数ビット値が論理演算子のオペランドとして使われた場合に報告されます。== などで 1 ビットに縮約してください。
invalid_select
この警告は、ビット選択や範囲選択が無効(範囲外など)な場合に報告されます。
mismatch_assignment
この警告は、代入の元と先の型が一致しない場合に報告されます。
mismatch_function_arg
この警告は、引数の型が関数のパラメータ型と一致しない場合に報告されます。
missing_port
この警告は、モジュールが宣言されたポートのいずれかを接続せずにインスタンス化された場合に報告されます。不足しているポート接続を追加してください。
missing_reset_statement
この警告は、if_reset を伴う always_ff ブロック内で宣言されたレジスタに if_reset 分岐内での代入が無い場合に報告されます。リセット値を定義するため、そのレジスタへの代入を追加してください。
mixed_struct_union_member
この警告は、struct または union 内で 2-state メンバーと 4-state メンバーが混在している場合に報告されます。メンバーをいずれか一方に統一してください。
unassign_variable
この警告は、宣言された変数に一度も値が代入されない場合に報告されます。
uncovered_branch
この警告は、信号が一部の分岐でしか代入されず、ラッチが推論される場合に報告されます。デフォルトの代入を加えるか、すべての分岐で代入してください。
unenclosed_inner_if_expression
この警告は、入れ子になった if 式が括弧で囲まれていない場合に報告されます。優先順位を明示するため括弧を追加してください。
unsigned_arith_shift
この警告は、符号なしオペランドに算術シフトを適用した場合に報告されます。論理シフトと結果が同じになるため、意図を明確にするには論理シフトを使用してください。
unused_return
この警告は、関数呼び出しの戻り値が破棄された場合に報告されます。結果を変数に代入するか、戻り値を無視する設計の関数を使用してください。
unused_variable
この警告は、宣言された変数が一度も参照されない場合に報告されます。意図的に未使用であることを示すには変数名の先頭に _ を付けるか、宣言を削除してください。