diff options
| author | Henry Cook | 2015-08-04 20:44:34 -0700 |
|---|---|---|
| committer | Andrew Waterman | 2015-08-05 16:36:03 -0700 |
| commit | a02d788d9e9c4b42fd866e5c34e42aa771aab68c (patch) | |
| tree | 7d95e9b35ae41ac6f360f718176542bd4d9f2dfd /src | |
| parent | cac4a521b0eea3b715d4a8136cc0efbfd5ebaa3f (diff) | |
add AdvTester
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/Chisel/AdvTester.scala | 223 | ||||
| -rw-r--r-- | src/main/scala/Chisel/Tester.scala | 5 |
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 |
