特徴

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

リアルタイム診断

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

diagnostics

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

自動フォーマット

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

format

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

組み込みテスト

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

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

依存関係管理

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

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

ジェネリクス

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

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

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

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

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

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

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

SystemVerilog Veryl
module ModuleA (
    input  i_clk_a,
    input  i_dat_a,
    output o_dat_a,
    input  i_clk_b,
    input  i_dat_b,
    output o_dat_b
);
    // 注意!!!
    // i_clk_a から i_clk_b へ
    assign o_dat_b = i_dat_a;
endmodule
module ModuleA (
    i_clk_a: input  `a clock,
    i_dat_a: input  `a logic,
    i_dat_a: output `a logic,
    i_clk_b: input  `b clock,
    i_dat_b: input  `b logic,
    i_dat_b: output `b logic,
) {
    unsafe (cdc) {
        assign o_dat_b = i_dat_a;
    }
}

末尾カンマ

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

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

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

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

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

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

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

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

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

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

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

always_ff での複合代入演算子

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

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

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

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

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

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

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

ビット連結における repeat

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

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

if / case

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

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

範囲 for / inside / outside

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

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

msb 記法

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

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

let

変数宣言と同時に値を束縛する専用の let 文が用意されており、SystemVerilogではサポートされていなかった様々な場所で使用することができます。

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

名前付きブロック

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

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

可視性制御

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

SystemVerilog Veryl
module ModuleA;
endmodule

module ModuleB;
endmodule
pub module ModuleA {
}

module ModuleB {
}
1

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