aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorAdam Izraelevitz2016-05-02 14:59:51 -0700
committerjackkoenig2016-05-12 22:42:06 -0700
commitf07baed2bc46e107250c317f290af48747a98322 (patch)
treea63c5d2477eabccab85dc6780f289fc24cbcad5c /src/main
parent0ee659e85c7fe46c2678a49866ef1eca8f4a2c65 (diff)
Restructured Compiler to use Transforms. Added an InlineInstance pass.
Transforms are new unit of modularity within the compiler.
Diffstat (limited to 'src/main')
-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
6 files changed, 595 insertions, 122 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 = ""