diff options
| author | Henry Cook | 2015-11-04 09:21:07 -0800 |
|---|---|---|
| committer | Henry Cook | 2015-11-04 09:21:07 -0800 |
| commit | a3c9680d1e2b84693759747a4779341ba80c4a50 (patch) | |
| tree | e97ab1d8394b0463ec7f600fce7ba278bd68d93a | |
| parent | 23d15d166d2ed32f8bd9a153a806c09982659011 (diff) | |
Remove Parameters library and refactor Driver.
In addition to removing all the extraneous Driver invocations that created various top-level Parameters instances,
this commit also lays the groundwork for stanza-firrtl/verilator based testing of Modules that extend BasicTester.
The execution-based tests have been updated accordingly. They will only succeed if firrtl and verilator binaries have been installed.
Further work is needed on individual tests to use assertions instead of .io.error.
21 files changed, 438 insertions, 929 deletions
diff --git a/src/main/resources/top.cpp b/src/main/resources/top.cpp new file mode 100644 index 00000000..8951032e --- /dev/null +++ b/src/main/resources/top.cpp @@ -0,0 +1,91 @@ +#include <verilated.h> +#include <iostream> + +#if VM_TRACE +# include <verilated_vcd_c.h> // Trace file format header +#endif + +using namespace std; + +//VGCDTester *top; +TOP_TYPE *top; + +vluint64_t main_time = 0; // Current simulation time + // This is a 64-bit integer to reduce wrap over issues and + // allow modulus. You can also use a double, if you wish. + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; // converts to double, to match + // what SystemC does +} + +// TODO Provide command-line options like vcd filename, timeout count, etc. +const long timeout = 100000000L; + +int main(int argc, char** argv) { + vluint32_t done = 0; + Verilated::commandArgs(argc, argv); // Remember args + top = new TOP_TYPE; + +#if VM_TRACE // If verilator was invoked with --trace + Verilated::traceEverOn(true); // Verilator must compute traced signals + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + top->trace (tfp, 99); // Trace 99 levels of hierarchy + tfp->open ("dump.vcd"); // Open the dump file +#endif + + + top->reset = 1; + + cout << "Starting simulation!\n"; + + while (!Verilated::gotFinish() && !done && main_time < timeout) { + if (main_time > 10) { + top->reset = 0; // Deassert reset + } + if ((main_time % 10) == 1) { + top->clock = 1; // Toggle clock + } + if ((main_time % 10) == 6) { + top->clock = 0; + } + top->eval(); // Evaluate model +#if VM_TRACE + if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp +#endif + done = top->io__024done; + main_time++; // Time passes... + } + + // Run for 10 more clocks + vluint64_t end_time = main_time + 100; + while (main_time < end_time) { + if ((main_time % 10) == 1) { + top->clock = 1; // Toggle clock + } + if ((main_time % 10) == 6) { + top->clock = 0; + } + top->eval(); // Evaluate model +#if VM_TRACE + if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp +#endif + main_time++; // Time passes... + } + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + if (main_time >= timeout) + cout << "Simulation terminated by timeout at cycle " << main_time << endl; + else + cout << "Simulation completed at cycle " << main_time << endl; + + int error = top->io__024error; + cout << "Simulation return value: " << error << endl; + + return error; +} + diff --git a/src/main/scala/Chisel/Aggregate.scala b/src/main/scala/Chisel/Aggregate.scala index 07362b9b..e6ac6b85 100644 --- a/src/main/scala/Chisel/Aggregate.scala +++ b/src/main/scala/Chisel/Aggregate.scala @@ -313,17 +313,6 @@ class Bundle extends Aggregate(NO_DIR) { } } -object Bundle { - private val keywords = - HashSet[String]("flip", "asInput", "asOutput", "cloneType", "toBits") - - def apply[T <: Bundle](b: => T)(implicit p: Parameters): T = { - Builder.paramsScope(p.push){ b } - } - - //TODO @deprecated("Use Chisel.paramsScope object","08-01-2015") - def apply[T <: Bundle](b: => T, f: PartialFunction[Any,Any]): T = { - val q = Builder.getParams.alterPartial(f) - apply(b)(q) - } +private[Chisel] object Bundle { + val keywords = List("flip", "asInput", "asOutput", "cloneType", "toBits") } diff --git a/src/main/scala/Chisel/Driver.scala b/src/main/scala/Chisel/Driver.scala index 5247dc0f..64356b21 100644 --- a/src/main/scala/Chisel/Driver.scala +++ b/src/main/scala/Chisel/Driver.scala @@ -2,75 +2,100 @@ package Chisel -import collection.mutable.{ArrayBuffer, HashSet, HashMap, Stack, LinkedHashSet, Queue => ScalaQueue} -import scala.math.min +import scala.sys.process._ +import java.io._ trait FileSystemUtilities { - def createOutputFile(name: String, contents: String) { - val f = new java.io.FileWriter(name) - f.write(contents) - f.close + def writeTempFile(pre: String, post: String, contents: String): File = { + val t = File.createTempFile(pre, post) + val w = new FileWriter(t) + w.write(contents) + w.close() + t + } + + // This "fire-and-forgets" the method, which can be lazily read through + // a Stream[String], and accumulates all errors on a StringBuffer + def sourceFilesAt(baseDir: String): (Stream[String], StringBuffer) = { + val buffer = new StringBuffer() + val cmd = Seq("find", baseDir, "-name", "*.scala", "-type", "f") + val lines = cmd lines_! ProcessLogger(buffer append _) + (lines, buffer) } } -object Driver extends FileSystemUtilities { +trait BackendCompilationUtilities { + def makeHarness(template: String => String, post: String)(f: File): File = { + val prefix = f.toString.split("/").last + val vf = new File(f.toString + post) + val w = new FileWriter(vf) + w.write(template(prefix)) + w.close() + vf + } - /** Instantiates a ChiselConfig class with the given name and uses it for elaboration */ - def elaborateWithConfigName[T <: Module]( - gen: () => T, - configClassName: String, - projectName: Option[String] = None, - collectConstraints: Boolean = false): Unit = { - val className = projectName match { - case Some(pn) => s"$pn.$configClassName" - case None => configClassName - } - val config = try { - Class.forName(className).newInstance.asInstanceOf[ChiselConfig] - } catch { - case e: java.lang.ClassNotFoundException => - throwException("Could not find the ChiselConfig subclass you asked for (i.e. \"" + - className + "\"), did you misspell it?", e) - } - elaborateWithConfig(gen, config, collectConstraints) + def firrtlToVerilog(prefix: String, dir: File): ProcessBuilder = { + Process( + Seq("firrtl", + "-i", s"$prefix.fir", + "-o", s"$prefix.v", + "-X", "verilog"), + dir) } - /** Uses the provided ChiselConfig for elaboration */ - def elaborateWithConfig[T <: Module]( - gen: () => T, - config: ChiselConfig, - collectConstraints: Boolean = false): Unit = { - val world = if(collectConstraints) config.toCollector else config.toInstance - val p = Parameters.root(world) - config.topConstraints.foreach(c => p.constrain(c)) - elaborate(gen, p, config) + def verilogToCpp( + prefix: String, + dir: File, + vDut: File, + cppHarness: File, + vH: File): ProcessBuilder = + Seq("verilator", + "--cc", vDut.toString, + "--assert", + "--Wno-fatal", + "--trace", + "-O2", + "+define+TOP_TYPE=V"+prefix, + "-CFLAGS", s"""-Wno-undefined-bool-conversion -O2 -DTOP_TYPE=V$prefix -include ${vH.toString}""", + "-Mdir", dir.toString, + "--exe", cppHarness.toString) + + def cppToExe(prefix: String, dir: File): ProcessBuilder = + Seq("make", "-C", dir.toString, "-j", "-f", s"V${prefix}.mk", s"V${prefix}") + + def executeExpectingFailure( + prefix: String, + dir: File, + assertionMsg: String = "Assertion failed"): Boolean = { + var triggered = false + val e = Process(s"./V${prefix}", dir) ! ProcessLogger(line => + triggered = triggered || line.contains(assertionMsg)) + triggered } - /** Elaborates the circuit specified in the gen function, optionally uses - * a parameter space to supply context-aware values. - * TODO: Distinguish between cases where we dump to file vs return IR for - * use by other Drivers. - */ - private[Chisel] def elaborateWrappedModule[T <: Module](gen: () => T, p: Parameters, c: Option[ChiselConfig]) { - val ir = Builder.build(gen()) - val name = c match { - case None => ir.name - case Some(config) => s"${ir.name}.$config" - } - createOutputFile(s"$name.knb", p.getKnobs) - createOutputFile(s"$name.cst", p.getConstraints) - createOutputFile(s"$name.prm", ir.parameterDump.getDump) - createOutputFile(s"$name.fir", ir.emit) + def executeExpectingSuccess(prefix: String, dir: File): Boolean = { + !executeExpectingFailure(prefix, dir) } - def elaborate[T <: Module](gen: () => T): Unit = - elaborate(gen, Parameters.empty) - def elaborate[T <: Module](gen: () => T, p: Parameters): Unit = - elaborateWrappedModule(() => Module(gen())(p), p, None) - private def elaborate[T <: Module](gen: () => T, p: Parameters, c: ChiselConfig): Unit = - elaborateWrappedModule(() => Module(gen())(p), p, Some(c)) + } -object chiselMain { - def apply[T <: Module](args: Array[String], gen: () => T, p: Parameters = Parameters.empty): Unit = - Driver.elaborateWrappedModule(gen, p, None) +object Driver extends FileSystemUtilities with BackendCompilationUtilities { + + /** Elaborates the Module specified in the gen function into a Circuit + * + * @param gen a function that creates a Module hierarchy + * + * @return the resulting Chisel IR in the form of a Circuit (TODO: Should be FIRRTL IR) + */ + def elaborate[T <: Module](gen: () => T): Circuit = Builder.build(Module(gen())) + + def emit[T <: Module](gen: () => T): String = elaborate(gen).emit + + def dumpFirrtl(ir: Circuit, optName: Option[File]): File = { + val f = optName.getOrElse(new File(ir.name + ".fir")) + val w = new FileWriter(f) + w.write(ir.emit) + w.close() + f + } } diff --git a/src/main/scala/Chisel/Module.scala b/src/main/scala/Chisel/Module.scala index 1c1c02de..f799a7ce 100644 --- a/src/main/scala/Chisel/Module.scala +++ b/src/main/scala/Chisel/Module.scala @@ -6,37 +6,27 @@ import Builder.pushCommand import Builder.dynamicContext object Module { - // TODO: update documentation when parameters gets removed from core Chisel - // and this gets simplified. /** A wrapper method that all Module instantiations must be wrapped in * (necessary to help Chisel track internal state). * * @param m the Module being created - * @param p Parameters passed down implicitly from that it is created in * - * @return the input module `m` + * @return the input module `m` with Chisel metadata properly set */ - def apply[T <: Module](bc: => T)(implicit currParams: Parameters = Builder.getParams.push): T = { - paramsScope(currParams) { - val parent = dynamicContext.currentModule - val m = bc.setRefs() - // init module outputs - m._commands prependAll (for (p <- m.io.flatten; if p.dir == OUTPUT) - yield Connect(p.lref, p.fromInt(0).ref)) - dynamicContext.currentModule = parent - val ports = m.computePorts - Builder.components += Component(m, m.name, ports, m._commands) - pushCommand(DefInstance(m, ports)) - // init instance inputs - for (p <- m.io.flatten; if p.dir == INPUT) - p := p.fromInt(0) - m - }.connectImplicitIOs() - } - - //TODO @deprecated("Use Chisel.paramsScope object","08-01-2015") - def apply[T <: Module](m: => T, f: PartialFunction[Any,Any]): T = { - apply(m)(Builder.getParams.alterPartial(f)) + def apply[T <: Module](bc: => T): T = { + val parent = dynamicContext.currentModule + val m = bc.setRefs() + // init module outputs + m._commands prependAll (for (p <- m.io.flatten; if p.dir == OUTPUT) + yield Connect(p.lref, p.fromInt(0).ref)) + dynamicContext.currentModule = parent + val ports = m.computePorts + Builder.components += Component(m, m.name, ports, m._commands) + pushCommand(DefInstance(m, ports)) + // init instance inputs + for (p <- m.io.flatten; if p.dir == INPUT) + p := p.fromInt(0) + m.connectImplicitIOs() } } diff --git a/src/main/scala/Chisel/Parameters.scala b/src/main/scala/Chisel/Parameters.scala deleted file mode 100644 index c2d32fbd..00000000 --- a/src/main/scala/Chisel/Parameters.scala +++ /dev/null @@ -1,669 +0,0 @@ -/* - Constructing Hardware in a Scala Embedded Language, Copyright (c) 2014, The - Regents of the University of California, through Lawrence Berkeley National - Laboratory (subject to receipt of any required approvals from the U.S. Dept. - of Energy). All rights reserved. - - If you have questions about your rights to use or distribute this software, - please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov. - - NOTICE. This software is owned by the U.S. Department of Energy. As such, - the U.S. Government has been granted for itself and others acting on its - behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software - to reproduce, prepare derivative works, and perform publicly and display - publicly. Beginning five (5) years after the date permission to assert - copyright is obtained from the U.S. Department of Energy, and subject to any - subsequent five (5) year renewals, the U.S. Government is granted for itself - and others acting on its behalf a paid-up, nonexclusive, irrevocable, - worldwide license in the Software to reproduce, prepare derivative works, - distribute copies to the public, perform publicly and display publicly, and to - permit others to do so. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - (1) Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - (2) Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - (3) Neither the name of the University of California, Lawrence Berkeley - National Laboratory, U.S. Dept. of Energy nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You are under no obligation whatsoever to provide any bug fixes, patches, or - upgrades to the features, functionality or performance of the source code - ("Enhancements") to anyone; however, if you choose to make your Enhancements - available either publicly, or directly to Lawrence Berkeley National - Laboratory, without imposing a separate written license agreement for such - Enhancements, then you hereby grant the following license: a non-exclusive, - royalty-free perpetual license to install, use, modify, prepare derivative - works, incorporate into other computer software, distribute, and sublicense - such enhancements or derivative works thereof, in binary and source code form. - - Authors: J. Bachan, A. Izraelevitz, H. Cook -*/ - -package Chisel - -import scala.collection.immutable.{Seq=>Seq, Iterable=>Iterable} -import scala.{collection=>readonly} -import scala.collection.mutable - -// Convention: leading _'s on names means private to the outside world -// but accessible to anything in this file. - -@deprecated("params is now globally available as Chisel.params object","08-01-2015") -abstract trait UsesParameters { } - -object params { - def apply[T](field:Any):T = Builder.getParams.apply(field) - def apply[T](field:Field[T]):T = Builder.getParams.apply(field) - // TODO: provide other mutators of Parameters? or @deprecate this and make - // Parameters private, only mutateable through paramsScope? - def alterPartial[T](mask: PartialFunction[Any,Any]): Parameters = { - Builder.getParams.alterPartial(mask) - } - def constrain(gen:ViewSym=>Ex[Boolean]) = Builder.getParams.constrain(gen) -} - -object paramsScope { - def apply[T](p: Parameters)(body: => T): T = { - Builder.paramsScope(p)(body) - } - def apply[T,S](mask: Map[S,Any])(body: => T): T = { - apply(Builder.getParams.alter(mask))(body) - } - def apply[T](mask: PartialFunction[Any,Any])(body: => T): T = { - apply(Builder.getParams.alterPartial(mask))(body) - } -} - -class ParameterUndefinedException(field:Any, cause:Throwable=null) - extends RuntimeException("Parameter " + field + " undefined.", cause) -class KnobUndefinedException(field:Any, cause:Throwable=null) - extends RuntimeException("Knob " + field + " undefined.", cause) - -// Knobs are top level free variables that go into the constraint solver. -final case class Knob[T](name:Any) - -// Fields are wrappers around particular a particular parameter's type -class Field[T] - -class ChiselConfig( - val topDefinitions: World.TopDefs = { (a,b,c) => {throw new scala.MatchError(a)}}, - val topConstraints: List[ViewSym=>Ex[Boolean]] = List( ex => ExLit[Boolean](true) ), - val knobValues: Any=>Any = { case x => {throw new scala.MatchError(x)}} -) { - import Implicits._ - type Constraint = ViewSym=>Ex[Boolean] - - def this(that: ChiselConfig) = this(that.topDefinitions, - that.topConstraints, - that.knobValues) - - def ++(that: ChiselConfig) = { - new ChiselConfig(this.addDefinitions(that.topDefinitions), - this.addConstraints(that.topConstraints), - this.addKnobValues(that.knobValues)) - } - - def addDefinitions(that: World.TopDefs): World.TopDefs = { - (pname,site,here) => { - try this.topDefinitions(pname, site, here) - catch { - case e: scala.MatchError => that(pname, site, here) - } - } - } - - def addConstraints(that: List[Constraint]):List[Constraint] = { - this.topConstraints ++ that - } - - - def addKnobValues(that: Any=>Any): Any=>Any = { case x => - try this.knobValues(x) - catch { - case e: scala.MatchError => that(x) - } - } - - def toCollector = new Collector(this.topDefinitions, this.knobValues) - def toInstance = new Instance(this.topDefinitions, this.knobValues) - override def toString = this.getClass.getSimpleName -} - -// TODO eliminate this or move it to DynamicContext -object Dump { - def apply[T](key:Any,value:T):T = Builder.parameterDump.apply(key, value) - def apply[T](knob:Knob[T]):Knob[T] = Builder.parameterDump.apply(knob) -} - -class ParameterDump { - val dump = mutable.Set[Tuple2[Any,Any]]() - val knobList = mutable.ListBuffer[Any]() - def apply[T](key:Any,value:T):T = {addToDump(key,value); value} - def apply[T](knob:Knob[T]):Knob[T] = {knobList += knob.name; knob} - def addToDump(key:Any,value:Any) = dump += ((key,value)) - def getDump:String = if (!dump.isEmpty) { - dump.map(_.toString).reduce(_+"\n"+_) + "\n" - } else { - "" - } -} - -// objects given to the user in mask functions (site,here,up) -abstract class View { - protected val deftSite: View // when views are queried without a specifying a site this is the default - - // use `this` view's behavior to query for a parameters value as if - // the original site were `site` - def apply[T](pname:Any, site:View):T - def sym[T](pname:Any, site:View):Ex[T] - - // query for a parameters value using the default site - final def apply[T](pname:Any):T = apply[T](pname, deftSite) - final def apply[T](field:Field[T]):T = apply[T](field.asInstanceOf[Any], deftSite) - - final def sym[T](pname:Any):Ex[T] = sym[T](pname, deftSite) - final def sym[T](field:Field[T]):Ex[T] = sym[T](field.asInstanceOf[Any], deftSite) -} - -/* Wrap a View to make the application return the symbolic expression, - * basically a shorthand to save typing '.sym' - * before: - * val v: View - * v.sym[Int]("x") // returns Ex[_] - * now: - * val vs = ViewSym(v) - * vs[Int]("xs") // Ex[_] -*/ -final case class ViewSym(view:View) { - def apply[T](f:Any):Ex[T] = view.sym[T](f) - def apply[T](f:Field[T]):Ex[T] = view.sym[T](f) - def apply[T](f:Any, site:View):Ex[T] = view.sym[T](f, site) - def apply[T](f:Field[T], site:View):Ex[T] = view.sym[T](f, site) -} - - -// internal type to represent functions that evaluate parameter values -abstract class _Lookup { - - def apply[T](pname:Any, site:View):Ex[T] - - // build a new Lookup that just defers to this one - final def push() = { - val me = this - new _Lookup { - def apply[T](pname:Any, site:View) = me.apply(pname, site) - } - } -} - -// Internal type used as name in all ExVar[T]'s -sealed abstract class _Var[T] - -// Variables which are 'free' parameters when seen from the top level. -final case class _VarKnob[T](kname:Any) extends _Var[T] { - override def toString = kname.toString -} -// Variables whose values are computed by `expr`. The term 'let' comes -// from the idea of 'let' bindings in functional languages i.e.: -final case class _VarLet[T](pname:Any,expr:Ex[T]) extends _Var[T] { - override def toString = pname.toString + "{" + expr.toString + "}" -} - - -object World { - // An alias for the type of function provided by user to describe parameters that - // reach the top level. The return of this function can be either: - // Knob(k): this parameter maps to the constraint variable `k` - // Ex: this parameter is computed using the expression - // Any(thing else): variable takes a literal value - type TopDefs = (/*pname:*/Any,/*site:*/View,/*here:*/View) => Any/*Knob[_] | Ex[_] | Any*/ -} - -// Worlds collect the variable definitions and constraints seen when building hardware. -abstract class World( - topDefs: World.TopDefs - ) { - - val _knobs = new mutable.HashSet[Any] - abstract class _View extends View { - val look: _Lookup - - def apply[T](pname:Any, site:View):T = { - _eval(look(pname, site).asInstanceOf[Ex[T]]) - } - def sym[T](pname:Any, site:View):Ex[T] = { - _bindLet[T](pname,look(pname, site).asInstanceOf[Ex[T]]) - } - } - - // evaluate an expression against this world - def _eval[T](e:Ex[T]):T = { - Ex.eval(e, { - case v:_VarKnob[_] => { - _knobs += v.kname - val e = _knobValue(v.kname) - if(Builder.parameterDump.knobList.contains(v.kname)) {Builder.parameterDump.addToDump(v.kname,e);e} else e - } - case v:_VarLet[_] => _eval(v.expr.asInstanceOf[Ex[T]]) - }) - } - - // create a view whose default site is itself - def _siteView(look:_Lookup):View = { - val _look = look - new _View { - val look = _look - val deftSite = this - } - } - - // create a View which with a supplied default site - def _otherView(look:_Lookup, deftSite:View):View = { - val _look = look - val _deft = deftSite - new _View { - val look = _look - val deftSite = _deft - } - } - - // the top level lookup - def _topLook():_Lookup = { - class TopLookup extends _Lookup { - - def apply[T](pname:Any, site:View):Ex[T] = { - val here = _otherView(this, site) - ( - try topDefs(pname, site, here) - catch { - case e:scala.MatchError => throw new ParameterUndefinedException(pname, e) - } - ) match { - case k:Knob[T @unchecked] => ExVar[T](_VarKnob[T](k.name)) - case ex:Ex[T @unchecked] => _bindLet[T](pname,ex) - case lit => ExLit(lit.asInstanceOf[T]) - } - } - } - new TopLookup - } - - def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T] - - def _constrain(e:Ex[Boolean]):Unit - - def _knobValue(kname:Any):Any - - def getConstraints:String = "" - - def getKnobs:String = "" -} - -// a world responsible for collecting all constraints in the first pass -class Collector( - topDefs: World.TopDefs, - knobVal: Any=>Any // maps knob names to default-values - ) - extends World(topDefs) { - - val _constraints = new mutable.HashSet[Ex[Boolean]] - - def knobs():List[Any] = { - _knobs.toList - } - - def constraints():List[Ex[Boolean]] = { - _constraints.toList - } - - def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T] = { - expr match { - case e:ExVar[T] => expr - case e:ExLit[T] => expr - case _ => ExVar[T](_VarLet[T](pname,expr)) - } - } - - def _constrain(c:Ex[Boolean]) = { - _constraints += c // add the constraint - - // Also add all equality constraints for all bound variables in the - // constraint expression and do it recursively for all expressions - // being bound to. - var q = List[Ex[_]](c) - while(!q.isEmpty) { - val e = q.head // pop an expression - q = q.tail - // walk over the variables in `e` - for(e <- Ex.unfurl(e)) { - e match { - case ExVar(_VarLet(p,e1)) => { - // form the equality constraint - val c1 = ExEq[Any](e.asInstanceOf[Ex[Any]], e1.asInstanceOf[Ex[Any]]) - // recurse into the expression if its never been seen before - if(!_constraints.contains(c1)) { - _constraints += c1 - q ::= e1 // push - } - } - case _ => {} - } - } - } - } - - def _knobValue(kname:Any) = { - try knobVal(kname) - catch { - case e:scala.MatchError => throw new KnobUndefinedException(kname, e) - } - } - - override def getConstraints:String = if(constraints.isEmpty) "" else constraints.map("( " + _.toString + " )").reduce(_ +"\n" + _) + "\n" - - override def getKnobs:String = if(knobs.isEmpty) "" else { - knobs.map(_.toString).reduce(_ + "\n" + _) + "\n" - } -} - -// a world instantianted to a specific mapping of knobs to values -class Instance( - topDefs: World.TopDefs, - knobVal: Any=>Any - ) - extends World(topDefs) { - - def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T] = expr - def _constrain(e:Ex[Boolean]) = {} - def _knobValue(kname:Any) = { - try knobVal(kname) - catch { - case e:scala.MatchError => throw new KnobUndefinedException(kname, e) - } - } -} - -object Parameters { - def root(w:World) = { - new Parameters(w, w._topLook()) - } - def empty = Parameters.root(new Collector((a,b,c) => {throw new ParameterUndefinedException(a); a},(a:Any) => {throw new KnobUndefinedException(a); a})) - - // Mask making helpers - - // Lift a regular function into a mask by looking for MatchError's and - // interpreting those as calls to up - def makeMask(mask:(Any,View,View,View)=>Any) = { - (f:Any, site:View, here:View, up:View) => { - try mask(f,site,here,up) - catch {case e:MatchError => up.sym[Any](f, site)} - } - } - - // Lift a Map to be a mask. - def makeMask(mask:Map[Any,Any]) = { - (f:Any, site:View, here:View, up:View) => { - mask.get(f) match { - case Some(y) => y - case None => up.sym[Any](f, site) - } - } - } - - // Lift a PartialFunction to be a mask. - def makeMask(mask:PartialFunction[Any,Any]) = { - (f:Any, site:View, here:View, up:View) => { - - if(mask.isDefinedAt(f)) - mask.apply(f) - else { - up.sym[Any](f, site) - } - } - } -} - -final class Parameters( - private val _world: World, - private val _look: _Lookup - ) { - - private def _site() = _world._siteView(_look) - - // Create a new Parameters that just defers to this one. This is identical - // to doing an `alter` but not overriding any values. - def push():Parameters = - new Parameters(_world, _look.push()) - - def apply[T](field:Any):T = - _world._eval(_look(field, _site())).asInstanceOf[T] - - def apply[T](field:Field[T]):T = - _world._eval(_look(field, _site())).asInstanceOf[T] - - def constrain(gen:ViewSym=>Ex[Boolean]) = { - val g = gen(new ViewSym(_site())) - if(!_world._eval(g)) Builder.error("Constraint failed: " + g.toString) - _world._constrain(g) - } - - private def _alter(mask:(/*field*/Any,/*site*/View,/*here*/View,/*up*/View)=>Any) = { - class KidLookup extends _Lookup { - - def apply[T](f:Any, site:View):Ex[T] = { - val here = _world._otherView(this, site) - val up = _world._otherView(_look, site) - - mask(f, site, here, up) match { - case e:Ex[T @unchecked] => e - case lit => ExLit(lit.asInstanceOf[T]) - } - } - } - - new Parameters(_world, new KidLookup) - } - - def alter(mask:(/*field*/Any,/*site*/View,/*here*/View,/*up*/View)=>Any) = - _alter(Parameters.makeMask(mask)) - - def alter[T](mask:Map[T,Any]) = - _alter(Parameters.makeMask(mask.asInstanceOf[Map[Any,Any]])) - - def alterPartial(mask:PartialFunction[Any,Any]) = - _alter(Parameters.makeMask(mask)) - - def getConstraints:String = _world.getConstraints - - def getKnobs:String = _world.getKnobs -} - - -/* - Expression Library -*/ -abstract class Ex[T] { - override def toString = Ex.pretty(this) -} - -case class IntEx (expr:Ex[Int]) { - def === (x:IntEx):Ex[Boolean] = (ExEq[Int](expr,x.expr)) - def + (x:IntEx):Ex[Int] = ExAdd(expr,x.expr) - def - (x:IntEx):Ex[Int] = ExSub(expr,x.expr) - def * (x:IntEx):Ex[Int] = ExMul(expr,x.expr) - def % (x:IntEx):Ex[Int] = ExMod(expr,x.expr) - def < (x:IntEx):Ex[Boolean] = ExLt(expr,x.expr) - def > (x:IntEx):Ex[Boolean] = ExGt(expr,x.expr) - def <= (x:IntEx):Ex[Boolean] = ExLte(expr,x.expr) - def >= (x:IntEx):Ex[Boolean] = ExGte(expr,x.expr) - def in (x:List[IntEx]):Ex[Boolean] = { - val canBound = x.map(_.expr match { - case e:ExVar[_] => false - case _ => true - }).reduce(_ && _) - if (canBound) { - val max = x.map(i => Ex.eval(i.expr,(x:Any)=>null)).max - val min = x.map(i => Ex.eval(i.expr,(x:Any)=>null)).min - ExAnd(IntEx(expr) in Range(min,max), IntEx(expr) _in x) - } else { - IntEx(expr) _in x - } - } - def in (x:Range):Ex[Boolean] = ExAnd(ExGte(expr,ExLit[Int](x.min)),ExLte(expr,ExLit[Int](x.max))) - private def _in (x:List[IntEx]):Ex[Boolean] = { - if (x.isEmpty) ExLit[Boolean](false) else { - ExOr(IntEx(expr) === x.head,IntEx(expr) _in x.tail) - } - } -} - -case class BoolEx (expr:Ex[Boolean]) { - def && (x:BoolEx):Ex[Boolean] = ExAnd(expr,x.expr) - def || (x:BoolEx):Ex[Boolean] = ExOr(expr,x.expr) - def ^ (x:BoolEx):Ex[Boolean] = ExXor(expr,x.expr) - def === (x:BoolEx):Ex[Boolean] = ExEq[Boolean](expr,x.expr) - def !== (x:BoolEx):Ex[Boolean] = ExEq[Boolean](expr,x.expr) -} - -object Implicits { - implicit def ExInt_IntEx(i:Ex[Int]):IntEx = IntEx(i) - implicit def Int_IntEx(i:Int):IntEx = IntEx(ExLit[Int](i)) - implicit def ExBool_BoolEx(b:Ex[Boolean]):BoolEx = BoolEx(b) - implicit def Bool_IntEx(b:Boolean):BoolEx = BoolEx(ExLit[Boolean](b)) - - implicit def ListInt_ListExInt(l:List[Int]):List[IntEx] = l.map((x:Int) => IntEx(ExLit[Int](x))) - implicit def ListExInt_ListExInt(l:List[Ex[Int]]):List[IntEx] = l.map((x:Ex[Int]) => IntEx(x)) -} - -final case class ExLit[T](value:T) extends Ex[T] -final case class ExVar[T](name:Any) extends Ex[T] - -final case class ExAnd(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean] -final case class ExOr(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean] -final case class ExXor(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean] - -final case class ExEq[T](a:Ex[T], b:Ex[T]) extends Ex[Boolean] -final case class ExNeq[T](a:Ex[T], b:Ex[T]) extends Ex[Boolean] - -final case class ExLt(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean] -final case class ExLte(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean] -final case class ExGt(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean] -final case class ExGte(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean] -final case class ExAdd(a:Ex[Int], b:Ex[Int]) extends Ex[Int] -final case class ExSub(a:Ex[Int], b:Ex[Int]) extends Ex[Int] -final case class ExMul(a:Ex[Int], b:Ex[Int]) extends Ex[Int] -final case class ExMod(a:Ex[Int], b:Ex[Int]) extends Ex[Int] - -object Ex { - // evaluate an expression given a context that maps variable names to values - def eval[T](e:Ex[T], ctx:Any=>Any):T = e match { - case ExLit(v) => v.asInstanceOf[T] - case ExVar(nm) => ctx(nm).asInstanceOf[T] - case ExAnd(a,b) => eval(a,ctx) && eval(b,ctx) - case ExOr(a,b) => eval(a,ctx) || eval(b,ctx) - case ExXor(a,b) => eval(a,ctx) ^ eval(b,ctx) - case e:ExEq[u] => eval(e.a,ctx) == eval(e.b,ctx) - case e:ExNeq[u] => eval(e.a,ctx) != eval(e.b,ctx) - case ExLt(a,b) => eval(a,ctx) < eval(b,ctx) - case ExLte(a,b) => eval(a,ctx) <= eval(b,ctx) - case ExGt(a,b) => eval(a,ctx) > eval(b,ctx) - case ExGte(a,b) => eval(a,ctx) >= eval(b,ctx) - case ExAdd(a,b) => eval(a,ctx) + eval(b,ctx) - case ExSub(a,b) => eval(a,ctx) - eval(b,ctx) - case ExMul(a,b) => eval(a,ctx) * eval(b,ctx) - case ExMod(a,b) => eval(a,ctx) % eval(b,ctx) - } - - // get shallow list of subexpressions - def subExs(e:Ex[_]):List[Ex[_]] = e match { - case ExLit(_) => Nil - case ExVar(_) => Nil - case ExAnd(a,b) => List(a,b) - case ExOr(a,b) => List(a,b) - case ExXor(a,b) => List(a,b) - case ExEq(a,b) => List(a,b) - case ExNeq(a,b) => List(a,b) - case ExLt(a,b) => List(a,b) - case ExLte(a,b) => List(a,b) - case ExGt(a,b) => List(a,b) - case ExGte(a,b) => List(a,b) - case ExAdd(a,b) => List(a,b) - case ExSub(a,b) => List(a,b) - case ExMul(a,b) => List(a,b) - case ExMod(a,b) => List(a,b) - } - - // get all subexpressions including the expression given - def unfurl(e:Ex[_]):List[Ex[_]] = - e :: (subExs(e) flatMap unfurl) - - // pretty-print expression - def pretty(e:Ex[_]):String = { - // precedence rank for deciding where to put parentheses - def rank(e:Ex[_]):Int = e match { - case e:ExAnd => 40 - case e:ExOr => 50 - case e:ExXor => 50 - case e:ExEq[_] => 30 - case e:ExNeq[_] => 30 - case e:ExLt => 30 - case e:ExLte => 30 - case e:ExGt => 30 - case e:ExGte => 30 - case e:ExAdd => 20 - case e:ExSub => 20 - case e:ExMul => 20 - case e:ExMod => 20 - case e:ExLit[_] => 0 - case e:ExVar[_] => 0 - } - - val r = rank(e) - - def term(t:Ex[_]):String = { - val rt = rank(t) - //if(rt >= r) - "( " + t.toString + " )" - //else - //t.toString - } - - import Implicits._ - e match { - case ExLit(v) => v.toString - case e:ExVar[_]=> "$"+e.name - case ExAnd(a,b) => term(a)+" && "+term(b) - case ExOr(a,b) => term(a)+" || "+term(b) - case ExXor(a,b) => term(a)+" ^ "+term(b) - case ExEq(a,b) => term(a)+" = "+term(b) - case ExNeq(a,b) => term(a)+" != "+term(b) - case ExLt(a,b) => term(a)+" < "+term(b) - case ExLte(a,b) => term(a)+" <= "+term(b) - case ExGt(a,b) => term(a)+" > "+term(b) - case ExGte(a,b) => term(a)+" >= "+term(b) - case ExAdd(a,b) => term(a)+" + "+term(b) - case ExSub(a,b) => term(a)+" - "+term(b) - case ExMul(a,b) => term(a)+" * "+term(b) - case ExMod(a,b) => term(a)+" % "+term(b) - } - } -} diff --git a/src/main/scala/Chisel/internal/Builder.scala b/src/main/scala/Chisel/internal/Builder.scala index b3c4ae40..79d5d6aa 100644 --- a/src/main/scala/Chisel/internal/Builder.scala +++ b/src/main/scala/Chisel/internal/Builder.scala @@ -74,21 +74,18 @@ private class DynamicContext { val globalRefMap = new RefMap val components = ArrayBuffer[Component]() var currentModule: Option[Module] = None - val parameterDump = new ParameterDump val errors = new ErrorLog } private object Builder { // All global mutable state must be referenced via dynamicContextVar!! private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None) - private val currentParamsVar = new DynamicVariable[Parameters](Parameters.empty) def dynamicContext: DynamicContext = dynamicContextVar.value.get def idGen: IdGen = dynamicContext.idGen def globalNamespace: Namespace = dynamicContext.globalNamespace def globalRefMap: RefMap = dynamicContext.globalRefMap def components: ArrayBuffer[Component] = dynamicContext.components - def parameterDump: ParameterDump = dynamicContext.parameterDump def pushCommand[T <: Command](c: T): T = { dynamicContext.currentModule.foreach(_._commands += c) @@ -99,11 +96,6 @@ private object Builder { def errors: ErrorLog = dynamicContext.errors def error(m: => String): Unit = errors.error(m) - def getParams: Parameters = currentParamsVar.value - def paramsScope[T](p: Parameters)(body: => T): T = { - currentParamsVar.withValue(p)(body) - } - def build[T <: Module](f: => T): Circuit = { dynamicContextVar.withValue(Some(new DynamicContext)) { errors.info("Elaborating design...") @@ -112,7 +104,7 @@ private object Builder { errors.checkpoint() errors.info("Done elaborating.") - Circuit(components.last.name, components, globalRefMap, parameterDump) + Circuit(components.last.name, components, globalRefMap) } } } diff --git a/src/main/scala/Chisel/ir/IR.scala b/src/main/scala/Chisel/ir/IR.scala index e25d3f56..106ad20c 100644 --- a/src/main/scala/Chisel/ir/IR.scala +++ b/src/main/scala/Chisel/ir/IR.scala @@ -152,6 +152,6 @@ case class ConnectInit(loc: Node, exp: Arg) extends Command case class Component(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Arg case class Port(id: Data, dir: Direction) -case class Circuit(name: String, components: Seq[Component], refMap: RefMap, parameterDump: ParameterDump) { +case class Circuit(name: String, components: Seq[Component], refMap: RefMap) { def emit: String = new Emitter(this).toString } diff --git a/src/main/scala/Chisel/testers/BasicTester.scala b/src/main/scala/Chisel/testers/BasicTester.scala index c73567c4..dbb269bb 100644 --- a/src/main/scala/Chisel/testers/BasicTester.scala +++ b/src/main/scala/Chisel/testers/BasicTester.scala @@ -10,4 +10,6 @@ class BasicTester extends Module { } io.done := Bool(false) io.error := UInt(0) + + def popCount(n: Long) = n.toBinaryString.count(_=='1') } diff --git a/src/main/scala/Chisel/testers/Driver.scala b/src/main/scala/Chisel/testers/Driver.scala deleted file mode 100644 index 860ef69c..00000000 --- a/src/main/scala/Chisel/testers/Driver.scala +++ /dev/null @@ -1,21 +0,0 @@ -// See LICENSE for license details. - -package Chisel.testers -import Chisel._ - -object TesterDriver { - /** For use with modules that should successfully be elaborated by the - * frontend, and which can be turned into executeables with error codes. */ - def execute(t: => BasicTester): Boolean = { - val circuit = Builder.build(Module(t)) - //val executable = invokeFIRRTL(circuit) - //Process(executable) ! - true - } - - /** For use with modules that should illicit errors from the frontend - * or which produce IR with consistantly checkable properties. */ - def elaborate(t: => Module): Circuit = { - Builder.build(Module(t)) - } -} diff --git a/src/main/scala/Chisel/testers/TesterDriver.scala b/src/main/scala/Chisel/testers/TesterDriver.scala new file mode 100644 index 00000000..657f7d37 --- /dev/null +++ b/src/main/scala/Chisel/testers/TesterDriver.scala @@ -0,0 +1,37 @@ +// See LICENSE for license details. + +package Chisel.testers +import Chisel._ +import scala.sys.process._ +import java.io.File + +object TesterDriver extends BackendCompilationUtilities with FileSystemUtilities { + /** For use with modules that should successfully be elaborated by the + * frontend, and which can be turned into executeables with assertions. */ + def execute(t: () => BasicTester): Boolean = { + // Invoke the chisel compiler to get the circuit's IR + val circuit = Driver.elaborate(t) + + // Set up a bunch of file handlers based on a random temp filename, + // plus the quirks of Verilator's naming conventions + val target = circuit.name + val fname = File.createTempFile(target, "") + val path = fname.getParentFile.toString + val prefix = fname.toString.split("/").last + val dir = new File(System.getProperty("java.io.tmpdir")) + val vDut = new File(fname.toString + ".v") + val vH = new File(path + "/V" + prefix + ".h") + val cppHarness = new File(fname.toString + ".cpp") + + // For now, dump the IR out to a file + Driver.dumpFirrtl(circuit, Some(new File(fname.toString + ".fir"))) + + // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe + if(((new File(System.getProperty("user.dir") + "/src/main/resources/top.cpp") #> cppHarness) #&& + firrtlToVerilog(prefix, dir) #&& + verilogToCpp(prefix, dir, vDut, cppHarness, vH) #&& + cppToExe(prefix, dir)).! == 0) { + executeExpectingSuccess(prefix, dir) + } else false + } +} diff --git a/src/test/scala/chiselTests/BitwiseOps.scala b/src/test/scala/chiselTests/BitwiseOps.scala index 86c7131b..d180c11e 100644 --- a/src/test/scala/chiselTests/BitwiseOps.scala +++ b/src/test/scala/chiselTests/BitwiseOps.scala @@ -7,18 +7,18 @@ import org.scalatest._ import org.scalatest.prop._ import Chisel.testers.BasicTester -class BitwiseOpsSpec extends ChiselPropSpec { +class BitwiseOpsTester(w: Int, _a: Int, _b: Int) extends BasicTester { + io.done := Bool(true) + val mask = (1 << w) - 1 + val a = UInt(_a) + val b = UInt(_b) + when(~a != UInt(mask & ~_a)) { io.error := UInt(1) } + when((a & b) != UInt(mask & (_a & _b))) { io.error := UInt(2) } + when((a | b) != UInt(mask & (_a | _b))) { io.error := UInt(3) } + when((a ^ b) != UInt(mask & (_a ^ _b))) { io.error := UInt(4) } +} - class BitwiseOpsTester(w: Int, _a: Int, _b: Int) extends BasicTester { - io.done := Bool(true) - val mask = (1 << w) - 1 - val a = UInt(_a) - val b = UInt(_b) - when(~a != UInt(mask & ~_a)) { io.error := UInt(1) } - when((a & b) != UInt(mask & (_a & _b))) { io.error := UInt(2) } - when((a | b) != UInt(mask & (_a | _b))) { io.error := UInt(3) } - when((a ^ b) != UInt(mask & (_a ^ _b))) { io.error := UInt(4) } - } +class BitwiseOpsSpec extends ChiselPropSpec { property("All bit-wise ops should return the correct result") { forAll(safeUIntPair) { case(w: Int, a: Int, b: Int) => diff --git a/src/test/scala/chiselTests/BundleWire.scala b/src/test/scala/chiselTests/BundleWire.scala index 99dc665f..5beed039 100644 --- a/src/test/scala/chiselTests/BundleWire.scala +++ b/src/test/scala/chiselTests/BundleWire.scala @@ -23,15 +23,15 @@ class BundleWire(n: Int) extends Module { } } -class BundleWireSpec extends ChiselPropSpec { +class BundleWireTester(n: Int, x: Int, y: Int) extends BasicTester { + val dut = Module(new BundleWire(n)) + io.done := Bool(true) + dut.io.in.x := UInt(x) + dut.io.in.y := UInt(y) + io.error := dut.io.outs.map(o => o.x != UInt(x) || o.y != UInt(y)).foldLeft(UInt(0))(_##_) +} - class BundleWireTester(n: Int, x: Int, y: Int) extends BasicTester { - val dut = Module(new BundleWire(n)) - io.done := Bool(true) - dut.io.in.x := UInt(x) - dut.io.in.y := UInt(y) - io.error := dut.io.outs.map(o => o.x != UInt(x) || o.y != UInt(y)).foldLeft(UInt(0))(_##_) - } +class BundleWireSpec extends ChiselPropSpec { property("All vec elems should match the inputs") { forAll(vecSizes, safeUInts, safeUInts) { (n: Int, x: Int, y: Int) => diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 1fe77a0e..88aaf06c 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -10,8 +10,8 @@ import Chisel.testers._ /** Common utility functions for Chisel unit tests. */ trait ChiselRunners { - def execute(t: => BasicTester): Boolean = TesterDriver.execute(t) - def elaborate(t: => Module): Circuit = TesterDriver.elaborate(t) + def execute(t: => BasicTester): Boolean = TesterDriver.execute(() => t) + def elaborate(t: => Module): Circuit = Driver.elaborate(() => t) } /** Spec base class for BDD-style testers. */ @@ -19,11 +19,9 @@ class ChiselFlatSpec extends FlatSpec with ChiselRunners with Matchers /** Spec base class for property-based testers. */ class ChiselPropSpec extends PropSpec with ChiselRunners with PropertyChecks { - /** Returns the number of 1s in the binary representation of the input. */ - def popCount(n: Long): Int = n.toBinaryString.count(_ == '1') // Generator for small positive integers. - val smallPosInts = Gen.choose(1, 7) + val smallPosInts = Gen.choose(1, 4) // Generator for widths considered "safe". val safeUIntWidth = Gen.choose(1, 30) diff --git a/src/test/scala/chiselTests/ComplexAssign.scala b/src/test/scala/chiselTests/ComplexAssign.scala index 09743e04..64fc8bda 100644 --- a/src/test/scala/chiselTests/ComplexAssign.scala +++ b/src/test/scala/chiselTests/ComplexAssign.scala @@ -29,23 +29,23 @@ class ComplexAssign(w: Int) extends Module { } } +class ComplexAssignTester(enList: List[Boolean], re: Int, im: Int) extends BasicTester { + val (cnt, wrap) = Counter(Bool(true), enList.size) + val dut = Module(new ComplexAssign(32)) + dut.io.in.re := UInt(re) + dut.io.in.im := UInt(im) + dut.io.e := Vec(enList.map(Bool(_)))(cnt) + val re_correct = dut.io.out.re === Mux(dut.io.e, dut.io.in.re, UInt(0)) + val im_correct = dut.io.out.im === Mux(dut.io.e, dut.io.in.im, UInt(0)) + when(!re_correct || !im_correct) { + io.done := Bool(true); io.error := cnt + } .elsewhen(wrap) { io.done := Bool(true) } +} + class ComplexAssignSpec extends ChiselPropSpec { - class ComplexAssignTester(enList: List[Boolean], re: Int, im: Int) extends BasicTester { - val (cnt, wrap) = Counter(Bool(true), enList.size) - val dut = Module(new ComplexAssign(32)) - dut.io.in.re := UInt(re) - dut.io.in.im := UInt(im) - dut.io.e := Vec(enList.map(Bool(_)))(cnt) - val re_correct = dut.io.out.re === Mux(dut.io.e, dut.io.in.re, UInt(0)) - val im_correct = dut.io.out.im === Mux(dut.io.e, dut.io.in.im, UInt(0)) - when(!re_correct || !im_correct) { - io.done := Bool(true); io.error := cnt - } .elsewhen(wrap) { io.done := Bool(true) } - } - property("All complex assignments should return the correct result") { - forAll(enSequence(4), safeUInts, safeUInts) { (en: List[Boolean], re: Int, im: Int) => + forAll(enSequence(2), safeUInts, safeUInts) { (en: List[Boolean], re: Int, im: Int) => assert(execute{ new ComplexAssignTester(en, re, im) }) } } diff --git a/src/test/scala/chiselTests/Counter.scala b/src/test/scala/chiselTests/Counter.scala index 0e8601b3..080130df 100644 --- a/src/test/scala/chiselTests/Counter.scala +++ b/src/test/scala/chiselTests/Counter.scala @@ -6,37 +6,37 @@ import org.scalatest._ import org.scalatest.prop._ import Chisel.testers.BasicTester -class CounterSpec extends ChiselPropSpec { +class CountTester(max: Int) extends BasicTester { + val cnt = Counter(max) + when(cnt.value === UInt(max)) { io.done := Bool(true) } +} - class CountTester(max: Int) extends BasicTester { - val cnt = Counter(max) - when(cnt.value === UInt(max)) { io.done := Bool(true) } +class EnableTester(seed: Int) extends BasicTester { + val ens = Reg(init = UInt(seed)) + ens := ens >> 1 + val (cntEn, cntWrap) = Counter(ens(0), 32) + val cnt = Counter(32) + when(cnt.value === UInt(31)) { + io.done := Bool(true) + io.error := cnt.value != UInt(popCount(seed)) } +} + +class WrapTester(max: Int) extends BasicTester { + val (cnt, wrap) = Counter(Bool(true), max) + when(wrap) { io.done := Bool(true); io.error := cnt != UInt(max) } +} + +class CounterSpec extends ChiselPropSpec { property("Counter should count up") { forAll(smallPosInts) { (max: Int) => assert(execute{ new CountTester(max) }) } } - class EnableTester(seed: Int) extends BasicTester { - val ens = Reg(init = UInt(seed)) - ens := ens >> 1 - val (cntEn, cntWrap) = Counter(ens(0), 32) - val cnt = Counter(32) - when(cnt.value === UInt(31)) { - io.done := Bool(true) - io.error := cnt.value != UInt(popCount(seed)) - } - } - property("Counter can be en/disabled") { forAll(safeUInts) { (seed: Int) => assert(execute{ new EnableTester(seed) }) } } - class WrapTester(max: Int) extends BasicTester { - val (cnt, wrap) = Counter(Bool(true), max) - when(wrap) { io.done := Bool(true); io.error := cnt != UInt(max) } - } - property("Counter should wrap") { forAll(smallPosInts) { (max: Int) => assert(execute{ new WrapTester(max) }) } } diff --git a/src/test/scala/chiselTests/Decoder.scala b/src/test/scala/chiselTests/Decoder.scala index 9ac0a3b7..7751804b 100644 --- a/src/test/scala/chiselTests/Decoder.scala +++ b/src/test/scala/chiselTests/Decoder.scala @@ -15,16 +15,16 @@ class Decoder(bitpats: List[String]) extends Module { io.matched := Vec(bitpats.map(BitPat(_) === io.inst)).reduce(_||_) } -class DecoderSpec extends ChiselPropSpec { +class DecoderTester(pairs: List[(String, String)]) extends BasicTester { + val (insts, bitpats) = pairs.unzip + val (cnt, wrap) = Counter(Bool(true), pairs.size) + val dut = Module(new Decoder(bitpats)) + dut.io.inst := Vec(insts.map(UInt(_)))(cnt) + when(!dut.io.matched) { io.done := Bool(true); io.error := cnt } + when(wrap) { io.done := Bool(true) } +} - class DecoderTester(pairs: List[(String, String)]) extends BasicTester { - val (insts, bitpats) = pairs.unzip - val (cnt, wrap) = Counter(Bool(true), pairs.size) - val dut = Module(new Decoder(bitpats)) - dut.io.inst := Vec(insts.map(UInt(_)))(cnt) - when(!dut.io.matched) { io.done := Bool(true); io.error := cnt } - when(wrap) { io.done := Bool(true) } - } +class DecoderSpec extends ChiselPropSpec { // Use a single Int to make both a specific instruction and a BitPat that will match it val bitpatPair = for(seed <- Arbitrary.arbitrary[Int]) yield { @@ -36,7 +36,7 @@ class DecoderSpec extends ChiselPropSpec { private def nPairs(n: Int) = Gen.containerOfN[List, (String,String)](n,bitpatPair) property("BitPat wildcards should be usable in decoding") { - forAll(nPairs(16)){ (pairs: List[(String, String)]) => + forAll(nPairs(4)){ (pairs: List[(String, String)]) => assert(execute{ new DecoderTester(pairs) }) } } diff --git a/src/test/scala/chiselTests/GCD.scala b/src/test/scala/chiselTests/GCD.scala index 48a96f0d..acc1e84e 100644 --- a/src/test/scala/chiselTests/GCD.scala +++ b/src/test/scala/chiselTests/GCD.scala @@ -24,21 +24,21 @@ class GCD extends Module { io.v := y === UInt(0) } -class GCDSpec extends ChiselPropSpec { - - class GCDTester(a: Int, b: Int, z: Int) extends BasicTester { - val dut = Module(new GCD) - val first = Reg(init=Bool(true)) - dut.io.a := UInt(a) - dut.io.b := UInt(b) - dut.io.e := first - when(first) { first := Bool(false) } - when(dut.io.v) { - io.done := Bool(true) - io.error := (dut.io.z != UInt(z)).toUInt - } +class GCDTester(a: Int, b: Int, z: Int) extends BasicTester { + val dut = Module(new GCD) + val first = Reg(init=Bool(true)) + dut.io.a := UInt(a) + dut.io.b := UInt(b) + dut.io.e := first + when(first) { first := Bool(false) } + when(dut.io.v) { + io.done := Bool(true) + io.error := (dut.io.z != UInt(z)).toUInt } +} +class GCDSpec extends ChiselPropSpec { + //TODO: use generators and this function to make z's def gcd(a: Int, b: Int): Int = if(b == 0) a else gcd(b, a%b) diff --git a/src/test/scala/chiselTests/Harness.scala b/src/test/scala/chiselTests/Harness.scala new file mode 100644 index 00000000..98ad3b11 --- /dev/null +++ b/src/test/scala/chiselTests/Harness.scala @@ -0,0 +1,75 @@ +package chiselTests +import Chisel.testers.BasicTester +import org.scalatest._ +import org.scalatest.prop._ +import java.io.File + +class HarnessSpec extends ChiselPropSpec + with Chisel.BackendCompilationUtilities { + + def makeTrivialVerilog = makeHarness((prefix: String) => s""" +module ${prefix}; + initial begin + $$display("$prefix!"); + $$finish; + end +endmodule +""", ".v") _ + + def makeFailingVerilog = makeHarness((prefix: String) => s""" +module $prefix; + initial begin + assert (1 == 0) else $$error("My specific, expected error message!"); + $$display("$prefix!"); + $$finish; + end +endmodule +""", ".v") _ + + def makeCppHarness = makeHarness((prefix: String) => s""" +#include "V$prefix.h" +#include "verilated.h" + +vluint64_t main_time = 0; +double sc_time_stamp () { return main_time; } + +int main(int argc, char **argv, char **env) { + Verilated::commandArgs(argc, argv); + V${prefix}* top = new V${prefix}; + while (!Verilated::gotFinish()) { top->eval(); } + delete top; + exit(0); +} +""", ".cpp") _ + + val dir = new File(System.getProperty("java.io.tmpdir")) + + def simpleHarnessBackend(make: File => File): String = { + val target = "test" + val fname = File.createTempFile(target, "") + val path = fname.getParentFile.toString + val prefix = fname.toString.split("/").last + val vDut = make(fname) + val vH = new File(path + "/V" + prefix + ".h") + val cppHarness = makeCppHarness(fname) + verilogToCpp(target, dir, vDut, cppHarness, vH).! + cppToExe(prefix, dir).! + prefix + } + + property("Test making trivial verilog harness and executing") { + val prefix = simpleHarnessBackend(makeTrivialVerilog) + + assert(executeExpectingSuccess(prefix, dir)) + } + + property("Test that assertion failues in Verilog are caught") { + val prefix = simpleHarnessBackend(makeFailingVerilog) + + assert(!executeExpectingSuccess(prefix, dir)) + assert(executeExpectingFailure(prefix, dir)) + assert(executeExpectingFailure(prefix, dir, "My specific, expected error message!")) + assert(!executeExpectingFailure(prefix, dir, "A string that doesn't match any test output")) + } +} + diff --git a/src/test/scala/chiselTests/MulLookup.scala b/src/test/scala/chiselTests/MulLookup.scala index 18dcc431..93917a4e 100644 --- a/src/test/scala/chiselTests/MulLookup.scala +++ b/src/test/scala/chiselTests/MulLookup.scala @@ -22,15 +22,15 @@ class MulLookup(val w: Int) extends Module { io.z := tbl(((io.x << w) | io.y)) } -class MulLookupSpec extends ChiselPropSpec { +class MulLookupTester(w: Int, x: Int, y: Int) extends BasicTester { + val dut = Module(new MulLookup(w)) + dut.io.x := UInt(x) + dut.io.y := UInt(y) + io.done := Bool(true) + io.error := dut.io.z != UInt(x * y) +} - class MulLookupTester(w: Int, x: Int, y: Int) extends BasicTester { - val dut = Module(new MulLookup(w)) - dut.io.x := UInt(x) - dut.io.y := UInt(y) - io.done := Bool(true) - io.error := dut.io.z != UInt(x * y) - } +class MulLookupSpec extends ChiselPropSpec { property("Mul lookup table should return the correct result") { forAll(smallPosInts, smallPosInts) { (x: Int, y: Int) => diff --git a/src/test/scala/chiselTests/Tbl.scala b/src/test/scala/chiselTests/Tbl.scala index 072f993f..40f71c69 100644 --- a/src/test/scala/chiselTests/Tbl.scala +++ b/src/test/scala/chiselTests/Tbl.scala @@ -23,22 +23,22 @@ class Tbl(w: Int, n: Int) extends Module { } } -class TblSpec extends ChiselPropSpec { +class TblTester(w: Int, n: Int, idxs: List[Int], values: List[Int]) extends BasicTester { + val (cnt, wrap) = Counter(Bool(true), idxs.size) + val dut = Module(new Tbl(w, n)) + val vvalues = Vec(values.map(UInt(_))) + val vidxs = Vec(idxs.map(UInt(_))) + val prev_idx = vidxs(cnt - UInt(1)) + val prev_value = vvalues(cnt - UInt(1)) + dut.io.wi := vidxs(cnt) + dut.io.ri := prev_idx + dut.io.we := Bool(true) //TODO enSequence + dut.io.d := vvalues(cnt) + when(cnt > UInt(0) && dut.io.o != prev_value) { io.done := Bool(true); io.error := prev_idx } + when(wrap) { io.done := Bool(true) } +} - class TblTester(w: Int, n: Int, idxs: List[Int], values: List[Int]) extends BasicTester { - val (cnt, wrap) = Counter(Bool(true), idxs.size) - val dut = Module(new Tbl(w, n)) - val vvalues = Vec(values.map(UInt(_))) - val vidxs = Vec(idxs.map(UInt(_))) - val prev_idx = vidxs(cnt - UInt(1)) - val prev_value = vvalues(cnt - UInt(1)) - dut.io.wi := vidxs(cnt) - dut.io.ri := prev_idx - dut.io.we := Bool(true) //TODO enSequence - dut.io.d := vvalues(cnt) - when(cnt > UInt(0) && dut.io.o != prev_value) { io.done := Bool(true); io.error := prev_idx } - when(wrap) { io.done := Bool(true) } - } +class TblSpec extends ChiselPropSpec { property("All table reads should return the previous write") { forAll(safeUIntPairN(8)) { case(w: Int, pairs: List[(Int, Int)]) => diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index ea670f37..4430ab66 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -7,15 +7,36 @@ import org.scalatest._ import org.scalatest.prop._ import Chisel.testers.BasicTester -class VecSpec extends ChiselPropSpec { +class ValueTester(w: Int, values: List[Int]) extends BasicTester { + io.done := Bool(true) + val v = Vec(values.map(UInt(_, width = w))) // TODO: does this need a Wire? Why no error? + io.error := v.zip(values).map { case(a,b) => + a != UInt(b) + }.foldLeft(UInt(0))(_##_) +} - class ValueTester(w: Int, values: List[Int]) extends BasicTester { - io.done := Bool(true) - val v = Vec(values.map(UInt(_, width = w))) // TODO: does this need a Wire? Why no error? - io.error := v.zip(values).map { case(a,b) => - a != UInt(b) - }.foldLeft(UInt(0))(_##_) - } +class TabulateTester(n: Int) extends BasicTester { + io.done := Bool(true) + val v = Vec(Range(0, n).map(i => UInt(i * 2))) + val x = Vec(Array.tabulate(n){ i => UInt(i * 2) }) + val u = Vec.tabulate(n)(i => UInt(i*2)) + when(v.toBits != x.toBits) { io.error := UInt(1) } + when(v.toBits != u.toBits) { io.error := UInt(2) } + when(x.toBits != u.toBits) { io.error := UInt(3) } +} + +class ShiftRegisterTester(n: Int) extends BasicTester { + val (cnt, wrap) = Counter(Bool(true), n*2) + when(wrap) { io.done := Bool(true) } + + val shifter = Vec(Reg(UInt(width = log2Up(n))), n) + (shifter, shifter drop 1).zipped.foreach(_ := _) + shifter(n-1) := cnt + val expected = cnt - UInt(n) + when(cnt >= UInt(n) && expected != shifter(0)) { io.done := Bool(true); io.error := expected } +} + +class VecSpec extends ChiselPropSpec { property("Vecs should be assignable") { forAll(safeUIntN(8)) { case(w: Int, v: List[Int]) => @@ -23,31 +44,10 @@ class VecSpec extends ChiselPropSpec { } } - class TabulateTester(n: Int) extends BasicTester { - io.done := Bool(true) - val v = Vec(Range(0, n).map(i => UInt(i * 2))) - val x = Vec(Array.tabulate(n){ i => UInt(i * 2) }) - val u = Vec.tabulate(n)(i => UInt(i*2)) - when(v.toBits != x.toBits) { io.error := UInt(1) } - when(v.toBits != u.toBits) { io.error := UInt(2) } - when(x.toBits != u.toBits) { io.error := UInt(3) } - } - property("Vecs should tabulate correctly") { forAll(smallPosInts) { (n: Int) => assert(execute{ new TabulateTester(n) }) } } - class ShiftRegisterTester(n: Int) extends BasicTester { - val (cnt, wrap) = Counter(Bool(true), n*2) - when(wrap) { io.done := Bool(true) } - - val shifter = Vec(Reg(UInt(width = log2Up(n))), n) - (shifter, shifter drop 1).zipped.foreach(_ := _) - shifter(n-1) := cnt - val expected = cnt - UInt(n) - when(cnt >= UInt(n) && expected != shifter(0)) { io.done := Bool(true); io.error := expected } - } - property("Vecs of regs should be usable as shift registers") { forAll(smallPosInts) { (n: Int) => assert(execute{ new ShiftRegisterTester(n) }) } } |
