aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Izraelevitz2016-05-02 14:59:51 -0700
committerjackkoenig2016-05-12 22:42:06 -0700
commitf07baed2bc46e107250c317f290af48747a98322 (patch)
treea63c5d2477eabccab85dc6780f289fc24cbcad5c
parent0ee659e85c7fe46c2678a49866ef1eca8f4a2c65 (diff)
Restructured Compiler to use Transforms. Added an InlineInstance pass.
Transforms are new unit of modularity within the compiler.
-rw-r--r--src/main/scala/firrtl/Compiler.scala129
-rw-r--r--src/main/scala/firrtl/Driver.scala113
-rw-r--r--src/main/scala/firrtl/LoweringAnnotations.scala88
-rw-r--r--src/main/scala/firrtl/LoweringCompilers.scala202
-rw-r--r--src/main/scala/firrtl/passes/Inline.scala171
-rw-r--r--src/main/scala/firrtl/passes/Passes.scala14
-rw-r--r--src/test/resources/features/ChirrtlMems.fir (renamed from src/test/resources/features/CHIRRTLMems.fir)0
-rw-r--r--src/test/resources/features/Printf.fir4
-rw-r--r--src/test/scala/firrtlTests/AnnotationTests.scala57
-rw-r--r--src/test/scala/firrtlTests/ChirrtlSpec.scala2
-rw-r--r--src/test/scala/firrtlTests/CompilerTests.scala120
-rw-r--r--src/test/scala/firrtlTests/FirrtlSpec.scala23
-rw-r--r--src/test/scala/firrtlTests/InlineInstancesTests.scala272
-rw-r--r--src/test/scala/firrtlTests/PassTests.scala82
14 files changed, 1147 insertions, 130 deletions
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala
index 08ff421f..8efd010c 100644
--- a/src/main/scala/firrtl/Compiler.scala
+++ b/src/main/scala/firrtl/Compiler.scala
@@ -33,73 +33,74 @@ import java.io.Writer
import Utils._
import firrtl.passes._
-trait Compiler extends LazyLogging {
- def run(c: Circuit, w: Writer)
+
+// ===========================================
+// Annotations
+// -------------------------------------------
+case class AnnotationException(message: String) extends Exception(message)
+trait Named { def name: String }
+case class ModuleName(name: String) extends Named
+case class ComponentName(name: String, module: ModuleName) extends Named
+
+// - Associated with an arbitrary serializable annotation
+trait Annotation {
+ def serialize: String
}
-object FIRRTLCompiler extends Compiler {
- val passes = Seq(
- CInferTypes,
- CInferMDir,
- RemoveCHIRRTL,
- ToWorkingIR,
- CheckHighForm
- )
- def run(c: Circuit, w: Writer) = {
- val highForm = PassUtils.executePasses(c, passes)
- FIRRTLEmitter.run(highForm, w)
- }
+// - Used to identify which annotation is consumed by which pass
+trait CircuitAnnotationKind
+case object UnknownCAKind extends CircuitAnnotationKind
+
+// - A collection of annotations on a given circuit
+// - Call update to keep annotations synced with circuit after
+// a transformation modifies module or component names
+trait CircuitAnnotation {
+ def kind: CircuitAnnotationKind
+ def update (renames: RenameMap): CircuitAnnotation
}
-object VerilogCompiler extends Compiler {
- // Copied from Stanza implementation
- val passes = Seq(
- //CheckHighForm,
- //FromCHIRRTL,
- CInferTypes,
- CInferMDir,
- RemoveCHIRRTL,
- ToWorkingIR,
- CheckHighForm,
- ResolveKinds,
- InferTypes,
- CheckTypes,
- Uniquify,
- ResolveKinds,
- InferTypes,
- ResolveGenders,
- CheckGenders,
- InferWidths,
- CheckWidths,
- PullMuxes,
- ExpandConnects,
- RemoveAccesses,
- ExpandWhens,
- CheckInitialization,
- ResolveKinds,
- InferTypes,
- ResolveGenders,
- InferWidths,
- Legalize,
- LowerTypes,
- ResolveKinds,
- InferTypes,
- ResolveGenders,
- InferWidths,
- RemoveValidIf,
- ConstProp,
- PadWidths,
- VerilogWrap,
- SplitExpressions,
- CommonSubexpressionElimination,
- DeadCodeElimination,
- VerilogRename
- )
- def run(c: Circuit, w: Writer)
- {
- val loweredIR = PassUtils.executePasses(c, passes)
- val verilogEmitter = new VerilogEmitter
- verilogEmitter.run(loweredIR, w)
- }
+// - A class that contains a map from old name to modified names
+// - Generated by transformations that modify names
+trait RenameMap { def map: Map[Named, Seq[Named]] }
+case class BasicRenameMap(map: Map[Named,Seq[Named]]) extends RenameMap
+
+
+// ===========================================
+// Transforms
+// -------------------------------------------
+
+case class TransformResult (
+ circuit: Circuit,
+ renames: Option[RenameMap] = None,
+ annotation: Option[CircuitAnnotation] = None)
+// - Transforms a circuit
+// - Can consume multiple CircuitAnnotation's
+trait Transform {
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult
}
+
+// ===========================================
+// Compilers
+// -------------------------------------------
+
+case class CompilerResult (circuit: Circuit, annotations: Seq[CircuitAnnotation])
+
+// - A sequence of transformations
+// - Call compile to executes each transformation in sequence onto
+// a given circuit.
+trait Compiler extends LazyLogging {
+ def transforms(w: Writer): Seq[Transform]
+ def compile(circuit: Circuit, annotations: Seq[CircuitAnnotation], writer: Writer): CompilerResult = {
+ transforms(writer).foldLeft(CompilerResult(circuit,annotations))((in: CompilerResult, xform: Transform) => {
+ val result = xform.execute(in.circuit,in.annotations)
+ val in_remapped = result.renames match {
+ case Some(renames) => in.annotations.map(_.update(renames))
+ case None => in.annotations
+ }
+ val full_annotations = in_remapped ++ result.annotation
+ CompilerResult(result.circuit,full_annotations)
+ })
+ }
+}
+
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala
index 587be24c..684bc569 100644
--- a/src/main/scala/firrtl/Driver.scala
+++ b/src/main/scala/firrtl/Driver.scala
@@ -24,87 +24,112 @@ ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
*/
-package firrtl
-import java.io._
-import java.nio.file.{Paths, Files}
+package firrtl
+import java.io.{PrintWriter, Writer, File}
import scala.io.Source
-import scala.sys.process._
-
-import com.typesafe.scalalogging.LazyLogging
+import scala.collection.mutable
import Utils._
import Parser.{InfoMode, IgnoreInfo, UseInfo, GenInfo, AppendInfo}
-object Driver extends LazyLogging {
+object Driver {
private val usage = """
Usage: sbt "run-main firrtl.Driver -i <input_file> -o <output_file> -X <compiler>"
firrtl -i <input_file> -o <output_file> -X <compiler> [options]
Options:
- -X <compiler> Specify the target language
- Currently supported: verilog firrtl
- --info-mode=<mode> Specify Info Mode
+ -X <compiler> Specify the target compiler
+ Currently supported: high low verilog
+ --info-mode <mode> Specify Info Mode
Supported modes: ignore, use, gen, append
"""
- private val defaultOptions = Map[Symbol, Any]().withDefaultValue(false)
- def compile(input: String, output: String, compiler: Compiler, infoMode: InfoMode = IgnoreInfo)
- {
+ // Compiles circuit. First parses a circuit from an input file,
+ // executes all compiler passes, and writes result to an output
+ // file.
+ def compile(
+ input: String,
+ output: String,
+ compiler: Compiler,
+ infoMode: InfoMode = IgnoreInfo,
+ annotations: Seq[CircuitAnnotation] = Seq.empty) = {
val parsedInput = Parser.parse(Source.fromFile(input).getLines, infoMode)
val writerOutput = new PrintWriter(new File(output))
- compiler.run(parsedInput, writerOutput)
+ compiler.compile(parsedInput, annotations, writerOutput)
writerOutput.close
}
- def main(args: Array[String])
- {
+ // Arguments specify the compiler, input file, and output file
+ def main(args: Array[String]) = {
val arglist = args.toList
- type OptionMap = Map[Symbol, Any]
+ sealed trait CompilerOption
+ case object InputFileName extends CompilerOption
+ case object OutputFileName extends CompilerOption
+ case object CompilerName extends CompilerOption
+ case object InfoModeOption extends CompilerOption
+ val defaultOptions = Map[CompilerOption, String]()
+
+ // Inline Annotation datastructure/function
+ val inlineAnnotations = mutable.HashMap[Named,Annotation]()
+ def handleInlineOption(value: String): Unit =
+ value.split('.') match {
+ case Array(module) =>
+ inlineAnnotations(ModuleName(module)) = TagAnnotation
+ case Array(module, inst) =>
+ inlineAnnotations(ComponentName(inst,ModuleName(module))) = TagAnnotation
+ case _ => throw new Exception(s"Bad inline instance/module name: $value")
+ }
+
+ type OptionMap = Map[CompilerOption, String]
def nextOption(map: OptionMap, list: List[String]): OptionMap = {
list match {
case Nil => map
+ case "--inline" :: value :: tail =>
+ handleInlineOption(value)
+ nextOption(map, tail)
case "-X" :: value :: tail =>
- nextOption(map ++ Map('compiler -> value), tail)
+ nextOption(map + (CompilerName -> value), tail)
case "-i" :: value :: tail =>
- nextOption(map ++ Map('input -> value), tail)
+ nextOption(map + (InputFileName -> value), tail)
case "-o" :: value :: tail =>
- nextOption(map ++ Map('output -> value), tail)
+ nextOption(map + (OutputFileName -> value), tail)
case "--info-mode" :: value :: tail =>
- nextOption(map ++ Map('infoMode -> value), tail)
- case ("-h" | "--help") :: tail =>
- nextOption(map ++ Map('help -> true), tail)
+ nextOption(map + (InfoModeOption -> value), tail)
+ case ("-h" | "--help") :: tail => { println(usage); sys.exit(0) }
case option :: tail =>
throw new Exception("Unknown option " + option)
}
}
+
val options = nextOption(defaultOptions, arglist)
- if (options('help) == true) {
- println(usage)
- System.exit(0)
- }
+ // Get input circuit/output filenames
+ val input = options.getOrElse(InputFileName, throw new Exception("No input file provided!" + usage))
+ val output = options.getOrElse(OutputFileName, throw new Exception("No output file provided!" + usage))
- val input = options('input) match {
- case s: String => s
- case false => throw new Exception("No input file provided!" + usage)
- }
- val output = options('output) match {
- case s: String => s
- case false => throw new Exception("No output file provided!" + usage)
- }
- val infoMode = options('infoMode) match {
- case ("use" | false) => UseInfo
- case "ignore" => IgnoreInfo
- case "gen" => GenInfo(input)
- case "append" => AppendInfo(input)
+ val infoMode = options.get(InfoModeOption) match {
+ case (Some("use") | None) => UseInfo
+ case Some("ignore") => IgnoreInfo
+ case Some("gen") => GenInfo(input)
+ case Some("append") => AppendInfo(input)
+ case Some(other) => throw new Exception("Unknown info mode option: " + other)
}
- options('compiler) match {
- case "verilog" => compile(input, output, VerilogCompiler, infoMode)
- case "firrtl" => compile(input, output, FIRRTLCompiler, infoMode)
- case other => throw new Exception("Invalid compiler! " + other)
+ // Construct all Circuit Annotations
+ val inlineCA =
+ if (inlineAnnotations.isEmpty) Seq.empty
+ else Seq(StickyCircuitAnnotation(passes.InlineCAKind, inlineAnnotations.toMap))
+ val allAnnotations = inlineCA // other annotations will be added here
+
+ // Execute selected compiler - error if not recognized compiler
+ options.get(CompilerName) match {
+ case Some("high") => compile(input, output, new HighFirrtlCompiler(), infoMode, allAnnotations)
+ case Some("low") => compile(input, output, new LowFirrtlCompiler(), infoMode, allAnnotations)
+ case Some("verilog") => compile(input, output, new VerilogCompiler(), infoMode, allAnnotations)
+ case Some(other) => throw new Exception("Unknown compiler option: " + other)
+ case None => throw new Exception("No specified compiler option.")
}
}
}
diff --git a/src/main/scala/firrtl/LoweringAnnotations.scala b/src/main/scala/firrtl/LoweringAnnotations.scala
new file mode 100644
index 00000000..ac09f67a
--- /dev/null
+++ b/src/main/scala/firrtl/LoweringAnnotations.scala
@@ -0,0 +1,88 @@
+/*
+Copyright (c) 2014 - 2016 The Regents of the University of
+California (Regents). All Rights Reserved. Redistribution and use in
+source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the name of the Regents nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+*/
+
+package firrtl
+
+// ===========================================
+// Lowering Annotations
+// -------------------------------------------
+
+case object TagAnnotation extends Annotation {
+ def serialize: String = this.toString
+}
+case class StringAnnotation (string: String) extends Annotation {
+ def serialize: String = string
+}
+
+// Annotated names cannot be renamed, split, or removed
+case class BrittleCircuitAnnotation(kind: CircuitAnnotationKind, map: Map[Named,Annotation]) extends CircuitAnnotation {
+ def check(renames: RenameMap): Unit = {
+ map.keys.foreach { mn => {
+ renames.map.get(mn) match {
+ case None => {}
+ case Some(rename) => rename.size match {
+ case 1 if (rename.head.name == mn.name) => {} // Ok case
+ case 0 => throw new AnnotationException(s"Cannot remove $mn.")
+ case 1 => throw new AnnotationException(s"Cannot rename $mn into ${rename.head.name}.")
+ case _ =>
+ throw new AnnotationException(s"Cannot split ${mn.name}into " + rename.map(_.name).reduce(_ + " and " + _) + ".")
+ }
+ }
+ }}
+ }
+ def update(renames: RenameMap): BrittleCircuitAnnotation = {
+ check(renames)
+ BrittleCircuitAnnotation(kind, map)
+ }
+}
+
+// Annotation moves with renamed and split names. Removed annotated names do not trigger an error.
+case class StickyCircuitAnnotation(kind: CircuitAnnotationKind, map: Map[Named,Annotation]) extends CircuitAnnotation {
+ def get(name: Named): Annotation = map(name)
+ def update(renames: RenameMap): StickyCircuitAnnotation = {
+ val mapx = renames.map.foldLeft(Map[Named, Annotation]()){
+ case (newmap: Map[Named, Annotation],(name: Named, renames: Seq[Named])) => {
+ if (map.contains(name)) {
+ renames.map(rename => Map(rename -> map(name))).reduce(_ ++ _) ++ newmap
+ } else newmap
+ }
+ }
+ StickyCircuitAnnotation(kind, mapx)
+ }
+ def getModuleNames: Seq[ModuleName] = map.keys.flatMap{
+ _ match {
+ case x: ModuleName => Seq(x)
+ case _ => Seq.empty
+ }
+ }.toSeq
+ def getComponentNames: Seq[ComponentName] = map.keys.flatMap{
+ _ match {
+ case x: ComponentName => Seq(x)
+ case _ => Seq.empty
+ }
+ }.toSeq
+}
diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala
new file mode 100644
index 00000000..3db83406
--- /dev/null
+++ b/src/main/scala/firrtl/LoweringCompilers.scala
@@ -0,0 +1,202 @@
+/*
+Copyright (c) 2014 - 2016 The Regents of the University of
+California (Regents). All Rights Reserved. Redistribution and use in
+source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ two paragraphs of disclaimer in the documentation and/or other materials
+ provided with the distribution.
+ * Neither the name of the Regents nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF
+ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION
+TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+*/
+
+package firrtl
+
+import com.typesafe.scalalogging.LazyLogging
+import java.io.Writer
+import firrtl.passes.Pass
+
+// ===========================================
+// Utility Traits
+// -------------------------------------------
+// Valid if all passes in transformation:
+// 1) Don't produce annotations
+// 2) Don't consume annotations
+// 3) No component or module names are renamed
+trait SimpleRun extends LazyLogging {
+ def run (circuit: Circuit, passes: Seq[Pass]): TransformResult = {
+ val result = passes.foldLeft(circuit) {
+ (c: Circuit, pass: Pass) => {
+ val name = pass.name
+ val x = Utils.time(name) { pass.run(c) }
+ logger.debug(x.serialize)
+ x
+ }
+ }
+ TransformResult(result)
+ }
+}
+
+// ===========================================
+// Lowering Transforms
+// -------------------------------------------
+// This transforms "CHIRRTL", the chisel3 IR, to "Firrtl". Note the resulting
+// circuit has only IR nodes, not WIR.
+// TODO(izraelevitz): Create RenameMap from RemoveCHIRRTL
+class Chisel3ToHighFirrtl () extends Transform with SimpleRun {
+ val passSeq = Seq(
+ passes.CInferTypes,
+ passes.CInferMDir,
+ passes.RemoveCHIRRTL)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult =
+ run(circuit, passSeq)
+}
+
+// Converts from the bare intermediate representation (IR.scala)
+// to a working representation (WIR.scala)
+class IRToWorkingIR () extends Transform with SimpleRun {
+ val passSeq = Seq(passes.ToWorkingIR)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult =
+ run(circuit, passSeq)
+}
+
+// Resolves types, kinds, and genders, and checks the circuit legality.
+// Operates on working IR nodes and high Firrtl.
+class ResolveAndCheck () extends Transform with SimpleRun {
+ val passSeq = Seq(
+ passes.CheckHighForm,
+ passes.ResolveKinds,
+ passes.InferTypes,
+ passes.CheckTypes,
+ passes.Uniquify,
+ passes.ResolveKinds,
+ passes.InferTypes,
+ passes.ResolveGenders,
+ passes.CheckGenders,
+ passes.InferWidths,
+ passes.CheckWidths)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult =
+ run(circuit, passSeq)
+}
+
+// Expands aggregate connects, removes dynamic accesses, and when
+// statements. Checks for uninitialized values. Must accept a
+// well-formed graph.
+// Operates on working IR nodes.
+class HighFirrtlToMiddleFirrtl () extends Transform with SimpleRun {
+ val passSeq = Seq(
+ passes.PullMuxes,
+ passes.ExpandConnects,
+ passes.RemoveAccesses,
+ passes.ExpandWhens,
+ passes.CheckInitialization,
+ passes.ResolveKinds,
+ passes.InferTypes,
+ passes.ResolveGenders,
+ passes.InferWidths)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult =
+ run(circuit, passSeq)
+}
+
+// Expands all aggregate types into many ground-typed components. Must
+// accept a well-formed graph of only middle Firrtl features.
+// Operates on working IR nodes.
+// TODO(izraelevitz): Create RenameMap from RemoveCHIRRTL
+class MiddleFirrtlToLowFirrtl () extends Transform with SimpleRun {
+ val passSeq = Seq(
+ passes.Legalize,
+ passes.LowerTypes,
+ passes.ResolveKinds,
+ passes.InferTypes,
+ passes.ResolveGenders,
+ passes.InferWidths)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult =
+ run(circuit, passSeq)
+}
+
+// Emits Verilog.
+// First optimizes for verilog width semantics with custom Primops,
+// then splits complex expressions into temporary nodes. Finally,
+// renames names that conflict with Verilog keywords.
+// Operates on working IR nodes.
+// TODO(izraelevitz): Create RenameMap from VerilogRename
+class EmitVerilogFromLowFirrtl (val writer: Writer) extends Transform with SimpleRun {
+ val passSeq = Seq(
+ passes.RemoveValidIf,
+ passes.ConstProp,
+ passes.PadWidths,
+ passes.VerilogWrap,
+ passes.SplitExpressions,
+ passes.CommonSubexpressionElimination,
+ passes.DeadCodeElimination,
+ passes.VerilogRename)
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult = {
+ val result = run(circuit, passSeq)
+ (new VerilogEmitter).run(result.circuit, writer)
+ result
+ }
+}
+
+// Emits Firrtl.
+// Operates on WIR/IR nodes.
+class EmitFirrtl (val writer: Writer) extends Transform {
+ def execute (circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult = {
+ FIRRTLEmitter.run(circuit, writer)
+ TransformResult(circuit)
+ }
+}
+
+
+// ===========================================
+// Lowering Compilers
+// -------------------------------------------
+// Emits input circuit
+// Will replace Chirrtl constructs with Firrtl
+class HighFirrtlCompiler extends Compiler {
+ def transforms(writer: Writer): Seq[Transform] = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ new EmitFirrtl(writer)
+ )
+}
+
+// Emits lowered input circuit
+class LowFirrtlCompiler extends Compiler {
+ def transforms(writer: Writer): Seq[Transform] = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ passes.InlineInstances,
+ new ResolveAndCheck(),
+ new HighFirrtlToMiddleFirrtl(),
+ new MiddleFirrtlToLowFirrtl(),
+ new EmitFirrtl(writer)
+ )
+}
+
+// Emits Verilog
+class VerilogCompiler extends Compiler {
+ def transforms(writer: Writer): Seq[Transform] = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ new ResolveAndCheck(),
+ new HighFirrtlToMiddleFirrtl(),
+ new MiddleFirrtlToLowFirrtl(),
+ passes.InlineInstances,
+ new EmitVerilogFromLowFirrtl(writer)
+ )
+}
diff --git a/src/main/scala/firrtl/passes/Inline.scala b/src/main/scala/firrtl/passes/Inline.scala
new file mode 100644
index 00000000..5e523a37
--- /dev/null
+++ b/src/main/scala/firrtl/passes/Inline.scala
@@ -0,0 +1,171 @@
+package firrtl
+package passes
+
+// Datastructures
+import scala.collection.mutable
+
+import firrtl.Mappers.{ExpMap,StmtMap}
+import firrtl.Utils.WithAs
+
+
+// Tags an annotation to be consumed by this pass
+case object InlineCAKind extends CircuitAnnotationKind
+
+// Only use on legal Firrtl. Specifically, the restriction of
+// instance loops must have been checked, or else this pass can
+// infinitely recurse
+object InlineInstances extends Transform {
+ val inlineDelim = "$"
+ def name = "Inline Instances"
+ def execute(circuit: Circuit, annotations: Seq[CircuitAnnotation]): TransformResult = {
+ annotations.count(_.kind == InlineCAKind) match {
+ case 0 => TransformResult(circuit, None, None)
+ case 1 => {
+ // This could potentially be cleaned up, but the best solution is unclear at the moment.
+ val myAnnotation = annotations.find(_.kind == InlineCAKind).get match {
+ case x: StickyCircuitAnnotation => x
+ case _ => throw new PassException("Circuit annotation must be StickyCircuitAnnotation")
+ }
+ check(circuit, myAnnotation)
+ run(circuit, myAnnotation.getModuleNames, myAnnotation.getComponentNames)
+ }
+ // Default behavior is to error if more than one annotation for inlining
+ // This could potentially change
+ case _ => throw new PassException("Found more than one circuit annotation of InlineCAKind!")
+ }
+ }
+
+ // Checks the following properties:
+ // 1) All annotated modules exist
+ // 2) All annotated modules are InModules (can be inlined)
+ // 3) All annotated instances exist, and their modules can be inline
+ def check(c: Circuit, ca: StickyCircuitAnnotation): Unit = {
+ val errors = mutable.ArrayBuffer[PassException]()
+ val moduleMap = (for(m <- c.modules) yield m.name -> m).toMap
+ val annModuleNames = ca.getModuleNames.map(_.name) ++ ca.getComponentNames.map(_.module.name)
+ def checkExists(name: String): Unit =
+ if (!moduleMap.contains(name))
+ errors += new PassException(s"Annotated module does not exist: ${name}")
+ def checkExternal(name: String): Unit = moduleMap(name) match {
+ case m: ExModule => errors += new PassException(s"Annotated module cannot be an external module: ${name}")
+ case _ => {}
+ }
+ def checkInstance(cn: ComponentName): Unit = {
+ var containsCN = false
+ def onStmt(name: String)(s: Stmt): Stmt = {
+ s match {
+ case WDefInstance(_, inst_name, module_name, tpe) =>
+ if (name == inst_name) {
+ containsCN = true
+ checkExternal(module_name)
+ }
+ case _ => {}
+ }
+ s map onStmt(name)
+ }
+ onStmt(cn.name)(moduleMap(cn.module.name).asInstanceOf[InModule].body)
+ if (!containsCN) errors += new PassException(s"Annotated instance does not exist: ${cn.module.name}.${cn.name}")
+ }
+ annModuleNames.foreach{n => checkExists(n)}
+ if (!errors.isEmpty) throw new PassExceptions(errors)
+ annModuleNames.foreach{n => checkExternal(n)}
+ if (!errors.isEmpty) throw new PassExceptions(errors)
+ ca.getComponentNames.foreach{cn => checkInstance(cn)}
+ if (!errors.isEmpty) throw new PassExceptions(errors)
+ }
+
+ def run(c: Circuit, modsToInline: Seq[ModuleName], instsToInline: Seq[ComponentName]): TransformResult = {
+ // ---- Rename functions/data ----
+ val renameMap = mutable.HashMap[Named,Seq[Named]]()
+ // Updates renameMap with new names
+ def update(name: Named, rename: Named) = {
+ val existing = renameMap.getOrElse(name, Seq[Named]())
+ if (!existing.contains(rename)) renameMap(name) = existing.:+(rename)
+ }
+
+ // ---- Pass functions/data ----
+ // Contains all unaltered modules
+ val originalModules = mutable.HashMap[String,Module]()
+ // Contains modules whose direct/indirect children modules have been inlined, and whose tagged instances have been inlined.
+ val inlinedModules = mutable.HashMap[String,Module]()
+
+ // Recursive.
+ def onModule(m: Module): Module = {
+ val inlinedInstances = mutable.ArrayBuffer[String]()
+ // Recursive. Replaces inst.port with inst$port
+ def onExp(e: Expression): Expression = e match {
+ case WSubField(WRef(ref, _, _, _), field, tpe, gen) => {
+ // Relies on instance declaration before any instance references
+ if (inlinedInstances.contains(ref)) {
+ val newName = ref + inlineDelim + field
+ update(ComponentName(ref, ModuleName(m.name)), ComponentName(newName, ModuleName(m.name)))
+ WRef(newName, tpe, WireKind(), gen)
+ }
+ else e
+ }
+ case e => e map onExp
+ }
+ // Recursive. Inlines tagged instances
+ def onStmt(s: Stmt): Stmt = s match {
+ case WDefInstance(info, instName, moduleName, instTpe) => {
+ def rename(name:String): String = {
+ val newName = instName + inlineDelim + name
+ update(ComponentName(name, ModuleName(moduleName)), ComponentName(newName, ModuleName(m.name)))
+ newName
+ }
+ // Rewrites references in inlined statements from ref to inst$ref
+ def renameStmt(s: Stmt): Stmt = {
+ def renameExp(e: Expression): Expression = {
+ e map renameExp match {
+ case WRef(name, tpe, kind, gen) => WRef(rename(name), tpe, kind, gen)
+ case e => e
+ }
+ }
+ s map rename map renameStmt map renameExp
+ }
+ val shouldInline =
+ modsToInline.contains(ModuleName(moduleName)) ||
+ instsToInline.contains(ComponentName(instName, ModuleName(m.name)))
+ // Used memoized instance if available
+ val instModule =
+ if (inlinedModules.contains(name)) inlinedModules(name)
+ else {
+ // Warning - can infinitely recurse if there is an instance loop
+ onModule(originalModules(moduleName))
+ }
+ if (shouldInline) {
+ inlinedInstances += instName
+ val instInModule = instModule match {
+ case m: ExModule => throw new PassException("Cannot inline external module")
+ case m: InModule => m
+ }
+ val stmts = mutable.ArrayBuffer[Stmt]()
+ for (p <- instInModule.ports) {
+ stmts += DefWire(p.info, rename(p.name), p.tpe)
+ }
+ stmts += renameStmt(instInModule.body)
+ Begin(stmts.toSeq)
+ } else s
+ }
+ case s => s map onExp map onStmt
+ }
+ m match {
+ case InModule(info, name, ports, body) => {
+ val mx = InModule(info, name, ports, onStmt(body))
+ inlinedModules(name) = mx
+ mx
+ }
+ case m: ExModule => {
+ inlinedModules(m.name) = m
+ m
+ }
+ }
+ }
+
+ c.modules.foreach{ m => originalModules(m.name) = m}
+ val top = c.modules.find(m => m.name == c.main).get
+ onModule(top)
+ val modulesx = c.modules.map(m => inlinedModules(m.name))
+ TransformResult(Circuit(c.info, modulesx, c.main), Some(BasicRenameMap(renameMap.toMap)), None)
+ }
+}
diff --git a/src/main/scala/firrtl/passes/Passes.scala b/src/main/scala/firrtl/passes/Passes.scala
index 8ccfb0b0..bc11bc9d 100644
--- a/src/main/scala/firrtl/passes/Passes.scala
+++ b/src/main/scala/firrtl/passes/Passes.scala
@@ -51,20 +51,6 @@ trait Pass extends LazyLogging {
class PassException(message: String) extends Exception(message)
class PassExceptions(exceptions: Seq[PassException]) extends Exception("\n" + exceptions.mkString("\n"))
-object PassUtils extends LazyLogging {
- val listOfPasses: Seq[Pass] = Seq(ToWorkingIR,ResolveKinds,InferTypes,ResolveGenders,InferWidths,PullMuxes,ExpandConnects,RemoveAccesses,ExpandWhens,LowerTypes)
- lazy val mapNameToPass: Map[String, Pass] = listOfPasses.map(p => p.name -> p).toMap
-
- def executePasses(c: Circuit, passes: Seq[Pass]): Circuit = {
- if (passes.isEmpty) {logger.info(s"Done!"); c}
- else {
- val p = passes.head
- val x = time(p.name) { p.run(c) }
- executePasses(x, passes.tail)
- }
- }
-}
-
// These should be distributed into separate files
object ToWorkingIR extends Pass {
private var mname = ""
diff --git a/src/test/resources/features/CHIRRTLMems.fir b/src/test/resources/features/ChirrtlMems.fir
index bd92c872..bd92c872 100644
--- a/src/test/resources/features/CHIRRTLMems.fir
+++ b/src/test/resources/features/ChirrtlMems.fir
diff --git a/src/test/resources/features/Printf.fir b/src/test/resources/features/Printf.fir
index d0c1c775..6e11c64e 100644
--- a/src/test/resources/features/Printf.fir
+++ b/src/test/resources/features/Printf.fir
@@ -1,5 +1,5 @@
-circuit Top :
- module Top :
+circuit Printf :
+ module Printf :
input clk : Clock
input reset : UInt<1>
diff --git a/src/test/scala/firrtlTests/AnnotationTests.scala b/src/test/scala/firrtlTests/AnnotationTests.scala
new file mode 100644
index 00000000..81a74b54
--- /dev/null
+++ b/src/test/scala/firrtlTests/AnnotationTests.scala
@@ -0,0 +1,57 @@
+package firrtlTests
+
+import java.io.StringWriter
+
+import org.scalatest.FlatSpec
+import org.scalatest.Matchers
+import org.scalatest.junit.JUnitRunner
+
+import firrtl.{Parser,Circuit}
+import firrtl.{
+ Named,
+ ModuleName,
+ ComponentName,
+ CircuitAnnotation,
+ StringAnnotation,
+ BrittleCircuitAnnotation,
+ UnknownCAKind,
+ Compiler,
+ CompilerResult,
+ Annotation,
+ RenameMap,
+ VerilogCompiler
+}
+
+/**
+ * An example methodology for testing Firrtl annotations.
+ */
+abstract class AnnotationSpec extends FlatSpec {
+ def parse (s: String): Circuit = Parser.parse(s.split("\n").toIterator)
+ def compiler: Compiler
+ def input: String
+ def getResult (annotation: CircuitAnnotation): CompilerResult = {
+ val writer = new StringWriter()
+ compiler.compile(parse(input), Seq(annotation), writer)
+ }
+}
+
+
+/**
+ * An example test for testing module annotations
+ */
+class BrittleModuleAnnotationSpec extends AnnotationSpec with Matchers {
+ val compiler = new VerilogCompiler()
+ val input =
+"""
+circuit Top :
+ module Top :
+ input a : UInt<1>[2]
+ node x = a
+"""
+ val message = "This is Top"
+ val map: Map[Named, Annotation] = Map(ModuleName("Top") -> StringAnnotation(message))
+ val annotation = BrittleCircuitAnnotation(UnknownCAKind, map)
+ "The annotation" should "get passed through the compiler" in {
+ (getResult(annotation).annotations.head == annotation) should be (true)
+ }
+}
diff --git a/src/test/scala/firrtlTests/ChirrtlSpec.scala b/src/test/scala/firrtlTests/ChirrtlSpec.scala
index 0059d7ed..858d43b6 100644
--- a/src/test/scala/firrtlTests/ChirrtlSpec.scala
+++ b/src/test/scala/firrtlTests/ChirrtlSpec.scala
@@ -73,6 +73,6 @@ class ChirrtlSpec extends FirrtlFlatSpec {
}
it should "compile and run" in {
- runFirrtlTest("CHIRRTLMems", "/features")
+ runFirrtlTest("ChirrtlMems", "/features")
}
}
diff --git a/src/test/scala/firrtlTests/CompilerTests.scala b/src/test/scala/firrtlTests/CompilerTests.scala
new file mode 100644
index 00000000..f66b39e6
--- /dev/null
+++ b/src/test/scala/firrtlTests/CompilerTests.scala
@@ -0,0 +1,120 @@
+package firrtlTests
+
+import java.io.StringWriter
+
+import org.scalatest.FlatSpec
+import org.scalatest.Matchers
+import org.scalatest.junit.JUnitRunner
+
+import firrtl.{Parser,Circuit}
+import firrtl.{
+ HighFirrtlCompiler,
+ LowFirrtlCompiler,
+ VerilogCompiler,
+ Compiler
+}
+
+/**
+ * An example methodology for testing Firrtl compilers.
+ *
+ * Given an input Firrtl circuit (expressed as a string),
+ * the compiler is executed. The output of the compiler
+ * should be compared against the check string.
+ */
+abstract class CompilerSpec extends FlatSpec {
+ def parse (s: String): Circuit = Parser.parse(s.split("\n").toIterator)
+ val writer = new StringWriter()
+ def compiler: Compiler
+ def input: String
+ def check: String
+ def getOutput: String = {
+ compiler.compile(parse(input), Seq.empty, writer)
+ writer.toString()
+ }
+}
+
+/**
+ * An example test for testing the HighFirrtlCompiler.
+ *
+ * Given an input Firrtl circuit (expressed as a string),
+ * the compiler is executed. The output of the compiler
+ * is parsed again and compared (in-memory) to the parsed
+ * input.
+ */
+class HighFirrtlCompilerSpec extends CompilerSpec with Matchers {
+ val compiler = new HighFirrtlCompiler()
+ val input =
+"""circuit Top :
+ module Top :
+ input a : UInt<1>[2]
+ node x = a
+"""
+ val check = input
+ "Any circuit" should "match exactly to its input" in {
+ (parse(getOutput)) should be (parse(check))
+ }
+}
+
+/**
+ * An example test for testing the LoweringCompiler.
+ *
+ * Given an input Firrtl circuit (expressed as a string),
+ * the compiler is executed. The output of the compiler is
+ * a lowered version of the input circuit. The output is
+ * string compared to the correct lowered circuit.
+ */
+class LowFirrtlCompilerSpec extends CompilerSpec with Matchers {
+ val compiler = new LowFirrtlCompiler()
+ val input =
+"""
+circuit Top :
+ module Top :
+ input a : UInt<1>[2]
+ node x = a
+"""
+ val check = Seq(
+ "circuit Top :",
+ " module Top :",
+ " input a_0 : UInt<1>",
+ " input a_1 : UInt<1>",
+ " node x_0 = a_0",
+ " node x_1 = a_1\n\n"
+ ).reduce(_ + "\n" + _)
+ "A circuit" should "match exactly to its lowered state" in {
+ (parse(getOutput)) should be (parse(check))
+ }
+}
+
+/**
+ * An example test for testing the VerilogCompiler.
+ *
+ * Given an input Firrtl circuit (expressed as a string),
+ * the compiler is executed. The output of the compiler is
+ * the corresponding Verilog. The output is string compared
+ * to the correct Verilog.
+ */
+class VerilogCompilerSpec extends CompilerSpec with Matchers {
+ val compiler = new VerilogCompiler()
+ val input =
+"""
+circuit Top :
+ module Top :
+ input a : UInt<1>[2]
+ output b : UInt<1>[2]
+ b <= a
+"""
+ val check = Seq(
+ "module Top(",
+ " input a_0,",
+ " input a_1,",
+ " output b_0,",
+ " output b_1",
+ ");",
+ " assign b_0 = a_0;",
+ " assign b_1 = a_1;",
+ "endmodule\n"
+ ).reduce(_ + "\n" + _)
+ "A circuit's verilog output" should "match the given string" in {
+ (getOutput) should be (check)
+ }
+}
diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala
index 8e1dd8cb..c148b488 100644
--- a/src/test/scala/firrtlTests/FirrtlSpec.scala
+++ b/src/test/scala/firrtlTests/FirrtlSpec.scala
@@ -33,6 +33,7 @@ import com.typesafe.scalalogging.LazyLogging
import scala.sys.process._
import org.scalatest._
import org.scalatest.prop._
+import scala.io.Source
import firrtl._
@@ -101,6 +102,7 @@ trait BackendCompilationUtilities {
"--Wno-fatal",
"--trace",
"-O2",
+ "--top-module", dutFile,
"+define+TOP_TYPE=V" + dutFile,
"-CFLAGS", s"""-Wno-undefined-bool-conversion -O2 -DTOP_TYPE=V$dutFile -include V$dutFile.h""",
"-Mdir", dir.toString,
@@ -129,15 +131,26 @@ trait BackendCompilationUtilities {
trait FirrtlRunners extends BackendCompilationUtilities {
lazy val cppHarness = new File(s"/top.cpp")
- def compileFirrtlTest(prefix: String, srcDir: String): File = {
+ def compileFirrtlTest(
+ prefix: String,
+ srcDir: String,
+ annotations: Seq[CircuitAnnotation] = Seq.empty): File = {
val testDir = createTempDirectory(prefix)
copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir"))
-
- Driver.compile(s"${testDir}/${prefix}.fir", s"${testDir}/${prefix}.v", VerilogCompiler)
+
+ Driver.compile(
+ s"$testDir/$prefix.fir",
+ s"$testDir/$prefix.v",
+ new VerilogCompiler(),
+ Parser.IgnoreInfo,
+ annotations)
testDir
}
- def runFirrtlTest(prefix: String, srcDir: String) {
- val testDir = compileFirrtlTest(prefix, srcDir)
+ def runFirrtlTest(
+ prefix: String,
+ srcDir: String,
+ annotations: Seq[CircuitAnnotation] = Seq.empty) = {
+ val testDir = compileFirrtlTest(prefix, srcDir, annotations)
val harness = new File(testDir, s"top.cpp")
copyResourceToFile(cppHarness.toString, harness)
diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala
new file mode 100644
index 00000000..52c01dc4
--- /dev/null
+++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala
@@ -0,0 +1,272 @@
+package firrtlTests
+
+import java.io.StringWriter
+
+import org.scalatest.FlatSpec
+import org.scalatest.Matchers
+import org.scalatest.junit.JUnitRunner
+
+import firrtl.{Parser,Circuit}
+import firrtl.passes.{PassExceptions,InlineCAKind}
+import firrtl.{
+ Named,
+ ModuleName,
+ ComponentName,
+ Annotation,
+ CircuitAnnotation,
+ TagAnnotation,
+ StickyCircuitAnnotation
+}
+import firrtl.passes.{InlineInstances, InlineCAKind}
+
+
+/**
+ * Tests inline instances transformation
+ */
+class InlineInstancesTests extends HighTransformSpec {
+ val transform = InlineInstances
+ "The module Inline" should "be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of Inline
+ | i.a <= a
+ | b <= i.b
+ | module Inline :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i$a : UInt<32>
+ | wire i$b : UInt<32>
+ | i$b <= i$a
+ | i$a <= a
+ | b <= i$b
+ | module Inline :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("Inline") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind, map)
+ execute(writer, Seq(annotation), input, check)
+ }
+
+ "The all instances of Simple" should "be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i0 of Simple
+ | inst i1 of Simple
+ | i0.a <= a
+ | i1.a <= i0.b
+ | b <= i1.b
+ | module Simple :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i0$a : UInt<32>
+ | wire i0$b : UInt<32>
+ | i0$b <= i0$a
+ | wire i1$a : UInt<32>
+ | wire i1$b : UInt<32>
+ | i1$b <= i1$a
+ | i0$a <= a
+ | i1$a <= i0$b
+ | b <= i1$b
+ | module Simple :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("Simple") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind, map)
+ execute(writer, Seq(annotation), input, check)
+ }
+
+ "Only one instance of Simple" should "be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i0 of Simple
+ | inst i1 of Simple
+ | i0.a <= a
+ | i1.a <= i0.b
+ | b <= i1.b
+ | module Simple :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i0$a : UInt<32>
+ | wire i0$b : UInt<32>
+ | i0$b <= i0$a
+ | inst i1 of Simple
+ | i0$a <= a
+ | i1.a <= i0$b
+ | b <= i1.b
+ | module Simple :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ComponentName("i0",ModuleName("Top")) -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind, map)
+ execute(writer, Seq(annotation), input, check)
+ }
+
+ "All instances of A" should "be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i0 of A
+ | inst i1 of B
+ | i0.a <= a
+ | i1.a <= i0.b
+ | b <= i1.b
+ | module A :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a
+ | module B :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of A
+ | i.a <= a
+ | b <= i.b""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i0$a : UInt<32>
+ | wire i0$b : UInt<32>
+ | i0$b <= i0$a
+ | inst i1 of B
+ | i0$a <= a
+ | i1.a <= i0$b
+ | b <= i1.b
+ | module A :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a
+ | module B :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i$a : UInt<32>
+ | wire i$b : UInt<32>
+ | i$b <= i$a
+ | i$a <= a
+ | b <= i$b""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("A") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind, map)
+ execute(writer, Seq(annotation), input, check)
+ }
+
+
+ // ---- Errors ----
+ // 1) ext module
+ "External module" should "not be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of A
+ | i.a <= a
+ | b <= i.b
+ | extmodule A :
+ | input a : UInt<32>
+ | output b : UInt<32>""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("A") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind,map)
+ failingexecute(writer, Seq(annotation), input)
+ }
+ // 2) ext instance
+ "External instance" should "not be inlined" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of A
+ | i.a <= a
+ | b <= i.b
+ | extmodule A :
+ | input a : UInt<32>
+ | output b : UInt<32>""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("A") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind,map)
+ failingexecute(writer, Seq(annotation), input)
+ }
+ // 3) no module
+ "Inlined module" should "exist" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("A") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind,map)
+ failingexecute(writer, Seq(annotation), input)
+ }
+ // 4) no inst
+ "Inlined instance" should "exist" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a""".stripMargin
+ val writer = new StringWriter()
+ val map: Map[Named, Annotation] = Map(ModuleName("A") -> TagAnnotation)
+ val annotation = StickyCircuitAnnotation(InlineCAKind,map)
+ failingexecute(writer, Seq(annotation), input)
+ }
+}
+
+// Execution driven tests for inlining modules
+class InlineInstancesIntegrationSpec extends FirrtlPropSpec {
+ // Shorthand for creating annotations to inline modules
+ def inlineModules(names: Seq[String]): Seq[CircuitAnnotation] =
+ Seq(StickyCircuitAnnotation(InlineCAKind, names.map(n => ModuleName(n) -> TagAnnotation).toMap))
+
+ case class Test(name: String, dir: String, ann: Seq[CircuitAnnotation])
+
+ val runTests = Seq(
+ Test("GCDTester", "/integration", inlineModules(Seq("DecoupledGCD")))
+ )
+
+ runTests foreach { test =>
+ property(s"${test.name} should execute correctly with inlining") {
+ println(s"Got annotations ${test.ann}")
+ runFirrtlTest(test.name, test.dir, test.ann)
+ }
+ }
+}
diff --git a/src/test/scala/firrtlTests/PassTests.scala b/src/test/scala/firrtlTests/PassTests.scala
new file mode 100644
index 00000000..38ecc7c3
--- /dev/null
+++ b/src/test/scala/firrtlTests/PassTests.scala
@@ -0,0 +1,82 @@
+package firrtlTests
+
+import com.typesafe.scalalogging.LazyLogging
+import java.io.{StringWriter,Writer}
+import org.scalatest.{FlatSpec, Matchers}
+import org.scalatest.junit.JUnitRunner
+import firrtl.{Parser,Circuit,FIRRTLEmitter}
+import firrtl.Parser.IgnoreInfo
+import firrtl.passes.{Pass, PassExceptions}
+import firrtl.{
+ Transform,
+ CircuitAnnotation,
+ TransformResult,
+ SimpleRun,
+ Chisel3ToHighFirrtl,
+ IRToWorkingIR,
+ ResolveAndCheck,
+ HighFirrtlToMiddleFirrtl,
+ MiddleFirrtlToLowFirrtl,
+ EmitFirrtl,
+ Compiler
+}
+
+
+// An example methodology for testing Firrtl Passes
+// Spec class should extend this class
+abstract class SimpleTransformSpec extends FlatSpec with Matchers with Compiler with LazyLogging {
+ // Utility function
+ def parse(s: String): Circuit = Parser.parse(s.split("\n").toIterator, infoMode = IgnoreInfo)
+
+ // Executes the test. Call in tests.
+ def execute(writer: Writer, annotations: Seq[CircuitAnnotation], input: String, check: String) = {
+ compile(parse(input), annotations, writer)
+ logger.debug(writer.toString)
+ logger.debug(check)
+ (parse(writer.toString)) should be (parse(check))
+ }
+ // Executes the test, should throw an error
+ def failingexecute(writer: Writer, annotations: Seq[CircuitAnnotation], input: String) = {
+ intercept[PassExceptions] {
+ compile(parse(input), annotations, writer)
+ }
+ }
+}
+
+trait LowTransformSpec extends SimpleTransformSpec {
+ def transform: Transform
+ def transforms (writer: Writer) = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ new ResolveAndCheck(),
+ new HighFirrtlToMiddleFirrtl(),
+ new MiddleFirrtlToLowFirrtl(),
+ new ResolveAndCheck(),
+ transform,
+ new EmitFirrtl(writer)
+ )
+}
+
+trait MiddleTransformSpec extends SimpleTransformSpec {
+ def transform: Transform
+ def transforms (writer: Writer) = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ new ResolveAndCheck(),
+ new HighFirrtlToMiddleFirrtl(),
+ new ResolveAndCheck(),
+ transform,
+ new EmitFirrtl(writer)
+ )
+}
+
+trait HighTransformSpec extends SimpleTransformSpec {
+ def transform: Transform
+ def transforms (writer: Writer) = Seq(
+ new Chisel3ToHighFirrtl(),
+ new IRToWorkingIR(),
+ new ResolveAndCheck(),
+ transform,
+ new EmitFirrtl(writer)
+ )
+}