diff options
Diffstat (limited to 'src')
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) }) } } |
