diff options
| author | Adam Izraelevitz | 2016-05-02 14:59:51 -0700 |
|---|---|---|
| committer | jackkoenig | 2016-05-12 22:42:06 -0700 |
| commit | f07baed2bc46e107250c317f290af48747a98322 (patch) | |
| tree | a63c5d2477eabccab85dc6780f289fc24cbcad5c /src/main | |
| parent | 0ee659e85c7fe46c2678a49866ef1eca8f4a2c65 (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.scala | 129 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 113 | ||||
| -rw-r--r-- | src/main/scala/firrtl/LoweringAnnotations.scala | 88 | ||||
| -rw-r--r-- | src/main/scala/firrtl/LoweringCompilers.scala | 202 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Inline.scala | 171 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Passes.scala | 14 |
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 = "" |
