Chiselを使ってオリジナルデザインを作成してみたい。とりあえず、ディープラーニングをターゲットとして、行列計算のためのモジュールをいろいろ作ってみたい。
まずは、Chisel単体で開発環境とテスト環境を構築するためにはどうしたらよいのだろうか。 いくつか環境を調査した。
Chisel-template を使った環境構築
Chiselを使ってVerilogファイルを生成するためには、
- Chiselを記述する
- テストを行う
- Chisel → (FIRRTL) → FIR → Verilog を生成する
の手順を踏む。このためには、 github で構築されている chisel-template リポジトリを使うのが便利だ。
Chisel-template を使う場合の注意
まず、Rocket-Chipの環境と共存するのは難しいと考えたほうがよさそうだ。
Rocket-Chipの環境もChiselを使うが、そのときのScala環境などの情報は~/.ivy2
というディレクトリに格納されているようだ。
しかし、Chisel-templateとRocket-Chipでは、この~/.ivy2
で使用する環境のバージョンが異なるようで、仕方がないので別のVirtualBox環境を構築してしまった。
2つもLinux環境を構築できない場合は、別のアカウントを作ってもよいかもしれない。
FixMadd デザインの開発
ここでは、8bitの入力値を2つ受け取り、その乗算結果を内部に格納された16bitのレジスタと加算する積和演算回路を作ってみよう。
z = z + x *y
の回路を作成する。
というわけで作ってみた。Chisel-templateの環境に乗せる形で作成した。GCDのサンプルプログラムがあるので、それをまねする形で作っていく。
├── src │ ├── main │ │ └── scala │ │ └── fixmadd │ │ └── FixMadd.scala │ └── test │ └── scala │ └── fixmadd │ ├── FixMaddMain.scala │ └── FixMaddUnit.scala
メインのプログラムは以下となる。
/** * Compute 8-bit MADD with 16-bit output */ class FixMadd extends Module { val io = IO(new Bundle { val in0 = Input(UInt(8.W)) val in1 = Input(UInt(8.W)) val clear = Input(Bool()) val in_valid = Input(Bool()) val out0 = Output(UInt(16.W)) val out_valid = Output(Bool()) }) val r_acc = Reg(UInt(16.W)) val r_mul_result = Reg(UInt(16.W)) val r_in_valid = Reg(Bool()) val r_out_valid = Reg(Bool()) r_in_valid := io.in_valid r_out_valid := r_in_valid when (io.clear) { r_acc := 0.U r_mul_result := 0.U r_in_valid := 0.U r_out_valid := 0.U } when (io.in_valid) { r_mul_result := io.in0 * io.in1 printf("r_mul_result = %d\n", r_mul_result) } when (r_in_valid) { r_acc := r_acc + r_mul_result } io.out0 := r_acc io.out_valid := r_out_valid }
シミュレーションとテスト
シミュレーションは以下のようなプログラムを書いて、テストパタンを作成した。
class FixMaddUnitTester(c: FixMadd) extends PeekPokeTester(c) { // Madd計算の検証用のテストパタン def computeFixMadd(in0: Int, in1: Int, out: Int): (Int) = { var resultInt: Int = out + in0 * in1 val max_val: Int = Math.pow(2, 16).asInstanceOf[Int] if (resultInt >= max_val) { resultInt = resultInt - max_val } resultInt } private val fixmadd = c var expected_fixmadd: Int = 0 // 内部信号のリセット poke(fixmadd.io.clear, 1) step(1) poke(fixmadd.io.clear, 0) // i, jの2値を振ってテストパタンを入力する for(i <- 1 to 40 by 3) { for (j <- 1 to 100 by 7) { // in0, in1 を設定する poke(fixmadd.io.in0, i) poke(fixmadd.io.in1, j) // in_valid を設定して 0→1→0 として入力を有効化する poke(fixmadd.io.in_valid, 1) step(1) poke(fixmadd.io.in_valid, 0) // Chisel で記述された FixMadd を実行する expected_fixmadd = computeFixMadd(i, j, expected_fixmadd) step(1) // テスト結果を比較する expect(fixmadd.io.out0, expected_fixmadd) expect(fixmadd.io.out_valid, 1) } } }
テストパタンを実行する
以下のように入力する。
sbt 'testOnly fixmadd.FixMaddTester -- -z Basic'
テストに成功すると、下記のメッセージが出力されて、テストが成功する。
... [info] [0.444] RAN 421 CYCLES PASSED [info] FixMaddTester: [info] FixMadd [info] FixMadd [info] Basic test using Driver.execute [info] - should be used as an alternative way to run specification [info] using --backend-name verilator [info] running with --is-verbose [info] running with --fint-write-vcd [info] using --help [info] ScalaTest [info] Run completed in 3 seconds, 77 milliseconds. [info] Total number of tests run: 1 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [info] Passed: Total 1, Failed 0, Errors 0, Passed 1 [success] Total time: 29 s, completed Jan 8, 2018 11:37:15 PM
Verilogを生成する
以下のように入力すると Verilog コードが生成される。
sbt test
確認してみよう。
$ find . -name *.v
./test_run_dir/fixmadd.FixMaddTester847075120/FixMadd.v
./test_run_dir/fixmadd.FixMaddTester2058266144/FixMadd.v
- test_run_dir/fixmadd.FixMaddTester847075120/FixMadd.v
module FixMadd( input clock, input reset, input [7:0] io_in0, input [7:0] io_in1, input io_clear, input io_in_valid, output [15:0] io_out0, output io_out_valid ); reg [15:0] r_acc; // @[FixMadd.scala 20:25] reg [31:0] _RAND_0; reg [15:0] r_mul_result; // @[FixMadd.scala 21:25] reg [31:0] _RAND_1; reg r_in_valid; // @[FixMadd.scala 22:25] reg [31:0] _RAND_2; ...
Vivadoで論理合成試行
Vivadoで論理合成を試行してみた。特に問題なく合成できるようだ。
- タイミング
------------------------------------------------------------------------------------------------ | Design Timing Summary | --------------------- ------------------------------------------------------------------------------------------------ WNS(ns) TNS(ns) TNS Failing Endpoints TNS Total Endpoints WHS(ns) THS(ns) THS Failing Endpoints THS Total Endpoints WPWS(ns) ------- ------- --------------------- ------------------- ------- ------- --------------------- ------------------- -------- 2.284 0.000 0 49 0.152 0.000 0 49 2.000
- 面積
+-------------------------+------+-------+-----------+-------+ | Site Type | Used | Fixed | Available | Util% | +-------------------------+------+-------+-----------+-------+ | Slice LUTs* | 89 | 0 | 53200 | 0.17 | | LUT as Logic | 89 | 0 | 53200 | 0.17 | | LUT as Memory | 0 | 0 | 17400 | 0.00 | | Slice Registers | 34 | 0 | 106400 | 0.03 | | Register as Flip Flop | 34 | 0 | 106400 | 0.03 | | Register as Latch | 0 | 0 | 106400 | 0.00 | | F7 Muxes | 0 | 0 | 26600 | 0.00 | | F8 Muxes | 0 | 0 | 13300 | 0.00 | +-------------------------+------+-------+-----------+-------+
関連記事
"FPGA開発日記"でのChiselを取り扱った記事。