Commit 05c528c1 by Benjamin Tu Committed by Thierry Moreau

[VTA] [Chisel] Added Chisel Module Unit Test Infrastructure (#3698)

* added wholething

* changed build and makefile
parent 3e4ed6d3
......@@ -44,6 +44,7 @@ vta_dir = $(abspath ../../)
tvm_dir = $(abspath ../../../)
verilator_build_dir = $(vta_dir)/$(BUILD_NAME)/verilator
chisel_build_dir = $(vta_dir)/$(BUILD_NAME)/chisel
test_name = mvm
verilator_opt = --cc
verilator_opt += +define+RANDOMIZE_GARBAGE_ASSIGN
......@@ -111,6 +112,9 @@ verilog_test: $(chisel_build_dir)/$(TOP_TEST).$(CONFIG).v
$(chisel_build_dir)/$(TOP_TEST).$(CONFIG).v:
sbt 'runMain vta.$(config_test) --target-dir $(chisel_build_dir) --top-name $(TOP_TEST).$(CONFIG)'
test:
sbt 'test:runMain unittest.Launcher $(test_name)'
clean:
-rm -rf target project/target project/project
......
<!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you under the Apache License, Version 2.0 (the -->
<!--- "License"); you may not use this file except in compliance -->
<!--- with the License. You may obtain a copy of the License at -->
<!--- http://www.apache.org/licenses/LICENSE-2.0 -->
<!--- Unless required by applicable law or agreed to in writing, -->
<!--- software distributed under the License is distributed on an -->
<!--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -->
<!--- KIND, either express or implied. See the License for the -->
<!--- specific language governing permissions and limitations -->
<!--- under the License. -->
VTA in Chisel
===================================================
For contributors who wants to test a chisel module:
- You can add your test files in `src/test/scala/unitttest`
- Add your test name and tests to the `test` object in `src/test/scala/unitttest/Launcher.scala`
- Check out the provided sample test `mvm` which tests the MatrixVectorComputation module
in `src/main/scala/core/TensorGemm.scala`
- Running unit tests: `make test test_name=your_own test_name`
......@@ -60,9 +60,13 @@ resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases"))
libraryDependencies ++= Seq(
"edu.berkeley.cs" %% "chisel3" % "3.1.7",
)
val defaultVersions = Map(
"chisel3" -> "3.1.7",
"chisel-iotesters" -> "[1.2.5,1.3-SNAPSHOT["
)
libraryDependencies ++= Seq("chisel3","chisel-iotesters").map {
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) }
scalacOptions ++= scalacOptionsVersion(scalaVersion.value)
javacOptions ++= javacOptionsVersion(scalaVersion.value)
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package unittest
// taken from https://github.com/freechipsproject/chisel-testers
import chisel3._
import chisel3.iotesters.{Driver, TesterOptionsManager}
import vta.core._
import vta.util.config._
import vta.shell._
class TestConfig extends Config(new CoreConfig ++ new PynqConfig)
/* Launcher.
*
* The Launcher object includes a test list for the TestRunner to check.
* Users can utilize this Launcher to run custom tests.
*
* How to Use:
* When the user input: sbt 'test:runMain unittest.Launcher mvm'
* the TestRunner will look for 'mvm' in the map and executes the
* test that 'mvm' is mapped to
*/
object Launcher {
implicit val p: Parameters = new TestConfig
val tests = Map(
"mvm" -> { (manager: TesterOptionsManager) =>
Driver.execute(() => new MatrixVectorMultiplication, manager) {
(c) => new TestMatrixVectorMultiplication(c)
}
}
)
def main(args: Array[String]): Unit = {
TestRunner(tests, args)
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package unittest
import chisel3._
import chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
import scala.util.Random
import scala.math.pow
import vta.core._
class TestMatrixVectorMultiplication(c: MatrixVectorMultiplication) extends PeekPokeTester(c) {
/* mvm_ref
*
* This is a software function that computes dot product with a programmable shift
* This is used as a reference for the hardware
*/
def mvm_ref(inp: Array[Int], wgt: Array[Array[Int]], shift: Int) : Array[Int] = {
val size = inp.length
val res = Array.fill(size) {0}
for (i <- 0 until size) {
var dot = 0
for (j <- 0 until size) {
dot += wgt(i)(j) * inp(j)
}
res(i) = dot * pow(2, shift).toInt
}
return res
}
val cycles = 5
for (i <- 0 until cycles) {
val r = new Random
// generate random data based on config bits
val in_a = Array.fill(c.size) { r.nextInt(pow(2, c.inpBits).toInt) - pow(2, c.inpBits-1).toInt}
val in_b = Array.fill(c.size, c.size) { r.nextInt(pow(2, c.wgtBits).toInt) - pow(2, c.wgtBits-1).toInt}
val res = mvm_ref(in_a, in_b, 0)
val inpMask = (pow(2, c.inpBits) - 1).toLong
val wgtMask = (pow(2, c.wgtBits) - 1).toLong
val accMask = (pow(2, c.accBits) - 1).toLong
for (i <- 0 until c.size) {
poke(c.io.inp.data.bits(0)(i), in_a(i) & inpMask)
poke(c.io.acc_i.data.bits(0)(i), 0)
for (j <- 0 until c.size) {
poke(c.io.wgt.data.bits(i)(j), in_b(i)(j) & wgtMask)
}
}
poke(c.io.reset, 0)
poke(c.io.inp.data.valid, 1)
poke(c.io.wgt.data.valid, 1)
poke(c.io.acc_i.data.valid, 1)
step(1)
poke(c.io.inp.data.valid, 0)
poke(c.io.wgt.data.valid, 0)
poke(c.io.acc_i.data.valid, 0)
// wait for valid signal
while (peek(c.io.acc_o.data.valid) == BigInt(0)) {
step(1) // advance clock
}
if (peek(c.io.acc_o.data.valid) == BigInt(1)) {
for (i <- 0 until c.size) {
expect(c.io.acc_o.data.bits(0)(i), res(i) & accMask)
}
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package unittest
// taken from https://github.com/freechipsproject/chisel-testers
import scala.collection.mutable.ArrayBuffer
import chisel3.iotesters._
object TestRunner {
def apply(testMap: Map[String, TesterOptionsManager => Boolean], args: Array[String]): Unit = {
var successful = 0
val errors = new ArrayBuffer[String]
val optionsManager = new TesterOptionsManager()
optionsManager.doNotExitOnHelp()
optionsManager.parse(args)
val programArgs = optionsManager.commonOptions.programArgs
if(programArgs.isEmpty) {
println("Available tests")
for(x <- testMap.keys) {
println(x)
}
println("all")
System.exit(0)
}
val testsToRun = if(programArgs.exists(x => x.toLowerCase() == "all")) {
testMap.keys
}
else {
programArgs
}
for(testName <- testsToRun) {
testMap.get(testName) match {
case Some(test) =>
println(s"Starting $testName")
try {
optionsManager.setTopName(testName)
optionsManager.setTargetDirName(s"test_run_dir/$testName")
if(test(optionsManager)) {
successful += 1
}
else {
errors += s"$testName: test error occurred"
}
}
catch {
case exception: Exception =>
exception.printStackTrace()
errors += s"$testName: exception ${exception.getMessage}"
case t : Throwable =>
errors += s"$testName: throwable ${t.getMessage}"
}
case _ =>
errors += s"Bad Test name: $testName"
}
}
if(successful > 0) {
println(s"Tests passing: $successful")
}
if(errors.nonEmpty) {
println("=" * 80)
println(s"Errors: ${errors.length}: in the following tests")
println(errors.mkString("\n"))
println("=" * 80)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment