[VTA] [Chisel] Improved Data Gen, Added ALU Test (#3743)
authorBenjamin Tu <tu.benjamin1115@gmail.com>
Tue, 13 Aug 2019 16:58:32 +0000 (09:58 -0700)
committerThierry Moreau <moreau@uw.edu>
Tue, 13 Aug 2019 16:58:32 +0000 (09:58 -0700)
* added alutest

* fix indent

* name change for cycle

* improved data gen and infra

* added alutest

* fix indent

* name change for cycle

* improved data gen and infra

* fix space

* fix indent

* fixes

* aluRef

* fix randomarary

* add

* Revert "add"

This reverts commit 87077daebbe055dee11f80e37da3a6291138e0f0.

* Revert "fix randomarary"

This reverts commit df386c1e660eb6ebcff1a1f905610573676f1589.

* Revert "aluRef"

This reverts commit 8665f0d4a7b12b796b2cb1ca6bf9cfe5613ee389.

* should fix dlmc-core

vta/hardware/chisel/.gitignore [new file with mode: 0644]
vta/hardware/chisel/src/main/scala/core/TensorAlu.scala
vta/hardware/chisel/src/test/scala/unittest/AluTest.scala [new file with mode: 0644]
vta/hardware/chisel/src/test/scala/unittest/Launcher.scala
vta/hardware/chisel/src/test/scala/unittest/MvmTest.scala
vta/hardware/chisel/src/test/scala/unittest/utils/Helper.scala [new file with mode: 0644]
vta/hardware/chisel/src/test/scala/unittest/utils/RandomArray.scala [new file with mode: 0644]
vta/hardware/chisel/src/test/scala/unittest/utils/TestRunner.scala [moved from vta/hardware/chisel/src/test/scala/unittest/TestRunner.scala with 99% similarity]

diff --git a/vta/hardware/chisel/.gitignore b/vta/hardware/chisel/.gitignore
new file mode 100644 (file)
index 0000000..f65a6ba
--- /dev/null
@@ -0,0 +1 @@
+test_run_dir
index fdab961..fbb0578 100644 (file)
@@ -75,6 +75,7 @@ class AluReg(implicit p: Parameters) extends Module {
 
 /** Vector of pipeline ALUs */
 class AluVector(implicit p: Parameters) extends Module {
+  val aluBits = p(CoreKey).accBits
   val io = IO(new Bundle {
     val opcode = Input(UInt(C_ALU_OP_BITS.W))
     val acc_a = new TensorMasterData(tensorType = "acc")
diff --git a/vta/hardware/chisel/src/test/scala/unittest/AluTest.scala b/vta/hardware/chisel/src/test/scala/unittest/AluTest.scala
new file mode 100644 (file)
index 0000000..13b8578
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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 unittest.util._
+import vta.core._
+
+class TestAluVector(c: AluVector) extends PeekPokeTester(c) {
+    
+  /* alu_ref
+   *
+   * This is a software function used as a reference for the hardware
+   */
+  def aluRef(opcode: Int, a: Array[Int], b: Array[Int], width: Int) : Array[Int] = {
+    val size = a.length
+    val mask = helper.getMask(log2Ceil(width))
+    val res = Array.fill(size) {0}
+    
+    if (opcode == 1) {
+      for (i <- 0 until size) {
+        res(i) = if (a(i) < b(i)) b(i) else a(i)
+      } 
+    } else if (opcode == 2) {
+      for (i <- 0 until size) {
+        res(i) = a(i) + b(i)
+      }
+    } else if (opcode == 3) {
+      for (i <- 0 until size) {
+        res(i) = a(i) >> (b(i) & mask).toInt
+      }
+    } else if (opcode == 4) {
+      // HLS shift left by >> negative number
+      // b always < 0 when opcode == 4
+      for (i <- 0 until size) {
+        res(i) = a(i) << ((-1*b(i)) & mask)
+      }
+    } else {
+      // default
+      for (i <- 0 until size) {
+        res(i) = if (a(i) < b(i)) a(i) else b(i)
+      }
+    }
+    return res
+  } 
+
+  val num_ops = ALU_OP_NUM
+  for (i <- 0 until num_ops) {
+    // generate data based on bits
+    val bits = c.aluBits
+    val dataGen = new RandomArray(c.blockOut, bits)
+    val op = i
+    val in_a = dataGen.any
+    val in_b = if (op != 4) dataGen.any else dataGen.negative
+    val mask = helper.getMask(bits)
+    val res = aluRef(op, in_a, in_b, bits)  
+    
+    for (i <- 0 until c.blockOut) {
+      poke(c.io.acc_a.data.bits(0)(i), in_a(i) & mask)
+      poke(c.io.acc_b.data.bits(0)(i), in_b(i) & mask)
+    }
+    poke(c.io.opcode, op) 
+
+    poke(c.io.acc_a.data.valid, 1)
+    poke(c.io.acc_b.data.valid, 1)
+    poke(c.io.acc_y.data.valid, 1)
+      
+    step(1)
+
+    poke(c.io.acc_a.data.valid, 0)
+    poke(c.io.acc_b.data.valid, 0)
+    poke(c.io.acc_y.data.valid, 0)
+
+    // wait for valid signal
+    while (peek(c.io.acc_y.data.valid) == BigInt(0)) {
+      step(1) // advance clock
+    } 
+    if (peek(c.io.acc_y.data.valid) == BigInt(1)) {
+      for (i <- 0 until c.blockOut) {
+          expect(c.io.acc_y.data.bits(0)(i), res(i) & mask)
+      }
+    }
+  } 
+}
index 53b0c9f..5c80a1e 100644 (file)
@@ -22,6 +22,7 @@ package unittest
 
 import chisel3._
 import chisel3.iotesters.{Driver, TesterOptionsManager}
+import unittest.util._
 import vta.core._
 import vta.util.config._
 import vta.shell._
@@ -45,7 +46,12 @@ object Launcher {
       Driver.execute(() => new MatrixVectorMultiplication, manager) {
         (c) => new TestMatrixVectorMultiplication(c)
       }
-    }
+    },
+               "alu" -> { (manager: TesterOptionsManager) =>
+      Driver.execute(() => new AluVector, manager) {
+        (c) => new TestAluVector(c)
+      }   
+    } 
   )
 
   def main(args: Array[String]): Unit = {
index afa68ea..7709f14 100644 (file)
@@ -22,8 +22,8 @@ package unittest
 import chisel3._
 import chisel3.util._
 import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
-import scala.util.Random
 import scala.math.pow
+import unittest.util._
 import vta.core._
 
 class TestMatrixVectorMultiplication(c: MatrixVectorMultiplication) extends PeekPokeTester(c) {
@@ -39,7 +39,7 @@ class TestMatrixVectorMultiplication(c: MatrixVectorMultiplication) extends Peek
     for (i <- 0 until size) {
         var dot = 0
         for (j <- 0 until size) {
-            dot += wgt(i)(j) * inp(j)
+          dot += wgt(i)(j) * inp(j)
         }
         res(i) = dot * pow(2, shift).toInt
     }
@@ -48,20 +48,21 @@ class TestMatrixVectorMultiplication(c: MatrixVectorMultiplication) extends Peek
 
   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}
+    // generate data based on bits
+    val inpGen = new RandomArray(c.size, c.inpBits)
+    val wgtGen = new RandomArray(c.size, c.wgtBits)
+    val in_a = inpGen.any
+    val in_b = Array.fill(c.size) { wgtGen.any }
     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
+    val inpMask = helper.getMask(c.inpBits)
+    val wgtMask = helper.getMask(c.wgtBits)
+    val accMask = helper.getMask(c.accBits)
     
     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.wgt.data.bits(i)(j), in_b(i)(j) & wgtMask)
       }
     }
     
@@ -79,13 +80,12 @@ class TestMatrixVectorMultiplication(c: MatrixVectorMultiplication) extends Peek
 
     // wait for valid signal
     while (peek(c.io.acc_o.data.valid) == BigInt(0)) {
-        step(1) // advance clock
+      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)
-        }
+      for (i <- 0 until c.size) {
+          expect(c.io.acc_o.data.bits(0)(i), res(i) & accMask)
+      }
     }
   }
-  
 }
diff --git a/vta/hardware/chisel/src/test/scala/unittest/utils/Helper.scala b/vta/hardware/chisel/src/test/scala/unittest/utils/Helper.scala
new file mode 100644 (file)
index 0000000..c6b006a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.util
+
+import scala.math.pow
+
+object helper {
+  def getMask(bits: Int) : Long = {
+    if (bits <= 0) throw new IllegalArgumentException ("bits should be greater than 0")
+    return (pow(2, bits) - 1).toLong
+  }
+}
diff --git a/vta/hardware/chisel/src/test/scala/unittest/utils/RandomArray.scala b/vta/hardware/chisel/src/test/scala/unittest/utils/RandomArray.scala
new file mode 100644 (file)
index 0000000..727ad82
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.util
+
+import scala.util.Random
+import scala.math.pow
+
+class RandomArray(val len: Int, val bits: Int) {
+  val r = new Random
+  if (bits < 1) throw new IllegalArgumentException ("bits should be greater than 1")
+
+  def any : Array[Int] = {
+    return Array.fill(len) { r.nextInt(pow(2, bits).toInt) - pow(2, bits-1).toInt }
+  }
+
+  def positive : Array[Int] = {
+    return Array.fill(len) { r.nextInt(pow(2, bits-1).toInt) }
+  }
+
+  def negative : Array[Int] = {
+    return Array.fill(len) { 0 - r.nextInt(pow(2, bits-1).toInt) }
+  }
+}
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package unittest
+package unittest.util
 // taken from https://github.com/freechipsproject/chisel-testers
 
 import scala.collection.mutable.ArrayBuffer