さて、入門1ではメリットだけを書きましたが、基本的に私はメリットだけ推して、デメリットを書かない論調が大っ嫌いです。デメリットがない文章はその時点で信用しません。
というわけで、今回はメリットデメリットを紹介しようと思います。
私が使ってみた上で、実感として感じるメリットは以下です。
- IntelliJ IDEAといった統合開発環境の強力な補完機能が使える
- Input,Outputの接続<>が強力
- 接続を後から書ける
- Wire、Regが本当に配線とレジスタという物理的な意味を持つ
- Wire、Regといった要素をプログラマブルに生成出来る
- 継承を利用することでOOPライクな思考が出来る
逆に私が使った見た上で、これ駄目だろって感じたデメリットは以下です。
- 新しい言語とパラダイムを学ばなければならない
- 非同期リセットがない (※Chisel3では実装予定とはなっている)
- IOポートをVerilogに変換した時の命名を指定出来ない
- エラーが超不親切
- MSYSでVerilatorシミュレーションが出来ない
では、まずはメリットから述べていきましょう。
メリット
統合開発環境の強力な補完機能が使える
Verilogを書く際の強力なエディタってなんなんでしょうか?私は正直よく分かりません。
一応Emacs+Companyが私の基本開発環境ですが、Verilogはあんまり良い感じに補完してくれないですね。Visual Studio Codeもまぁ、頑張ってるけど、こう、C++とかJavaとかのように、これって感じじゃない。
一方、Chiselは結局ただのScalaなので、IntelliJ IDEAやEclipseのScalaプラグインなどを入れれば統合開発環境の恩恵を受けることが出来ます。
Chiselで久しぶりにScalaを触ったのですが、実用に十分な力があると言って良いです。Scalaが出たばっかの頃の使えないIDEのイメージが強くて、Scala(のIDE)なんて使えねーと想っていたのですが、今回Scala(のIDE)の評価を上方修正しました。
わざわざ「Scala(のIDE)の評価」と、(のIDE)を薄く書いていますが、私は言語の評価と統合開発環境の評価はほぼ直結すると思っています。無論、ライブラリや、フレームワークなど、評価の全てではありませんが。
そういう意味で、Chiselは良い選択肢でしょう。
接続記述が強力、抽象的に書ける
<>については、既にChisel入門1であげたので、具体的に説明はしませんが、モジュールAのインスタンスaとモジュールBのインスタンスbの入力と出力を単純に繋ぎたい場合、以下の一文で済みます。
a.io <> b.io
実際には、こんなに単純な接続なんてありませんから、もうちょっと構造を工夫するなどの考察は必要ですが、Chiselの接続は最後に書かれた物が有効などのルールも併用すれば、かなり短く書くことが出来ます。これはかなりメリットですよね。
もう一つのメリットは、モジュールのインスタンスを宣言した後で、接続記述を書ける、と言う点をあげておきたいと思います。すなわち、インスタンス化記述と接続記述が分離されていることがメリットです。
val a = Module(new AModule) val b = Module(new BModule) a.io.INPUT := b.io.OUTPUT
これにより以下のメリットが得られます。
- a.io.INPUT等の様に書くことでどのインスタンスの処理をしているかが各行で明確である
- 分離されたことにより、接続は接続でプログラマブルに処理出来る
各行で明確だと、後から読む側としては、非常にありがたいですよね。百個ぐらいポートがあることも割とあるので、後から読みやすくなります。
また、接続を分離して、プログラマブルに書けると言うことは、記述量の圧縮に繋がります。例えば、Module A,B,Cのインスタンスの接続を統一的に扱いたい場合を考えてみましょう。
Verilogならこのようになるでしょう。
module D( ポート定義 ); A a(ポート接続); B b(ポート接続); C c(ポート接続); endmodule;
上記のモジュールDを用意した後、必要な場所でDを実体化します。
D d1(ポート接続); D d2(ポート接続);
一方、Chiselだと以下の様になるでしょう。
val a1 = Module(new A),a2 = Module(new A) val b1 = Module(new B),b2 = Module(new B) val c1 = Module(new C),c2 = Module(new C) //接続処理 def d(a :A,b :B,c:C) =>{ //a,b,cの接続を書く } d(a1,b1,c1) d(a2,b2,c2)
大して変わらない様に見えますが、Verilogだと、Dという本来不必要なモジュールの階層が必要になります。後からa1の出力が他で必要だったとかなった場合、Dを修正する必要があり、あー面倒くさい。Chiselの方は必要ないですよね。
なお、階層分けすることが悪い訳ではないので、やっぱりVerilogの方がいい、と言われるかも知れません。ここでは、あくまでmoduleを作らなくても、同じ接続を省略出来るという点に着目してください。
さらに、RegやModule、Wireなどもプログラマブルに生成出来るので、配列やら再帰関数を使えば、複雑な物も割とさっくり書けます。Verilogだと、あれつないでこれ繋いで、あーーーとかなってるところです。
継承によるOOPが使えるのもプログラム畑の人間からしたらメリットですね。
ただし、これらは人によってはデメリットと捉えることには注意しておきましょう。なぜなら、Verilogの方が具体的だからです。
例えば、以下のVerilogを考えてみます。
wire boutput; AModule a ( .INPUT(boutput), //他略 ); BModule b( .OUTPUT(boutput), //他略 );
AModuleのaのINPUTは常にboutputに繋がります。そして、BModuleのbのOUTPUTも常にboutputに繋がります。a.io.INPUTが一体何処に繋がっているのか具体的に記述されていない、という現象は起こりえません。
某staticおじさんよろしく、プログラミングの世界では抽象度の低いことは良いこととはあまり見なされませんが、ハードの世界は違います。何故なら、ハードは最終的に具体的な物(回路)になり、その具体的な物に対して検証をせねばならないからです。
従って、抽象的であることは悪である、と言われた時、それを否定するだけの理屈を私は持っていません。同じ記述の素子であっても、性能ばらつくんだから。
え、ソフトだったら?抽象化が絶対の正義で真理です。コピペマンはコテンパンにしてやんよ、かかってこい。
デメリット
さて、メリット紹介が終わったら次はデメリットですね。がっつり行きましょう。
新しい言語とパラダイムを学ばなければならない
Chisel入門1では、あたかもVerilogと一対一対応で書ける様な雰囲気で書きましたが、やっぱり別言語です。効率の良い書き方などを求める場合、どうしても学習からは逃げられません。
(一応、inline verilogという逃げ道もあるけど)
どんな言語や機能でもそうですが、これが最大の欠点ですよね。
Verilogなんて何も変化してませんもんね。OVL対応とか、そういうツールの変化はあったかも知れないけど。え?System Verilog?さぁ………。
どんな開発現場でも、新技術は導入したくないはずです。その新技術を使うことによるメリット、コストの評価も、信頼性の評価もしなければならないし、新技術なんてあっても良いことないです。技術なんて、少なくとも数百年ぐらいは同じ技術だけで食ってけるのが一番。イノベーションなんて打ち壊せー。ビバ、ラッダイト運動。
学習を覚悟しなければならない、それはやっぱり一番大きなデメリットだと思います。
非同期リセットがない(実装予定)
ちょーっとこれは論外なんじゃないですかねぇ。
これはたぶんだけど、ChiselがメインターゲットとしてるのがFPGAだからっぽい雰囲気。
一応、Chisel3.2で実装予定とのことだけど、元々Chisel3.0で実装予定が3.1になり、3.2に、3.3に3.4にとずるずる後ろに下がっていきそう。(追記:めでたく3.3に降格されました!) 元々非同期リセットいらないと言ってた人達らしいので、やる気が低いのでしょうね………。ただ、実装するとは約束しているようです。
一応、回避策はあって、BlackBoxを使うという手段もあるけど、BlackBoxを使うと今度はFIRRTLのシミュレーションが出来ないでしょうね。
なお、非同期リセットはないのに、マルチクロックドメインには対応しているという不思議。
ちなみに、非同期リセットの日本語記事がありました。この記事で最後の方でSpinal HDLというChiselインスパイア言語を知ったのでそっちもちらっと見てみました。
サクッと読んだ感じ、Spnial HDLは良さそうな感じですが、やっぱ金がね。Chiselは金が動いてる感あって安心感あるもんね。
ただ、JavaScriptを見てる限り、Alt系の言語は激動の歴史を辿る可能性があります。Chiselにオールインするよりも、色々見ておく方がいいとは思う。
IOポートをVerilogに変換した時の命名を指定出来ない
全てがChiselで終わるなら、別にVerilogに変換した時にどういう命名になろうが、そこまで気にしないでいいです。
でも、全てがChiselで済む訳じゃないですよね。ChiselからVerilogを呼び出すのはまだ良いのですが、ChiselをVerilogから呼び出すってことも当然ある訳で。
例えば、以下の様なモジュールの入力ポートは、「in」という名前にせよ、という指令が来ていても、Chiselでは出来ないのです。
class A extends Module{ val io =IO(new Bundle{ val in = Input(new Bool) //これは「input io_in」というポートになる }) }
いや、一応出来るんですよ。こうすれば。
class A extends Module{ val io =IO(new Bundle{}) val in = IO(Input(new Bool)) //これは「input in」というポートになる }
ただ、こうすると、Bundleは使えないし、Chiselのルール的にI/Oなのかよくわからんくなる。(io.~は入出力)
だから、こんな機能付けて欲しいですよね。
io.in.setName("in")
というのも、これ、Chisel2ではあったそうなのですが、Chisel3では消されました。
で、やっぱ不満に思っている人が居るらしく、issueにありました。requestとして受け入れられて、今のところ3.2で実装予定(あくまで予定)のようです。 拒否されました。ラッパー使えと。え、そういう問題なの!?
ただ、何をどう実装するつもりなのか、ノープランぽいって言うか、とりあえず全部3.2にしとけって感じなので、ほぼ確実に3.2では実装されないでしょう。
エラーが超不親切
ビックリするくらい不親切だと思うわ。C++のtemplateと良い勝負してる気がする。
C++のエラーメッセージがだいぶ親切になってきたのは近年だから、まだまだ時間はかかりそうかなー。
MSYSでVerilatorシミュレーションが出来ない
Verilatorは問題ないのですが、Chiselが吐き出すC++コードがUnixのシステム(sys/mmanとか)に依存しています。
なので、VerilatorシミュレーションはMSYSでやるのは諦めました。うん、めんどうくさい。WSLでUbuntu使えばいいだけだし。
でも、できればさ、MSYSで完結したかったなぁ。うん、超個人的なデメリットだけど。
え、Cygwin?………やーだー……
どうでしたでしょうか。
正直、結構デメリットの非同期リセットが出来ないというのと、ポート名を変えられないという点が結構痛くて、部分的にしかプロジェクトでは使えないと思っています。
が、逆に私が論外すぎるなぁと思ったのもこの二点ぐらいしかないので、3.2でどのぐらい改善してくるのか、楽しみにしております。
というわけで、ようやく次回から、Chiselの使い方の説明に入っていこうかな。