吃透Chisel语言.12.Chisel项目构建、运行和测试(四)——Chisel测试之ChiselTest

Chisel项目构建、运行和测试(四)——Chisel测试之ChiselTest

上一篇文章我们介绍了ScalaTest,它是Scala和Java的测试工具。而现在Chisel模块最新的标准测试工具是ChiselTest,它是基于ScalaTest的,允许我们用于Chisel测试。为了使用ChiselTest,我们同样需要在build.sbt里面包含chiseltest的库:

libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "0.5.1" % "test"

或者这样:

libraryDependencies ++= Seq(
      "edu.berkeley.cs" %% "chisel3" % chiselVersion,
      "edu.berkeley.cs" %% "chiseltest" % "0.5.1" % "test"
    ),

Chisel-template的build.sbt里面是默认包含了chiseltest库的,所以我们可以不修改。用这种方法包含ChiselTest会自动包含对应版本的ScalaTest,所以上一篇文章中说的包含ScalaTest也是不需要的。

要使用ChiselTest的话,我们需要导入下面的这些包:

import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec

对电路进行测试需要包含至少两部分,一个是DUT(待测件),另一个是测试逻辑,也叫testbench。启动测试和ScalaTest是一样的,运行sbt test命令就行了,不需要有个包含主函数的对象作为程序入口。

下面的代码是一个简单的DUT,它有两个输入端口和一个输出端口,都是二位无符号数信号,实现的功能是对输入ab执行按位与操作,然后把结果输出到out上:

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
}

虽然还没介绍模块的写法,但是这个代码也是很好懂的。现在我们给出这个DUT的testbench,它是从AnyFlatSpecChiselScalatestTester拓展来的,因此是具有ChiselTest功能的ScalaTest。而调用test()方法时,以新创建的DUT的一个实例为参数,以测试代码为函数字面量(function literal)。代码如下:

class SimpleTest extends AnyFlatSpec with ChiselScalatestTester {
    "DUT" should "pass" in {
        test(new DeviceUnderTest) { dut =>
            dut.io.a.poke(0.U)
            dut.io.b.poke(1.U)
            dut.clock.step()
            println("Result is: " + dut.io.out.peek().toString)
            dut.io.a.poke(3.U)
            dut.io.b.poke(2.U)
            dut.clock.step()
            println("Result is: " + dut.io.out.peek().toString)
        }
    }
}

DUT的实例的输入输出端口通过dut.io来访问。我们可以通过在端口上调用poke来给端口赋值,它接受的是端口对应的Chisel类型的值。而在输出端口上调用peek可以把端口的输出给读取出来,返回值也是该端口对应的Chisel类型的值,再在该值上调用toString可以将其转换为字符串,进而可以调用println()函数在命令行输出。测试中调用dut.clock.step()可以让时钟前进一个周期,以此让仿真前进。要是想要前进多个周期,我们可以给step()提供一个参数。

运行sbt test或者:

sbt "testOnly SimpleTest"

就可以在终端看到测试的结果:

在这里插入图片描述

结果表明,01按位与的结果为032按位与的结果为2。当然信息不止有结果,我们还可以看到toString方法还输出了结果的Chisel类型UInt<2>,即二位无符号整数。

那我们人工核对输出结果肯定是不好的,但确实已经成功一半了。要想自动完成和预期结果的比较,我们可以通过在输出端口上调用expect(value)来在testbench中给出预期值,预期值就是expect(value)的参数value。修改之后的测试如下:

class SimpleTestExpect extends AnyFlatSpec with ChiselScalatestTester {
    "DUT" should "pass" in {
        test(new DeviceUnderTest) { dut =>
            dut.io.a.poke(0.U)
            dut.io.b.poke(1.U)
            dut.clock.step()
            dut.io.out.expect(0.U)
            dut.io.a.poke(3.U)
            dut.io.b.poke(2.U)
            dut.clock.step()
            dut.io.out.expect(2.U)
        }
    }
}

现在运行这个测试:

sbt "testOnly SimpleTestExpect"

这个测试不会输出任何来自硬件的结果,它自动在测试内完成了结果与预期值的比较,输出如下:

在这里插入图片描述

如果测试失败的话,比如DUT或者testbench里面有错误,导致实际结果和预期值不符,那就回生成一条错误信息来描述两者的差异。比如我们把testbench中最后一行的dut.io.out.expect(2.U)改成dut.io.out.expect(1.U),输出结果就是这样的:

在这里插入图片描述

一片红色的报错,同时也给出了测试未通过的理由和具体差异在哪里,在调试的时候这些信息非常有用,所以说测试很重要。

结语

这一篇文章讲了如何用ChiselTest做简单的测试,可以看到很基础,同时也很方便。然而,更精髓的内容还没有体现出来,目前写的测试还没有发挥Scala的强大能力。比如说,上面的测试里面我们都是手动给出测试的输入和输出预期值的,而Scala可以用于写一个参考模型,也就是在另一个系列中说的黄金模型,这样测试就可以更自动化、更全面了。不过我们暂时还不会展开讲,得先把几个测试方法都讲完先,下一篇我们就讲讲用波形进行测试。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值