summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHenry Cook2015-08-04 20:44:34 -0700
committerAndrew Waterman2015-08-05 16:36:03 -0700
commita02d788d9e9c4b42fd866e5c34e42aa771aab68c (patch)
tree7d95e9b35ae41ac6f360f718176542bd4d9f2dfd /src
parentcac4a521b0eea3b715d4a8136cc0efbfd5ebaa3f (diff)
add AdvTester
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/Chisel/AdvTester.scala223
-rw-r--r--src/main/scala/Chisel/Tester.scala5
2 files changed, 226 insertions, 2 deletions
diff --git a/src/main/scala/Chisel/AdvTester.scala b/src/main/scala/Chisel/AdvTester.scala
new file mode 100644
index 00000000..f0e76b2e
--- /dev/null
+++ b/src/main/scala/Chisel/AdvTester.scala
@@ -0,0 +1,223 @@
+/*
+ Copyright (c) 2011, 2012, 2013, 2014 The Regents of the University of
+ California (Regents). All Rights Reserved. Redistribution and use in
+ source and binary forms, with or without modification, are permitted
+ provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the name of the Regents nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+ SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+ ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+ ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
+ TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+ MODIFICATIONS.
+*/
+
+/*
+ Written by Stephen Twigg, Eric Love
+ Version 0.9
+*/
+package Chisel.AdvTester // May eventually add this to the base Chisel package
+import Chisel._
+import scala.collection.mutable.ArrayBuffer
+
+class AdvTester[+T <: Module](val dut: T, isTrace: Boolean = false) extends Tester[T](dut, isTrace) {
+ val defaultMaxCycles = 1024
+ var cycles = 0
+ var pass = true
+
+ // List of scala objects that need to be processed along with the test benches, like sinks and sources
+ val preprocessors = new ArrayBuffer[Processable]()
+ val postprocessors = new ArrayBuffer[Processable]()
+ // pre v post refers to when user-customized update code ('work') is processed
+ // e.g. sinks are in the preprocessing list and sources in the postprocessing list
+ // this allows the testbench to respond to a request within one cycle
+
+ // This section of code lets testers easily emulate have registers right before dut inputs
+ // This testing style conforms with the general ASPIRE testbench style
+ // Also, to ensure difference enforced, poke 'deprecated' and replaced with wire_poke
+ def wire_poke(port: Bits, target: Boolean) = { super.poke(port, int(target)) }
+ def wire_poke(port: Bits, target: Int) = { super.poke(port, int(target)) }
+ def wire_poke(port: Bits, target: Long) = { super.poke(port, int(target)) }
+ def wire_poke(port: Bits, target: BigInt) = { super.poke(port, target) }
+ def wire_poke(port: Aggregate, target: Array[BigInt]) = { super.poke(port, target) }
+
+ override def poke(port: Bits, target: BigInt) = require(false, "poke hidden for AdvTester, use wire_poke or reg_poke")
+ override def poke(port: Aggregate, target: Array[BigInt]) = require(false, "poke hidden for AdvTester, use wire_poke or reg_poke")
+
+ val registered_bits_updates = new scala.collection.mutable.HashMap[Bits,BigInt]()
+ val registered_aggr_updates = new scala.collection.mutable.HashMap[Aggregate,Array[BigInt]]()
+
+ def do_registered_updates() = {
+ registered_bits_updates.foreach( kv => wire_poke(kv._1,kv._2) )
+ registered_aggr_updates.foreach( kv => wire_poke(kv._1,kv._2) )
+
+ registered_bits_updates.clear()
+ registered_aggr_updates.clear()
+ }
+
+ def reg_poke(port: Bits, target: BigInt) = { registered_bits_updates(port) = target }
+ def reg_poke(port: Aggregate, target: Array[BigInt]) = { registered_aggr_updates(port) = target }
+
+ // Convenience functions
+ def Boolean2Int(i: Boolean): Int = (if(i) 1 else 0) // TODO: Investigate name and inclusion as a Chisel Tester auto-convert
+
+ // This function replaces step in the advanced tester and makes sure all tester features are clocked in the appropriate order
+ def takestep(work: => Unit = {}): Unit = {
+ cycles += 1
+ step(1)
+ do_registered_updates()
+ preprocessors.foreach(_.process()) // e.g. sinks
+ work
+ postprocessors.foreach(_.process())
+ }
+ def takesteps(n: Int)(work: =>Unit = {}): Unit = {
+ require(n>0, "Number of steps taken must be positive integer.")
+ (0 until n).foreach(_ => takestep(work))
+ }
+
+ // Functions to step depending on predicates
+ def until(pred: =>Boolean, maxCycles: Int = defaultMaxCycles)(work: =>Unit): Boolean = {
+ var timeout_cycles = 0
+ while(!pred && (timeout_cycles < maxCycles)) {
+ takestep(work)
+ timeout_cycles += 1
+ }
+ assert(timeout_cycles < maxCycles,
+ "until timed out after %d cycles".format(timeout_cycles))
+ pred
+ }
+ def eventually(pred: =>Boolean, maxCycles: Int = defaultMaxCycles) = {until(pred, maxCycles){}}
+ def do_until(work: =>Unit)(pred: =>Boolean, maxCycles: Int = defaultMaxCycles): Boolean = {
+ takestep(work)
+ until(pred, maxCycles){work}
+ }
+
+ def assert(expr: Boolean, errMsg:String = "") = {
+ pass &= expr
+ if(!expr && errMsg != "") { println("ASSERT FAILED: " + errMsg) }
+ expr
+ }
+
+
+ class DecoupledSink[T <: Data, R]( socket: DecoupledIO[T], cvt: T=>R ) extends Processable
+ {
+ var max_count = -1
+ val outputs = new scala.collection.mutable.Queue[R]()
+ private var amReady = false
+ private def isValid = () => (peek(socket.valid) == 1)
+
+ def process() = {
+ // Handle this cycle
+ if(isValid() && amReady) {
+ outputs.enqueue(cvt(socket.bits))
+ }
+ // Decide what to do next cycle and post onto register
+ amReady = max_count < 1 || outputs.length < max_count
+ reg_poke(socket.ready, Boolean2Int(amReady))
+ }
+
+ // Initialize
+ wire_poke(socket.ready, 0)
+ preprocessors += this
+ }
+ object DecoupledSink {
+ def apply[T<:Bits](socket: DecoupledIO[T]) = new DecoupledSink(socket, (socket_bits: T) => peek(socket_bits))
+ }
+
+ class ValidSink[T <: Data, R]( socket: ValidIO[T], cvt: T=>R ) extends Processable
+ {
+ val outputs = new scala.collection.mutable.Queue[R]()
+ private def isValid = peek(socket.valid) == 1
+
+ def process() = {
+ if(isValid) {
+ outputs.enqueue(cvt(socket.bits))
+ }
+ }
+
+ // Initialize
+ preprocessors += this
+ }
+ object ValidSink {
+ def apply[T<:Bits](socket: ValidIO[T]) = new ValidSink(socket, (socket_bits: T) => peek(socket_bits))
+ }
+
+ class DecoupledSource[T <: Data, R]( socket: DecoupledIO[T], post: (T,R)=>Unit ) extends Processable
+ {
+ val inputs = new scala.collection.mutable.Queue[R]()
+
+ private var amValid = false
+ private var justFired = false
+ private def isReady = (peek(socket.ready) == 1)
+ def isIdle = !amValid && inputs.isEmpty && !justFired
+
+ def process() = {
+ justFired = false
+ if(isReady && amValid) { // Fired last cycle
+ amValid = false
+ justFired = true
+ }
+ if(!amValid && !inputs.isEmpty) {
+ amValid = true
+ post(socket.bits, inputs.dequeue())
+ }
+ reg_poke(socket.valid, Boolean2Int(amValid))
+ }
+
+ // Initialize
+ wire_poke(socket.valid, 0)
+ postprocessors += this
+ }
+ object DecoupledSource {
+ def apply[T<:Bits](socket: DecoupledIO[T]) = new DecoupledSource(socket, (socket_bits: T, in: BigInt) => reg_poke(socket_bits, in))
+ }
+
+ class ValidSource[T <: Data, R]( socket: ValidIO[T], post: (T,R)=>Unit ) extends Processable
+ {
+ val inputs = new scala.collection.mutable.Queue[R]()
+ private var amValid = false
+ private var justFired = false
+
+ def isIdle = inputs.isEmpty && !amValid
+
+ def process() = {
+ // Always advance the input
+ justFired = (amValid==true)
+ amValid = false
+ if(!inputs.isEmpty) {
+ amValid = true
+ post(socket.bits, inputs.dequeue())
+ }
+ reg_poke(socket.valid, Boolean2Int(amValid))
+ }
+
+ // Initialize
+ wire_poke(socket.valid, 0)
+ postprocessors += this
+ }
+ object ValidSource {
+ def apply[T<:Bits](socket: ValidIO[T]) = new ValidSource(socket, (socket_bits: T, in: BigInt) => reg_poke(socket_bits, in))
+ }
+
+}
+
+trait Processable {
+ def process(): Unit
+}
+
diff --git a/src/main/scala/Chisel/Tester.scala b/src/main/scala/Chisel/Tester.scala
index f8fb23fc..8f013f7b 100644
--- a/src/main/scala/Chisel/Tester.scala
+++ b/src/main/scala/Chisel/Tester.scala
@@ -286,8 +286,9 @@ class ManualTester[+T <: Module]
}
def int(x: Boolean): BigInt = if (x) 1 else 0
- def int(x: Int): BigInt = x
- def int(x: Bits): BigInt = x.litValue()
+ def int(x: Int): BigInt = (BigInt(x >>> 1) << 1) | x & 1
+ def int(x: Long): BigInt = (BigInt(x >>> 1) << 1) | x & 1
+ def int(x: Bits): BigInt = x.litValue()
var ok = true;
var failureTime = -1