Chiselコンパイルのあれこれと回路の動作テスト（Chisel勉強会３）

## はじめに

筆者は失恋でめんたるぼろぼろなので、間違いなどがありましたら優しく教えてくださいまし
すからは嫌になってきた

## Chiselのビルド方法

Chiselをビルドするにはpart1で環境を整えたsbt（Simple Build Tool）
コマンドというものを使用していきます

Chiselまわりのライブラリをインターネットから引っ張ってきてビルドを自動的にやってくれるもの

ビルドに失敗したときは、インターネットの接続状態とハードウェアのデザインを見直す必要がある

ChiselやScalaのバージョンをいじりたいときはbuild.sbt(Chiselプロジェクトの一番親ディレクトリに書き込む設定ファイル)をカスタマイズすることで可能

全然シンプルじゃないけどこれの使い方をいかに記していきたいと思います

## sbtのあれこれ

完全に余談ではあるが、sbtコマンドはMavenというビルドツールを派生させたものらしく、Java -> Maven -> sbt と作られていったのだそう

より深く知りたかったらMavenってやつを知る必要あり？（自分はめんどくさがりなのでパス）

プロジェクトの構造は以下のようになっているのがベース

```
.
├── build.sbt
├── project
│   ├── build.properties
│   ├── plugins.sbt
│   ├── project
│   │   └── target
│   └── target
├── src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   ├── scala
│   └── test
│       ├── java
│       ├── resources
│       ├── scala
└── target
```

## Chiselを用いたライブラリの作成

自作のクラスの作成の記述方法は以下のようになります

```scala:OriginalClass.scala
// ライブラリ名の定義
package mypack

// chiselのクラス一覧を全てインポート
import chisel3._

// オリジナルのクラスの定義
class ABC extends Modules{
    val io = IO(new Bundle{})
}
```

```scala:ImportClass.scala
import mypack._

// ABCクラス単体でのインポート方法
// import mypack.ABC

class ABCUser extends Module{
    val io = IO(new Bundle{})
    
    // オリジナルクラスの呼び出し
    val abc = Module(new ABC())
}
```

うん、Javaと変わんない

## Chiselでのテストベンチ記述方法

言うまでもないですが、ハードウェアのファイルに入力を与えて結果と予想を照らし合わせるためのファイルをテストベンチといいます

ChiselではテストベンチをPeekPokeTesterというもので値をぶっこんでいきます（なんか名前かわいい）

実際にテストベンチを以下のように作っていきます

書き方１つめ

```scala:Target_tb1.scala
import chisel3._
import chisel3.iotesters._

class DeviceUnderTest extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt (2.W))
        val b = Input(UInt (2.W))
        val out = Output(UInt (2.W))
    })
    
    io.out := io.a & io.b
}

object Test extends App {
    iotesters.Driver.execute(args, () => new DeviceUnderTest){
        c => new PeekPokeTester(c) {
            poke(dut.io.a, 0.U)
            poke(dut.io.b, 1.U)
            step (1)
            println("Result is: " + peek(dut.io.out).toString)

            poke(dut.io.a, 3.U)
            poke(dut.io.b, 2.U)
            step (1)
            println("Result is: " + peek(dut.io.out).toString)
        }
    }
}
```

書き方２つめ

```scala:Target_tb2.scala
object Test extends App {
    iotesters.Driver.execute(args, () => new DeviceUnderTest){
        c => new PeekPokeTester(c) {
            poke(dut.io.a, 3.U)
            poke(dut.io.b, 1.U)
            step (1)
            expect(dut.io.out, 1)

            poke(dut.io.a, 2.U)
            poke(dut.io.b, 0.U)
            step (1)
            expect(dut.io.out, 0)
        }
    }
}
```

書き方１では結果が全て出力されるが、２では先に答えを入れておいて正しい答えが出力されたときにSuccessと表示するものになっている

ちなみにDUTはdesign under testって意味だお

いざ実行・・・の前にbuild.sbtに以下を付け足さなきゃいけないみたい

```sbt:build.sbt
// バージョンを指定してchiselのiotestersをインストール
// 他のライブラリを使用するためには初回のダウンロードする必要があり？（よーわかってない）

libraryDependencies += "edu.berkeley.cs" %% "chisel-iotesters" % "1.3.1"
```

実行

```
// １つめのテスト

$ sbt run

[info] running Test 
[info] [0.001] Elaborating design...
[info] [0.517] Done elaborating.
Total FIRRTL Compile Time: 205.3 ms
file loaded in 0.032897348 seconds, 7 symbols, 3 statements
[info] [0.000] SEED 1575360902041
[info] [0.002] Result is: 0
[info] [0.002] Result is: 2
test DeviceUnderTest Success: 0 tests passed in 7 cycles in 0.008071 seconds 867.28 Hz
[info] [0.003] RAN 2 CYCLES PASSED

// ２つめのテスト

$ sbt run

[info] running Test 
[info] [0.001] Elaborating design...
[info] [0.520] Done elaborating.
Total FIRRTL Compile Time: 204.3 ms
file loaded in 0.032164604 seconds, 7 symbols, 3 statements
[info] [0.000] SEED 1575361065328
test DeviceUnderTest Success: 2 tests passed in 7 cycles in 0.007838 seconds 893.12 Hz
[info] [0.003] RAN 2 CYCLES PASSED
```

できたあああああああああああああああああああうおおおおおおおおおおおおおおおおおおおおおおおお

回路の評価の際は１つめでゆる〜いチェックして、２つめで回路が完璧に動くかを判断するといいかも？

## Packageとテストを組み合わせてみる

packageでライブラリ生成して実際にimportしたものを評価してみましょう！

```scala:Package.scala
package pack

import chisel3._
import chisel3.iotesters._

class DeviceUnderTest extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt (2.W))
        val b = Input(UInt (2.W))
        val out = Output(UInt (2.W))
    })

    io.out := io.a & io.b
}
```

```scala:Main.scala
import pack._

import chisel3._
import chisel3.Driver
import chisel3.iotesters._

class Main extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt(2.W))
        val b = Input(UInt(2.W))
        val out = Output(UInt(2.W))
    })
    
    // 実際に回路を使用する場合はModuleとして呼び出す必要がある
    val d = Module(new DeviceUnderTest())

    d.io.a := io.a
    d.io.b := io.b
    io.out := d.io.out
}

object Test extends App {
    iotesters.Driver.execute(args, () => new Main){
        c => new PeekPokeTester(c) {
            poke(dut.io.a, 3.U)
            poke(dut.io.b, 1.U)
            step (1)
            println("Result is: " + peek(dut.io.out).toString)

            poke(dut.io.a, 2.U)
            poke(dut.io.b, 0.U)
            step (1)
            println("Result is: " + peek(dut.io.out).toString)
        }
    }
}
```

## まとめ

もう主要な機能はわかってきたから、とりあえず今回で締めかな？
あとはアーキテクチャやらアルゴリズムの理解の問題だと思われるので！
また重要なことがあればまとめます！
でも実際にChiselでRISCの設計をしてないから、とりあえずMipsでも作ってみよーかな

## 参考文献

https://raw.githubusercontent.com/wiki/schoeberl/chisel-book/chisel-book.pdf