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 | |
| parent | 0ee659e85c7fe46c2678a49866ef1eca8f4a2c65 (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.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 | ||||
| -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.fir | 4 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/AnnotationTests.scala | 57 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ChirrtlSpec.scala | 2 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/CompilerTests.scala | 120 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FirrtlSpec.scala | 23 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/InlineInstancesTests.scala | 272 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/PassTests.scala | 82 |
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) + ) +} |
