今まではRubyのテーブルと自作のデコーダ生成スクリプトを使ってVerilog-HDLのコードを生成してきたが、もうちょっと既存のフレームワークを探してみよう。
まずはChiselだ。ChiselはScalaで記述された言語情報からハードウェアを自動生成するためのフレームワークだ。
Chisel: Constructing Hardware in an Scala Embedded Language
そのまま翻訳してみた(クオリティ低い...)
About Chisel:
- ハードウェア生成言語 (Cからゲートに変換する訳ではない)
- スカラプログラミング言語に埋め込まれている
- 代数的(?)な形成と配線
- 抽象的なデータ方とインタフェース
- バルク接続(?)
- 階層的 + オブジェクト試行 + 関数的形成
- スカラによるメタプログラミングを利用することで高度なパラメタライズが可能
- ドメイン駆動言語をサポート
- 可変な標準ライブラリ (浮動小数点含む)
- 複数のクロックドメイン
- 低レベルなVerilog-HDLを生成できる
- githubに公開されたオープンソースコードとBSDライセンス
- 完全なドキュメント
- 成長しているコミュニティ
実行環境はUbuntu-14.04LTSだ。
git clone https://github.com/ucb-bar/chisel.git cd chisel make clean test publish-local
早速ダウンロードしてみたけど、sbtが無いと言われて実行することができない。 インストールしてみよう。
sudo aptitude install openjdk-7-jre wget https://dl.bintray.com/sbt/debian/sbt-0.13.6.deb sudo dpkg -i sbt-0.13.6.deb
改めてビルドしてみる。かなり時間がかかった。
make clean test publish-local
ついでに、チュートリアルのダウンロード:
git clone https://github.com/ucb-bar/chisel-tutorial.git cd chisel-tutiroial/hello make
これだけで何か出来た。とりあえず生成物を確認する。
$ ls -1 build.sbt chisel-dependent.sbt emulator_api.h emulator.h emulator_mod.h Hello Hello.cpp Hello-emulator.cpp Hello-emulator.o Hello.h Hello-harness.v Hello.o Hello.out Hello.scala Hello.v Hello.vcd Makefile project target
入力がHello.scala、.cppとか、.vとかが生成物だろう。とりあえず中を見てみよう。
- Hello.scala
package Hello import Chisel._ class Hello extends Module { val io = new Bundle { val out = UInt(OUTPUT, 8) } io.out := UInt(42) } class HelloTests(c: Hello) extends Tester(c) { step(1) expect(c.io.out, 42) } object Hello { def main(args: Array[String]): Unit = { val tutArgs = args.slice(1, args.length) chiselMainTest(tutArgs, () => Module(new Hello())) { c => new HelloTests(c) } } }
Moduleの継承?でHelloクラスが作られているあたり、Helloモジュールを生成している。 変数ioの中で出力ポート(out, 8ビット)を作成しており、それに定数Uint(42)をそのまま代入しているのかな?
HelloTestsは、Helloモジュールのテストをするためのモジュールかな。 c.io.out が 42であることを期待している。
- Hello.v
module Hello( output[7:0] io_out ); assign io_out = 8'h2a; endmodule
間違い無いけど、、、これだけではあまり作った感じがしないなあ。
- Hello.cpp
#include "Hello.h" void Hello_t::init ( val_t rand_init ) { this->__srand(rand_init); } int Hello_t::clock ( dat_t<1> reset ) { uint32_t min = ((uint32_t)1<<31)-1; if (clk_cnt < min) min = clk_cnt; clk_cnt-=min; if (clk_cnt == 0) clock_hi( reset ); if (clk_cnt == 0) clock_lo( reset ); if (clk_cnt == 0) clk_cnt = clk; return min; } mod_t* Hello_t::clone() { mod_t* cloned = new Hello_t(*this); return cloned; } bool Hello_t::set_circuit_from ( mod_t* src ) { Hello_t* mod_typed = dynamic_cast<Hello_t*>(src); assert(mod_typed); Hello__io_out = mod_typed->Hello__io_out; clk = mod_typed->clk; clk_cnt = mod_typed->clk_cnt; return true; } void Hello_t::print ( FILE* f ) { } void Hello_t::print ( std::ostream& s ) { } void Hello_t::dump_init ( FILE* f ) { fputs("$timescale 1ps $end\n", f); fputs("$scope module Hello $end\n", f); fputs("$var wire 8 \x21 io_out $end\n", f); fputs("$upscope $end\n", f); fputs("$enddefinitions $end\n", f); fputs("$dumpvars\n", f); fputs("$end\n", f); fputs("#0\n", f); dat_dump<1>(f, Hello__io_out, 0x21); Hello__io_out__prev = Hello__io_out; } void Hello_t::dump ( FILE* f, int t ) { if (t == 0) return dump_init(f); fprintf(f, "#%d\n", t); if (Hello__io_out != Hello__io_out__prev) goto L0; K0: return; L0: Hello__io_out__prev = Hello__io_out; dat_dump<1>(f, Hello__io_out, 0x21); goto K0; } void Hello_t::clock_lo ( dat_t<1> reset ) { { Hello__io_out.values[0] = 0x2aL;} } void Hello_t::clock_hi ( dat_t<1> reset ) { } void Hello_api_t::init_mapping_table ( ) { dat_table.clear(); mem_table.clear(); Hello_t* mod_typed = dynamic_cast<Hello_t*>(module); assert(mod_typed); dat_table["Hello.io_out"] = new dat_api<8>(&mod_typed->Hello__io_out, "Hello.io_out", ""); }
なが!これだけのScalaコードでこんなに長いコードが生成されるの?
次回は、もうちょっと実用的なものを試してみたい。