Ryuz's tech blog

FPGAなどの技術ブログ

Verilogの課題を再考してみる

はじめに

RTL開発を行える言語は多数ありますが、Verilog と その後継である SystemVerilog はその有力な言語の1つかと思います。

一方で、もともとシミュレーション言語として開発された後に合成にも使われるようになった経緯と、そもそもとても長い歴を持っている、早い話が古い言語であるため、後方互換を捨てない限りは永久に負の遺産も抱え続けることになります。

詳しい言語の歴史は Wikipedia などを見て頂くとして、20年ほど前ぐらいに私が半導体関係のお仕事に携わってた頃は周りは Verilog-1995 を使っていたようです。もっともこのころ私はまだ Verilog は使っていなくて、後に FPGA をやり始めて、当時 Xilinx の ISE で Verilog-2001 あたりから使い始めたと記憶しております。

そののち Vivado になり SystemVerilog が使えるようになり、少しづつ試しつつ verilator との出会いなどもあって、SystemVerilog も限定的に使い始めたりしています。

Verilog に限った話ではないのですが、仕様の足し算で済む話は言語の進化でどんどん解消していきますが、引き算に関してはなかなか難しく、「新しくこういう機能用意したのでなるべく古いものは使わないでね」としていくしかないので、なかなか難しい問題があります。

私が持つ Verilog の不満どころ

ということで、私の思う Verilog の不満を少し書いてみるわけです。

合成とシミュレーションで動作が変わる場合がある

多分これが昔からある一番の課題でしょう。

  • うっかり書いたブロッキング代入で実行順序が保証されない
  • always_ff @( clock ) begin とかに勝手にコード補完されてて変なラッチが生成されてた
  • 源振の違う同じ周波数のクロックでsimだと誤差が無いので動いてしまった

とか、いろいろあるあるです。

この辺りが Veryl だと CDCの扱いなども含めてかなり解消されているようで、期待している次第だったりします。

バス幅ゼロを許容していない

地味にこれ私すごくフラストレーション持っているのですが、[0:0] 表記で1bit が生成されてしまうので 0bit 幅ってできないのですよね。

ポート数に合わせてIDの幅とか決めたいときに $clog2(N) みたいなことをすると、N=1 の時だけ個別に場合分けしないといけない。

しばし

parameter int ID_WIDTH = N > 1 ? $clog2(N) : 1;
logic [ID_WIDTH -1:0] id;

みたいなコードを書く羽目になり、N = 1 時には id には 0 しか来ないので 論理圧縮で消されるのを祈る みたいなことをよくやります。美しくないです。

Python や Rust など [0:10] や 0..10 の表記で 0から9までを生成してくれる系の言語に慣れていると特にいらいらしてしまうわけです。

マクロやジェネリクスがまだまだ弱い

Verilog-2001 から導入された generate 文は超強力で、parameter との組み合わせでかなりのメタプログラミングが出来るので、個人的にはこれはとても気に入ってる構文です。

一方で、ジェネリクスとして綺麗に定義されているわけでもなく、マクロに関しては C言語並みです。

もともと C言語のマクロが非力すぎるというのあって、IF しかないのでチューリング完全でなく、FORなどの繰り返しが無いので、割り込みテーブルの静的生成とか、特定用途のコード生成では MASM (マクロアセンブラ) にも負けていたように思います(苦笑)。

Verilog では generate 文でこのあたりはかなり解消されましたが、それでも Lisp や Rust のマクロのような、一度 AST に持っていくようなちゃんとしたマクロがあると応用性は高そうな気はしています。

Veryl にとっても期待しています。

勝手に wire 推定する

`default_nettype none

しないと 1bit の wire になってしまうのは、いまだに嵌ることがたまにあります。

言語的にはそうしないと後方互換が保てないのでどうしようもないような気がします。

automatic と書かない限り static になる

C言語に慣れていると、ブロックの先頭で定義した変数は自動変数っぽいイメージを持つが、実はこれ Verilog との互換の為にデフォルト static なのでうかつなことをするとラッチが生成されたりとかあらぬことが起こります。

ちょっと厄介なことに xsim(Vivado Simulator)がデフォルトで automatic だったりするので、しばらく気が付かなかったりしました(今はどうなったのかな?)。

仕様が膨大すぎる

1800-2017.pdf は 1315ページもあります。 先の nettype とか var と wire と データ型とか初心者泣かせすぎな仕様も結構ある気がします。

単に wire と書くとデフォルトの DataType で wire logic が推定され、単に logic と書くとデフォルトの kind として var logic が推定されるので SV が Verilog と見た目上の互換になるとか、マニアックすぎる後方互換確保にいまだに混乱してます。 ちゃんと理解できてる自身があまりないまま使っていたりします。

おわりに

今回は思いつくまま不満を書いてみる回になってしまいました。

今は AI もあるので、うっかりなバグは減りましたが、それでも罠になりうることを知っておくのは悪いことではないかとは思います。

SystemVerilog になって class や可変長配列など便利な機能も増えましたし SVA の登場や UVM の存在など、今後も進化は続くのかなとは思います。 一方で、仕様がより複雑になる事はあっても単純になる事は無いと思われるので、今後も覚えることは増えそうです。

足し算しか出来ない言語進化に対して、別言語を再定義することで結果的に引き算を行って、シンプルな言語を作ろうとする Veryl の意義を改めて再確認した次第です。