ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chisel Bootcamp - Module3.2(2) - N-tap版FIRフィルタモデルを使ったChiselのテスト回路実装

スポンサーリンク

前回の記事ではChisel BootcampではModule3.2に入り、Scalaのコレクション型を使ったFIRフィルタのリファレンスデザインを実装した。

www.tech-diningyo.info

今回も引き続きModule3.2を見て行くが、今日はScalaで実装したFIRフィルタのテストを行い、リファレンスモデルとしての確かさを確認した後に、このモデルをChiselのテスト回路に組み込んでいく。

Module 3.2: ジェネレータ:コレクション

リファレンスモデルのテストの修正

以前の記事でFIRフィルタを扱った際↓には、以下に記載したようなコードを使ってFIRフィルタのテストを行った。

www.tech-diningyo.info

// Simple 4-point moving average
Driver(() => new My4ElementFir(1, 1, 1, 1)) {
  c => new PeekPokeTester(c) {
    poke(c.io.in, 1)
    expect(c.io.out, 1)  // 1, 0, 0, 0
    step(1)
    poke(c.io.in, 4)
    expect(c.io.out, 5)  // 4, 1, 0, 0
    step(1)
    poke(c.io.in, 3)
    expect(c.io.out, 8)  // 3, 4, 1, 0
    step(1)
    poke(c.io.in, 2)
    expect(c.io.out, 10)  // 2, 3, 4, 1
    step(1)
    poke(c.io.in, 7)
    expect(c.io.out, 16)  // 7, 2, 3, 4
    step(1)
    poke(c.io.in, 0)
    expect(c.io.out, 12)  // 0, 7, 2, 3
  }
}

上記のテストを先日のFIRフィルタのリファレンスデザイン向けに変更したものが以下になる。

val filter = new ScalaFirFilter(Seq(1, 1, 1, 1))

var out = 0

out = filter.poke(1)
println(s"out = $out")
assert(out == 1)  // 1, 0, 0, 0

out = filter.poke(4)
assert(out == 5)  // 4, 1, 0, 0
println(s"out = $out")

out = filter.poke(3)
assert(out == 8)  // 3, 4, 1, 0
println(s"out = $out")

out = filter.poke(2)
assert(out == 10)  // 2, 3, 4, 1
println(s"out = $out")

out = filter.poke(7)
assert(out == 16)  // 7, 2, 3, 4
println(s"out = $out")

out = filter.poke(0)
assert(out == 12)  // 0, 7, 2, 3
println(s"out = $out")

Chiselのモジュールをテストする訳ではないのでDriverPeekPokeTesterといったChiselの機能を使う必要はなくFIRフィルタクラスに実装したアウトプットを生成する関数pokeに所定のデータを入力し、その戻り値が期待値と一致するかどうかをassertを使って確認している。

これを実行した結果は以下の様にassertが発火することなく、正常に終了する。

out = 1
out = 5
out = 8
out = 10
out = 16
out = 12

リファレンスモデルを使ったテスト回路

上記でリファレンスモデルのFIRフィルタのデザインは妥当なものであるということが確認できたので、このモデルを期待値生成モデルとして、Chiselのテスト回路に組み込んでみよう。

val goldenModel = new ScalaFirFilter(Seq(1, 1, 1, 1))

Driver(() => new My4ElementFir(1, 1, 1, 1)) {
  c => new PeekPokeTester(c) {
    for(i <- 0 until 100) {
      val input = scala.util.Random.nextInt(8)

      val goldenModelResult = goldenModel.poke(input)

      poke(c.io.in, input)

      expect(c.io.out, goldenModelResult, s"i $i, input $input, gm $goldenModelResult, ${peek(c.io.out)}")

      step(1)
    }
  }
}

やっていることはとてもシンプルで、

  1. 作成したリファレンスデザインをインスタンス
  2. ChiselのDriverPeekPokeTesterを使ってChiselのFIRフィルタデザインをテスト

と2つだけだ。

テストのコードは最初の入力値/期待値ベタ書きのものに比べるとコンパクトになり、テスト実施回数はループ回数である100回となり、良いことだらけ!

ここでの注意点として以下の2点が紹介されている(&執筆時にハマった点らしい)

  1. step関数の呼び出し位置に注意すること。ScalaのリファレンスモデルとChiselのハードウェアのデザインは別々に実行されるので、間違いやすい
  2. このテストはIOとレジスタのサイズに敏感なためテストとしては弱い。実装したいハードウェアのリファレンスモデルをソフトで構築する際には任意のデータを扱えるようにしておくと複雑化することが出来る。しかし、ここではハードウェアにフィットする値しか渡せないので確認が必要となる。

今日はここで一区切り。次回はChiselの4tap-FIRフィルタジェネレータをN-tap版に変更していく。