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

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

Chiselのテストでshould be (true)付けてなくてプチハマりした

スポンサーリンク

今作り直してるオレオレRISC-V(dirv)の対向に接続するメモリモデルがバグってるので、バグ再現パターンを起こしてシミュレーションを行っていた。その際に出くわした「いや、当たり前でしょ」な話をメモ書き程度に残しておく。ScalaTest使ってる人は多分タイトルでわかる方もいる気が。。

メモリモデルがバグってて。。

とりあえず簡単に状況説明。現在以下の構成のオレオレRISC-Vにおいてdirvブロックの対向に接続しているメモリモデルを書き直している。

f:id:diningyo-kpuku-jougeki:20190519221354p:plain

メモリ用の簡単なテストモジュールを作成して実装を進めてなんとなーく形になったなぁ、と思ってdirvに接続してみたところメモリアクセス系の命令が軒並みFAILした。 解析を進めて理由が分かったので、メモリモデル用のテストクラスにパターンを追加するか、、と思ってテストを作成している際にこの記事のタイトルにあるshould be (true)でハマったのでその内容をメモ。

バグ再現パターン

以下のようなテストを書いていて、コメント部分を読んでもらうとわかるのだがstep3の部分でバグのトリガーとなる動作が実行されてstep4で期待値比較がFAILすることを期待した内容になっている。

  it should
    f"keep Mbus.r.valid high if Mbus r.ready is Low. [$dutName-BUG-100]" in {

    val outDir = dutName + "-BUG-100"
    val args = getArgs(Map(
      "--top-name" -> dutName,
      "--target-dir" -> s"test_run_dir/$outDir"
    ))

    Driver.execute(args, () => new SimDTMMemTop(base_p)(timeoutCycle)) {
      c => new MemTopUnitTester(c) {
        idle(10)

        val wrData = Seq(
          0xf0008093, 0x00008f03,
          0xfff00e93, 0x00200193)

        // Set value
        for ((data, idx) <- wrData.zipWithIndex) {
          single_write(idx * 4, 0xf, data)
        }

        // step 1
        var data = wrData(0)
        set_imem(true, 0x4, MemCmd.rd, true)
        comp_imem(true, true, wrData(0), MemResp.ok)
        step(1)

        // step 2
        set_imem(true, 0x8,MemCmd.rd,  true)
        comp_imem(true, true, wrData(1), MemResp.ok)
        step(1)

        // step 3
        // change ready signal to LOW, so mbus read data will be kept in next cycle.
        set_imem(true, 0xc, MemCmd.rd, false)
        comp_imem(true, true, wrData(2), MemResp.ok)
        step(1)

        // step 4
        // This bug regeneration pattern expose that
        // mbus read data doesn't keep the value in previous cycle.
        set_imem(true, 0xc, MemCmd.rd, true)
        comp_imem(true, true, wrData(2), MemResp.ok)
        step(1)

        // step 5
        set_imem(true, 0x10, MemCmd.rd, true)
        comp_imem(true, true, wrData(3), MemResp.ok)
        step(1)

        idle(10)
      }
    }
  }

このパターンを実行してみると、以下のようにテストがPASSする結果になる。

STARTING test_run_dir/MemTopTester-BUG-100/VSimDTMMemTop
[info] [0.001] SEED 1566016484339
[info] [0.022] (cmd_ready, w_ready) = (1, 1)
[info] [0.024] (cmd_ready, w_ready) = (1, 1)
[info] [0.026] (cmd_ready, w_ready) = (1, 1)
[info] [0.027] (cmd_ready, w_ready) = (1, 1)
Enabling waves..
Exit Code: 0
[info] [0.036] RAN 33 CYCLES FAILED FIRST AT CYCLE 18
[info] MemTopTester:
[info] MemTopTester
[info] - should keep Mbus.r.valid high if Mbus r.ready is Low. [MemTopTester-BUG-100]
[info] ScalaTest
[info] Run completed in 4 seconds, 129 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

おかしいなーと思って--is-verboseオプションを有効にして再実行。

[info] [0.028] STEP 18 -> 19
[info] [0.029]   POKE SimDTMMemTop.io_dut_imem_valid <- 1
[info] [0.029]   POKE SimDTMMemTop.io_dut_imem_addr <- 8
[info] [0.029]   POKE SimDTMMemTop.io_dut_imem_cmd <- 0
[info] [0.029]   POKE SimDTMMemTop.io_dut_imem_r_ready <- 1
[info] [0.029]   PEEK SimDTMMemTop.io_dut_imem_ready -> 1
[info] [0.029]   EXPECT SimDTMMemTop.io_dut_imem_ready -> 1 == 1 PASS
[info] [0.029]   PEEK SimDTMMemTop.io_dut_imem_r_valid -> 1
[info] [0.029]   EXPECT SimDTMMemTop.io_dut_imem_r_valid -> 1 == 1 PASS
[info] [0.029]   PEEK SimDTMMemTop.io_dut_imem_r_data -> 36611
[info] [0.029]   EXPECT SimDTMMemTop.io_dut_imem_r_data -> 36611 == 36611 PASS
[info] [0.030]   PEEK SimDTMMemTop.io_dut_imem_r_resp -> 0
[info] [0.030]   EXPECT SimDTMMemTop.io_dut_imem_r_resp -> 0 == 0 PASS
[info] [0.030] STEP 19 -> 20
[info] [0.030]   POKE SimDTMMemTop.io_dut_imem_valid <- 1
[info] [0.030]   POKE SimDTMMemTop.io_dut_imem_addr <- 12
[info] [0.030]   POKE SimDTMMemTop.io_dut_imem_cmd <- 0
[info] [0.030]   POKE SimDTMMemTop.io_dut_imem_r_ready <- 0
[info] [0.031]   PEEK SimDTMMemTop.io_dut_imem_ready -> 1
[info] [0.031]   EXPECT SimDTMMemTop.io_dut_imem_ready -> 1 == 1 PASS
[info] [0.031]   PEEK SimDTMMemTop.io_dut_imem_r_valid -> 1
[info] [0.031]   EXPECT SimDTMMemTop.io_dut_imem_r_valid -> 1 == 1 PASS
[info] [0.031]   PEEK SimDTMMemTop.io_dut_imem_r_data -> 4293922451
[info] [0.031]   EXPECT SimDTMMemTop.io_dut_imem_r_data ->
 4293922451 == -1044845 FAIL *****
[info] [0.031]   PEEK SimDTMMemTop.io_dut_imem_r_resp -> 0
[info] [0.031]   EXPECT SimDTMMemTop.io_dut_imem_r_resp -> 0 == 0 PASS
[info] [0.031] STEP 20 -> 21
[info] [0.032]   POKE SimDTMMemTop.io_dut_imem_valid <- 1
[info] [0.032]   POKE SimDTMMemTop.io_dut_imem_addr <- 12
[info] [0.032]   POKE SimDTMMemTop.io_dut_imem_cmd <- 0
[info] [0.032]   POKE SimDTMMemTop.io_dut_imem_r_ready <- 1
[info] [0.032]   PEEK SimDTMMemTop.io_dut_imem_ready -> 1
[info] [0.032]   EXPECT SimDTMMemTop.io_dut_imem_ready -> 1 == 1 PASS
[info] [0.032]   PEEK SimDTMMemTop.io_dut_imem_r_valid -> 1
[info] [0.032]   EXPECT SimDTMMemTop.io_dut_imem_r_valid -> 1 == 1 PASS
[info] [0.032]   PEEK SimDTMMemTop.io_dut_imem_r_data -> 2097555
[info] [0.032]   EXPECT SimDTMMemTop.io_dut_imem_r_data ->
 2097555 == -1044845 FAIL *****
[info] [0.033]   PEEK SimDTMMemTop.io_dut_imem_r_resp -> 0
[info] [0.033]   EXPECT SimDTMMemTop.io_dut_imem_r_resp -> 0 == 0 PASS
[info] [0.033] STEP 21 -> 22
[info] [0.033]   POKE SimDTMMemTop.io_dut_imem_valid <- 1
[info] [0.033]   POKE SimDTMMemTop.io_dut_imem_addr <- 16
[info] [0.034]   POKE SimDTMMemTop.io_dut_imem_cmd <- 0
[info] [0.034]   POKE SimDTMMemTop.io_dut_imem_r_ready <- 1
[info] [0.034]   PEEK SimDTMMemTop.io_dut_imem_ready -> 1
[info] [0.034]   EXPECT SimDTMMemTop.io_dut_imem_ready -> 1 == 1 PASS
[info] [0.034]   PEEK SimDTMMemTop.io_dut_imem_r_valid -> 1
[info] [0.034]   EXPECT SimDTMMemTop.io_dut_imem_r_valid -> 1 == 1 PASS
[info] [0.034]   PEEK SimDTMMemTop.io_dut_imem_r_data -> 2097555
[info] [0.034]   EXPECT SimDTMMemTop.io_dut_imem_r_data -> 2097555 == 2097555 PASS
[info] [0.034]   PEEK SimDTMMemTop.io_dut_imem_r_resp -> 0
[info] [0.035]   EXPECT SimDTMMemTop.io_dut_imem_r_resp -> 0 == 0 PASS

STEP19 -> 20 / STEP 20 -> 21がFAILしている。FAILしてるのにPASSってなんでだろう、、、ともってテストを確認してたところ、テスト結果判定の構文が抜けていた。

        set_imem(true, 0x10, MemCmd.rd, true)
        comp_imem(true, true, wrData(3), MemResp.ok)
        step(1)

        idle(10)
      }
    } // ここにテスト結果を確認する "should be (true)"が必要

should be (true) を付与して再度実行

修正して流した結果は以下の通り、ちゃんとFAILすることが確認できた。

[info] [0.028] RAN 33 CYCLES FAILED FIRST AT CYCLE 18
[info] MemTopTester:
[info] MemTopTester
[info] - should keep Mbus.r.valid high if Mbus r.ready is Low. [MemTopTester-BUG-100] *** FAILED ***
[info]   false was not true (MemTopTester.scala:308)
[info] ScalaTest
[info] Run completed in 3 seconds, 657 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***

ということで「当たり前だろー、そんなのー」という声が聞こえてきそうですがshould be (true)忘れてプチハマりした話でした。やっぱりもう少しScalaTest自体の仕組みを学ばければと思う一件だった。。。