みなさん、Verilog やってますか? 私も最近DNNの計算をFPGAでやりたくなり、押入れの中でホコリをかぶっていた Altera DE0 (nanoじゃないやつ) を引っ張り出してきました。
##動機・経緯
Linux で Quartus II と ModelSim を動かし、Lチカと4bit×4bitの乗算器くらい作ったところで、テストベンチ(ソフト開発で言うユニットテストみたいなやつ)を Verilog で書くのがかったるくなってきました。回路の記述は Verilog でいいとしても、テストくらいは好きな言語で書きたいよね、でもそんな方法あるのか?などと思い始めたのが5日前(2017.9.5)。
Google先生に尋ねたところ、「Verilator というやつを使うと VerilogをC++に翻訳して、激速でシミュレーションできるらしい」ということがわかりました。ただし、テストベンチは C++ で書く必要があります。
じゃーその生成されたC++を wrap して Ruby に 組み込めばいいんじゃね?と思いついたのも5日前。しかしながら、当方 SWIG も C++ も経験ない。。。CのライブラリをRubyに組み込んだことはありますけど。まあ勉強と思ってやってみっか、と決心しました。
でまあ、仕事しながら調べては書き、試し、などしているうちに、どうやら所望のことができるようになったのが昨日。Rakeにするかextconf.rbベースにするか悩みつつ、結局 extconf.rb 崩れのようなコードになったのが今日。
コード公開するなら github だべ、ということで初めて公開リポジトリ作ってみました。
できたよー!
つうわけで恥ずかしながらコード晒します。
https://github.com/sin00b/VerilatorGen.rb
コードってもRubyスクリプトがひとつだけです。中には SWIG のテンプレートがヒアドキュメントで入ってたりして、結構赤面モノですが、「美しいが動かないコードより、汚くても動くコード」ということで勘弁してください。
サンプル的な使い方は README に書きましたので、それ見てもらえればわかると思いますが、Rubyで書いたテストベンチの例と実行結果を挙げておきます。fooはクロックの立ち上がりで1ずつインクリメントし、goo1は初期値を1として2倍していくカウンタです。内部レジスタは両方barという名前です。そこそこ直感的に書けていると思いますがどうでしょうか。
require "./verilator"
top = Verilator::Vtest.new
(0..10).each do |i|
clk = i % 2
top.clk = clk
top.eval
puts "clk: #{clk} / top.foo.bar: #{top.foo.bar} / top.foo.goo1.bar #{top.foo.goo1.bar}"
end
$ ruby test.rb
clk: 0 / top.foo.bar: 0 / top.foo.goo1.bar 1
clk: 1 / top.foo.bar: 1 / top.foo.goo1.bar 2
clk: 0 / top.foo.bar: 1 / top.foo.goo1.bar 2
clk: 1 / top.foo.bar: 2 / top.foo.goo1.bar 4
clk: 0 / top.foo.bar: 2 / top.foo.goo1.bar 4
clk: 1 / top.foo.bar: 3 / top.foo.goo1.bar 8
clk: 0 / top.foo.bar: 3 / top.foo.goo1.bar 8
clk: 1 / top.foo.bar: 4 / top.foo.goo1.bar 16
clk: 0 / top.foo.bar: 4 / top.foo.goo1.bar 16
clk: 1 / top.foo.bar: 5 / top.foo.goo1.bar 32
clk: 0 / top.foo.bar: 5 / top.foo.goo1.bar 32
##言い訳
自分で作ってて言うのもなんですが、よくわかってないところが多々あります。特に、これはVerilatorの仕様になるんでしょうが、ブロッキング代入(=)とノンブロッキング代入(<=)をどう書き分けるのかとか、全然わかってません。Verilogモジュールを解釈してクラスに変換するのはまるっきりVerilator任せですし、そのクラスをRubyから使うとこも、これまた SWIG 任せです。
ちなみに32ビットのクロックポートが1つあり、内部レジスタでカウントする単純なカウンタのラッパーコードが1.5k行くらいになったりします。
##作ってみた感想など
先ほども書いたようにSWIG使ってラッパー書くのは初めてで、Verilatorの仕様もC++自体もよくわかってない中でラッパーコードの正解を探るのは結構な修行でした。
Verilator が吐くコードはコンピュータが生成してるわけですけど、それを(多少の手直しは必要とはいえ)解釈できるSWIGは賢い。
SWIGが生成できるのはRubyはもちろん、Python、Perl、PHP、Goなど多岐にわたります。XMLを吐くオプションもありますし、ソースコードを静的解析したい、みたいな向きにも使えるんじゃないでしょうか。
Pythonな人で興味のある方は、ハードコーディングされてる swig ソースを参照していただくといいと思います。