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

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

Chisel使った開発環境でテスト実行時にプログラム引数を指定する方法

スポンサーリンク

前回の記事でChiselFlatSpecを使ったBDDスタイルの開発例、、、、みたいな記事を書いてみた。

www.tech-diningyo.info

その際にデバッグの時に引数とか追加できんのかいねー??とか思って、方法を探してみたのでそれを書いてみようと思う。

Chiselのテスト実行時にプログラム引数を指定する方法

きっかけは単純で、最初は以下のような感じでChiselFlatSpec内のテストケースのexecuteの第一引数に直接Arrayで書いておいた固定の引数を使っていた(以下はChisel-templateのもの)

class GCDTester extends ChiselFlatSpec {
  ~いろいろ省略~
  "using --backend-name verilator" should "be an alternative way to run using verilator" in {
    if(backendNames.contains("verilator")) {
      iotesters.Driver.execute(Array("--backend-name", "verilator"), () => new GCD) {
        c => new GCDUnitTester(c)
      } should be(true)
    }
  }
  ~いろいろ省略~
}

で、いろいろfirrtlやらtreadleやらverilatorを切り替えて何がどう変わるんだろー??とかこのテストだけFAILしたからピンポイントで波形とって実行したいなーとか、出てくるわけで。 とりあえず苦肉の策で、

  val args = Array(
    "--generate-vcd-output", "on",
    //"--backend-name", "verilator",
    "--target-dir", "test_run_dir/fifo_test",
    "--top-name", "fifo_dtm"
    //"--is-verbose"
  )

こんな感じで書いといて、実行時にソースを編集して切り替えたりしていた。 流石に面倒なので、ちょっと真面目に調べてみた結果をご紹介

BeforeAndAfterAllConfigMapを使ってConfigMapを操作する方法

やり方としては、ほぼそのままで見たほうが早いと思うので早速コードを。 因みにこの方法は検索してきて出てきたstack overflowのトピックを参考にしました。

stackoverflow.com

class MyTester extends ChiselFlatSpec with BeforeAndAfterAllConfigMap {

  val defaultArgs = scala.collection.mutable.Map(
    "--generate-vcd-output" -> "off",
    "--backend-name" -> "verilator",
    "--is-verbose" -> false
  )

  override def beforeAll(configMap: ConfigMap): Unit = {
    if (configMap.get("--backend-name").isDefined) {
      defaultArgs("--backend-name") = configMap.get("--backend-name").fold("")(_.toString)
    }
    if (configMap.get("--generate-vcd-output").isDefined) {
      defaultArgs("--generate-vcd-output") = configMap.get("--generate-vcd-output").fold("")(_.toString)
    }
    if (configMap.get("--is-verbose").isDefined) {
      defaultArgs("--is-verbose") = true
    }
  }

  def getArgs(optArgs: Map[String, Any]): Array[String] = {
    val argsMap = defaultArgs ++ optArgs
    argsMap.map {
      case (key: String, value: String) => s"$key=$value"
      case (key: String, value: Boolean) => if (value) key else ""
    }.toArray
  }

ポイントとしては以下の3点。

  1. trait BeforeAndAfterAllConfigMapをミックスインする
  2. BeforeAndAfterAllConfigMapのメソッドbeforeAllをオーバーライドし引数のConfigMapからプログラム引数を取り出し、定義しておいたdefaultArgsにセットする
  3. Driver.executeに渡す用の引数を構築するメソッドgetArgsを操作する方法

毎回作んのも面倒なので、上記の要素を含むクラスを作っといて、それを使うようにしたほうが楽なのかも。

テスト実行時のプログラム引数の指定方法

実装は上記のものでOKで実行時にはどうするか、、という話だがScalaTestのマニュアルによると-Dを使うと設定した値がConfigMapに設定されると書いてあるので、それを使えばOK。 上記の例だと、以下のようになる。

sbt "testOnly MyTester -- -D--backend-name=firrtl -D--generate-vcd-output=on -D--is-verbose=true"

上記の例でプログラム引数を指定した場合としなかった場合での実行結果は以下のようになった。 なお、以下の結果はテストケース内でgetArgsで取得したArrayのデータを展開したもの。

  • 実行(デフォルト引数をそのまま使用)

デフォルト引数(defaultArgs)の中身が変更されないため--is-verboseが含まれてない。

--backend-name=treadle

--generate-vcd-output=off
  • 実行(testOnlyの引数を指定)

こちらはsbtコマンドtestOnly実行時に引数を指定した場合。 各々の引数が変更されているのがわかるかと思う。

--backend-name=firrtl <-- firrtlに
--is-verbose           <-- --is-verboseが追加
--generate-vcd-output=on <-- 波形取得がonになった

とりあえずこれでFAILしたテストだけ、波形とって流し直しとかも出来るようになるので効率が上げられる!!はず。。。 これを調べてる際にDriver.executeの他の引数についても気になったものがあるので、いずれ紹介出来ると良いなぁ。