diff options
26 files changed, 555 insertions, 242 deletions
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala index 808fa435..6251fc5c 100644 --- a/src/main/scala/firrtl/Compiler.scala +++ b/src/main/scala/firrtl/Compiler.scala @@ -8,6 +8,7 @@ import annotations._ import firrtl.ir.Circuit import passes.Pass +import Utils.throwInternalError /** * RenameMap maps old names to modified names. Generated by transformations @@ -33,10 +34,28 @@ case class AnnotationMap(annotations: Seq[Annotation]) { * Generally only a return value from [[Transform]]s */ case class CircuitState( - circuit: Circuit, - form: CircuitForm, - annotations: Option[AnnotationMap] = None, - renames: Option[RenameMap] = None) + circuit: Circuit, + form: CircuitForm, + annotations: Option[AnnotationMap] = None, + renames: Option[RenameMap] = None) { + + /** Helper for getting just an emitted circuit */ + def emittedCircuitOption: Option[EmittedCircuit] = + emittedComponents collectFirst { case x: EmittedCircuit => x } + /** Helper for getting an [[EmittedCircuit]] when it is known to exist */ + def getEmittedCircuit: EmittedCircuit = emittedCircuitOption match { + case Some(emittedCircuit) => emittedCircuit + case None => throw new FIRRTLException("No EmittedCircuit found! Check Emitter Annotations") + } + /** Helper function for extracting emitted components from annotations */ + def emittedComponents: Seq[EmittedComponent] = { + val emittedOpt = annotations map (_.annotations collect { + case EmittedCircuitAnnotation(x) => x + case EmittedModuleAnnotation(x) => x + }) + emittedOpt.getOrElse(Seq.empty) + } +} /** Current form of the Firrtl Circuit * @@ -137,10 +156,9 @@ abstract class PassBasedTransform extends Transform with PassBased { } } -/** Similar to a Transform except that it writes to a Writer instead of returning a - * CircuitState - */ -abstract class Emitter { +/** Defines old API for Emission. Deprecated */ +trait Emitter extends Transform { + @deprecated("Use emission annotations instead", "firrtl 1.0") def emit(state: CircuitState, writer: Writer): Unit } @@ -157,12 +175,13 @@ object CompilerUtils { Seq.empty } else { inputForm match { - case ChirrtlForm => Seq(new ChirrtlToHighFirrtl) ++ getLoweringTransforms(HighForm, outputForm) + case ChirrtlForm => + Seq(new ChirrtlToHighFirrtl) ++ getLoweringTransforms(HighForm, outputForm) case HighForm => Seq(new IRToWorkingIR, new ResolveAndCheck, new transforms.DedupModules, new HighFirrtlToMiddleFirrtl) ++ getLoweringTransforms(MidForm, outputForm) case MidForm => Seq(new MiddleFirrtlToLowFirrtl) ++ getLoweringTransforms(LowForm, outputForm) - case LowForm => error("Internal Error! This shouldn't be possible") // should be caught by if above + case LowForm => throwInternalError // should be caught by if above } } } @@ -212,7 +231,9 @@ object CompilerUtils { } trait Compiler { + // Emitter is still somewhat special because we want to make sure it is run last def emitter: Emitter + /** The sequence of transforms this compiler will execute * @note The inputForm of a given transform must be higher than or equal to the ouputForm of the * preceding transform. See [[CircuitForm]] @@ -242,10 +263,52 @@ trait Compiler { * @param customTransforms Any custom [[Transform]]s that will be inserted * into the compilation process by [[CompilerUtils.mergeTransforms]] */ + @deprecated("Please use compileAndEmit or other compile method instead", "firrtl 1.0") def compile(state: CircuitState, writer: Writer, customTransforms: Seq[Transform] = Seq.empty): CircuitState = { - val allTransforms = CompilerUtils.mergeTransforms(transforms, customTransforms) + val finalState = compileAndEmit(state, customTransforms) + finalState.emittedCircuitOption match { + case Some(emitted) => writer.write(emitted.value) + case _ => throwInternalError + } + finalState + } + + /** Perform compilation and emit the whole Circuit + * + * This is intended as a convenience method wrapping up Annotation creation for the common case. + * It creates a [[EmitCircuitAnnotation]] that will be consumed by this Transform's emitter. The + * [[EmittedCircuit]] can be extracted from the returned [[CircuitState]] via + * [[CircuitState.emittedCircuitOption]] + * + * @param state The Firrtl AST to compile + * @param customTransforms Any custom [[Transform]]s that will be inserted + * into the compilation process by [[CompilerUtils.mergeTransforms]] + * @return result of compilation with emitted circuit annotated + */ + def compileAndEmit(state: CircuitState, + customTransforms: Seq[Transform] = Seq.empty): CircuitState = { + val emitAnno = EmitCircuitAnnotation(emitter.getClass) + // TODO This is ridiculous. Fix Annotations + val annotations = state.annotations.map(_.annotations).getOrElse(Seq.empty) + val annotationMap = AnnotationMap(annotations :+ emitAnno) + + // Run compiler + compile(state.copy(annotations = Some(annotationMap)), customTransforms) + } + + /** Perform compilation + * + * Emission will only be performed if [[EmitAnnotation]]s are present + * + * @param state The Firrtl AST to compile + * @param customTransforms Any custom [[Transform]]s that will be inserted into the compilation + * process by [[CompilerUtils.mergeTransforms]] + * @return result of compilation + */ + def compile(state: CircuitState, customTransforms: Seq[Transform]): CircuitState = { + val allTransforms = CompilerUtils.mergeTransforms(transforms, customTransforms) :+ emitter val finalState = allTransforms.foldLeft(state) { (in, xform) => val result = Utils.time(s"***${xform.name}***") { xform.execute(in) } @@ -273,8 +336,6 @@ trait Compiler { val newAnnotations = AnnotationMap(remappedAnnotations ++ resultAnnotations) CircuitState(result.circuit, result.form, Some(newAnnotations)) } - - emitter.emit(finalState, writer) finalState } } diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 0fdfa768..f321ad16 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -13,6 +13,7 @@ import Parser.{IgnoreInfo, InfoMode} import annotations._ import firrtl.annotations.AnnotationYamlProtocol._ import firrtl.transforms.{BlackBoxSourceHelper, BlackBoxTargetDir} +import Utils.throwInternalError /** @@ -41,6 +42,7 @@ object Driver { // Compiles circuit. First parses a circuit from an input file, // executes all compiler passes, and writes result to an output // file. + @deprecated("Please use execute", "firrtl 1.0") def compile( input: String, output: String, @@ -155,20 +157,44 @@ object Driver { loadAnnotations(optionsManager) val parsedInput = Parser.parse(firrtlSource, firrtlConfig.infoMode) - val outputBuffer = new java.io.CharArrayWriter - firrtlConfig.compiler.compile( - CircuitState(parsedInput, ChirrtlForm, Some(AnnotationMap(firrtlConfig.annotations))), - outputBuffer, + + // Does this need to be before calling compiler? + optionsManager.makeTargetDir() + + // Output Annotations + val outputAnnos = firrtlConfig.getEmitterAnnos(optionsManager) + + val finalState = firrtlConfig.compiler.compile( + CircuitState(parsedInput, ChirrtlForm, Some(AnnotationMap(firrtlConfig.annotations ++ outputAnnos))), firrtlConfig.customTransforms ) - val outputFileName = firrtlConfig.getOutputFileName(optionsManager) - val outputFile = new java.io.PrintWriter(outputFileName) - val outputString = outputBuffer.toString - outputFile.write(outputString) - outputFile.close() + // Do emission + // Note: Single emission target assumption is baked in here + // Note: FirrtlExecutionSuccess emitted is only used if we're emitting the whole Circuit + val emittedRes = firrtlConfig.getOutputConfig(optionsManager) match { + case SingleFile(filename) => + finalState.emittedCircuitOption match { + case Some(emitted: EmittedCircuit) => + val outputFile = new java.io.PrintWriter(filename) + outputFile.write(emitted.value) + outputFile.close() + emitted.value + case _ => throwInternalError + } + case OneFilePerModule(dirName) => + val emittedModules = finalState.emittedComponents collect { case x: EmittedModule => x } + if (emittedModules.isEmpty) throwInternalError // There should be something + emittedModules.foreach { case module => + val filename = optionsManager.getBuildFileName(firrtlConfig.outputSuffix, s"$dirName/${module.name}") + val outputFile = new java.io.PrintWriter(filename) + outputFile.write(module.value) + outputFile.close() + } + "" // Should we return something different here? + } - FirrtlExecutionSuccess(firrtlConfig.compilerName, outputBuffer.toString) + FirrtlExecutionSuccess(firrtlConfig.compilerName, emittedRes) } /** diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index 9ad97859..10d3ae85 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -12,6 +12,7 @@ import scala.io.Source import firrtl.ir._ import firrtl.passes._ +import firrtl.annotations._ import firrtl.Mappers._ import firrtl.PrimOps._ import firrtl.WrappedExpression._ @@ -22,10 +23,143 @@ import scala.collection.mutable.{ArrayBuffer, LinkedHashMap, HashSet} case class EmitterException(message: String) extends PassException(message) -class FirrtlEmitter extends Emitter { +// ***** Annotations for telling the Emitters what to emit ***** +sealed abstract class EmitAnnotation(marker: String) { + // TODO Is there a better way to do Name to indicate don't care? + def apply(transform: Class[_ <: Transform]): Annotation = + Annotation(CircuitTopName, transform, marker) + def unapply(a: Annotation): Boolean = a match { + // Assumes transform is already filtered appropriately + case Annotation(CircuitTopName, _, str) if str == marker => true + case _ => false + } +} +object EmitCircuitAnnotation extends EmitAnnotation("emitCircuit") +object EmitAllModulesAnnotation extends EmitAnnotation("emitAllModules") + +// ***** Annotations for results of emission ***** +sealed abstract class EmittedComponent { + def name: String + def value: String +} +sealed abstract class EmittedCircuit extends EmittedComponent +final case class EmittedFirrtlCircuit(name: String, value: String) extends EmittedCircuit +final case class EmittedVerilogCircuit(name: String, value: String) extends EmittedCircuit +sealed abstract class EmittedModule extends EmittedComponent +final case class EmittedFirrtlModule(name: String, value: String) extends EmittedModule +final case class EmittedVerilogModule(name: String, value: String) extends EmittedModule + +/** Super class for Annotations containing emitted components + * + * @note These annotations cannot be serialized and deserialized to/from an annotation file + */ +sealed abstract class EmittedAnnotation[T <: EmittedComponent](marker: String) { + // Private datastructure to hold the actual emitted objects + // TODO Once annotations can contain arbitrary datastructures, get rid of this + private val emittedBuffer = mutable.ArrayBuffer.empty[T] + + def apply(value: T): Annotation = { + // Synchronize because of multithreading + // This doesn't happen often, shouldn't be a big deal for performance + val idx = emittedBuffer.synchronized { + emittedBuffer += value + emittedBuffer.size - 1 + } + Annotation(CircuitTopName, classOf[Transform], s"$marker:$idx") + } + def unapply(a: Annotation): Option[T] = a match { + // assume transform has been filtered + case Annotation(CircuitTopName, _, str) if str.startsWith(marker) => + val idx = str.stripPrefix(s"$marker:").toInt + Some(emittedBuffer(idx)) + case _ => None + } +} + +object EmittedFirrtlCircuitAnnotation extends EmittedAnnotation[EmittedFirrtlCircuit]("emittedFirrtlCircuit") +object EmittedVerilogCircuitAnnotation extends EmittedAnnotation[EmittedVerilogCircuit]("emittedVerilogCircuit") +object EmittedCircuitAnnotation { + def apply(value: EmittedCircuit): Annotation = value match { + case firrtl: EmittedFirrtlCircuit => EmittedFirrtlCircuitAnnotation(firrtl) + case verilog: EmittedVerilogCircuit => EmittedVerilogCircuitAnnotation(verilog) + } + def unapply(a: Annotation): Option[EmittedCircuit] = a match { + case EmittedFirrtlCircuitAnnotation(x) => Some(x) + case EmittedVerilogCircuitAnnotation(x) => Some(x) + case _ => None + } +} +object EmittedFirrtlModuleAnnotation extends EmittedAnnotation[EmittedFirrtlModule]("emittedFirrtlModule") +object EmittedVerilogModuleAnnotation extends EmittedAnnotation[EmittedVerilogModule]("emittedVerilogModule") +object EmittedModuleAnnotation { + def apply(value: EmittedModule): Annotation = value match { + case firrtl: EmittedFirrtlModule => EmittedFirrtlModuleAnnotation(firrtl) + case verilog: EmittedVerilogModule => EmittedVerilogModuleAnnotation(verilog) + } + def unapply(a: Annotation): Option[EmittedModule] = a match { + case EmittedFirrtlModuleAnnotation(x) => Some(x) + case EmittedVerilogModuleAnnotation(x) => Some(x) + case _ => None + } +} + + +sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Emitter { + def inputForm = form + def outputForm = form + + private def emitAllModules(circuit: Circuit): Seq[EmittedFirrtlModule] = { + // For a given module, returns a Seq of all modules instantited inside of it + def collectInstantiatedModules(mod: Module, map: Map[String, DefModule]): Seq[DefModule] = { + // Use list instead of set to maintain order + val modules = mutable.ArrayBuffer.empty[DefModule] + def onStmt(stmt: Statement): Statement = stmt match { + case DefInstance(_, _, name) => + modules += map(name) + stmt + case WDefInstance(_, _, name, _) => + modules += map(name) + stmt + case _: WDefInstanceConnector => throwInternalError + case other => other map onStmt + } + onStmt(mod.body) + modules.distinct + } + val modMap = circuit.modules.map(m => m.name -> m).toMap + // Turn each module into it's own circuit with it as the top and all instantied modules as ExtModules + circuit.modules collect { case m: Module => + val instModules = collectInstantiatedModules(m, modMap) + val extModules = instModules map { + case Module(info, name, ports, _) => ExtModule(info, name, ports, name, Seq.empty) + case ext: ExtModule => ext + } + val newCircuit = Circuit(m.info, extModules :+ m, m.name) + EmittedFirrtlModule(m.name, newCircuit.serialize) + } + } + + def execute(state: CircuitState): CircuitState = { + val newAnnos = getMyAnnotations(state).flatMap { + case EmitCircuitAnnotation() => + Seq(EmittedFirrtlCircuitAnnotation.apply( + EmittedFirrtlCircuit(state.circuit.main, state.circuit.serialize))) + case EmitAllModulesAnnotation() => + emitAllModules(state.circuit) map (EmittedFirrtlModuleAnnotation(_)) + case _ => Seq() + } + state.copy(annotations = Some(AnnotationMap(newAnnos))) + } + + // Old style, deprecated def emit(state: CircuitState, writer: Writer): Unit = writer.write(state.circuit.serialize) } +// ***** Start actual Emitters ***** +class HighFirrtlEmitter extends FirrtlEmitter(HighForm) +class MiddleFirrtlEmitter extends FirrtlEmitter(HighForm) +class LowFirrtlEmitter extends FirrtlEmitter(HighForm) + case class VRandom(width: BigInt) extends Expression { def tpe = UIntType(IntWidth(width)) def nWords = (width + 31) / 32 @@ -36,7 +170,10 @@ case class VRandom(width: BigInt) extends Expression { def mapWidth(f: Width => Width): Expression = this } -class VerilogEmitter extends Emitter with PassBased { +class VerilogEmitter extends Transform with PassBased with Emitter { + def inputForm = LowForm + def outputForm = LowForm + val tab = " " def AND(e1: WrappedExpression, e2: WrappedExpression): Expression = { if (e1 == e2) e1.e1 @@ -583,21 +720,22 @@ class VerilogEmitter extends Emitter with PassBased { m } - def emit_preamble(implicit w: Writer) { - emit(Seq( - "`ifdef RANDOMIZE_GARBAGE_ASSIGN\n", - "`define RANDOMIZE\n", - "`endif\n", - "`ifdef RANDOMIZE_INVALID_ASSIGN\n", - "`define RANDOMIZE\n", - "`endif\n", - "`ifdef RANDOMIZE_REG_INIT\n", - "`define RANDOMIZE\n", - "`endif\n", - "`ifdef RANDOMIZE_MEM_INIT\n", - "`define RANDOMIZE\n", - "`endif\n")) - } + /** Preamble for every emitted Verilog file */ + def preamble: String = + """|`ifdef RANDOMIZE_GARBAGE_ASSIGN + |`define RANDOMIZE + |`endif + |`ifdef RANDOMIZE_INVALID_ASSIGN + |`define RANDOMIZE + |`endif + |`ifdef RANDOMIZE_REG_INIT + |`define RANDOMIZE + |`endif + |`ifdef RANDOMIZE_MEM_INIT + |`define RANDOMIZE + |`endif + | + |""".stripMargin def passSeq = Seq( passes.VerilogModulusCleanup, @@ -606,12 +744,37 @@ class VerilogEmitter extends Emitter with PassBased { passes.VerilogPrep) def emit(state: CircuitState, writer: Writer): Unit = { + writer.write(preamble) + val circuit = runPasses(state.circuit) - emit_preamble(writer) - val moduleMap = (circuit.modules map (m => m.name -> m)).toMap - circuit.modules foreach { - case (m: Module) => emit_verilog(m, moduleMap)(writer) - case (m: ExtModule) => + val moduleMap = circuit.modules.map(m => m.name -> m).toMap + circuit.modules.foreach { + case m: Module => emit_verilog(m, moduleMap)(writer) + case _: ExtModule => // do nothing + } + } + + def execute(state: CircuitState): CircuitState = { + val newAnnos = getMyAnnotations(state).flatMap { + case EmitCircuitAnnotation() => + val writer = new java.io.StringWriter + emit(state, writer) + Seq(EmittedVerilogCircuitAnnotation(EmittedVerilogCircuit(state.circuit.main, writer.toString))) + + case EmitAllModulesAnnotation() => + val circuit = runPasses(state.circuit) + val moduleMap = circuit.modules.map(m => m.name -> m).toMap + + circuit.modules flatMap { + case module: Module => + val writer = new java.io.StringWriter + writer.write(preamble) + emit_verilog(module, moduleMap)(writer) + Some(EmittedVerilogModuleAnnotation(EmittedVerilogModule(module.name, writer.toString))) + case _: ExtModule => None + } + case _ => Seq() } + state.copy(annotations = Some(AnnotationMap(newAnnos))) } } diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala index 5615da99..2d221e6b 100644 --- a/src/main/scala/firrtl/ExecutionOptionsManager.scala +++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala @@ -125,6 +125,15 @@ trait HasCommonOptions { parser.help("help").text("prints this usage text") } +/** Firrtl output configuration specified by [[FirrtlExecutionOptions]] + * + * Derived from the fields of the execution options + * @see [[FirrtlExecutionOptions.getOutputConfig]] + */ +sealed abstract class OutputConfig +final case class SingleFile(targetFile: String) extends OutputConfig +final case class OneFilePerModule(targetDir: String) extends OutputConfig + /** * The options that firrtl supports in callable component sense * @@ -133,7 +142,6 @@ trait HasCommonOptions { * @param compilerName which compiler to use * @param annotations annotations to pass to compiler */ - case class FirrtlExecutionOptions( inputFileNameOverride: String = "", outputFileNameOverride: String = "", @@ -144,9 +152,13 @@ case class FirrtlExecutionOptions( customTransforms: Seq[Transform] = List.empty, annotations: List[Annotation] = List.empty, annotationFileNameOverride: String = "", - forceAppendAnnoFile: Boolean = false) + forceAppendAnnoFile: Boolean = false, + emitOneFilePerModule: Boolean = false) extends ComposableOptions { + require(!(emitOneFilePerModule && outputFileNameOverride.nonEmpty), + "Cannot both specify the output filename and emit one file per module!!!") + def infoMode: InfoMode = { infoModeName match { case "use" => UseInfo @@ -186,14 +198,43 @@ case class FirrtlExecutionOptions( def getInputFileName(optionsManager: ExecutionOptionsManager): String = { optionsManager.getBuildFileName("fir", inputFileNameOverride) } - /** - * build the output file name, taking overriding parameters + /** Get the user-specified [[OutputConfig]] * * @param optionsManager this is needed to access build function and its common options - * @return + * @return the output configuration + */ + def getOutputConfig(optionsManager: ExecutionOptionsManager): OutputConfig = { + if (emitOneFilePerModule) OneFilePerModule(optionsManager.targetDirName) + else SingleFile(optionsManager.getBuildFileName(outputSuffix, outputFileNameOverride)) + } + /** Get the user-specified targetFile assuming [[OutputConfig]] is [[SingleFile]] + * + * @param optionsManager this is needed to access build function and its common options + * @return the targetFile as a String */ - def getOutputFileName(optionsManager: ExecutionOptionsManager): String = { - optionsManager.getBuildFileName(outputSuffix, outputFileNameOverride) + def getTargetFile(optionsManager: ExecutionOptionsManager): String = { + getOutputConfig(optionsManager) match { + case SingleFile(targetFile) => targetFile + case other => throw new Exception("OutputConfig is not SingleFile!") + } + } + /** Gives annotations based on the output configuration + * + * @param optionsManager this is needed to access build function and its common options + * @return Annotations that will be consumed by emitter Transforms + */ + def getEmitterAnnos(optionsManager: ExecutionOptionsManager): Seq[Annotation] = { + // TODO should this be a public function? + val emitter = compilerName match { + case "high" => classOf[HighFirrtlEmitter] + case "middle" => classOf[MiddleFirrtlEmitter] + case "low" => classOf[LowFirrtlEmitter] + case "verilog" => classOf[VerilogEmitter] + } + getOutputConfig(optionsManager) match { + case SingleFile(_) => Seq(EmitCircuitAnnotation(emitter)) + case OneFilePerModule(_) => Seq(EmitAllModulesAnnotation(emitter)) + } } /** * build the annotation file name, taking overriding parameters @@ -223,8 +264,13 @@ trait HasFirrtlOptions { parser.opt[String]("output-file") .abbr("o") - .valueName ("<output>"). - foreach { x => + .valueName("<output>") + .validate { x => + if (firrtlOptions.emitOneFilePerModule) + parser.failure("Cannot override output-file if split-modules is specified") + else parser.success + } + .foreach { x => firrtlOptions = firrtlOptions.copy(outputFileNameOverride = x) }.text { "use this to override the default output file name, default is empty" @@ -234,7 +280,7 @@ trait HasFirrtlOptions { .abbr("faf") .valueName ("<output>"). foreach { x => - firrtlOptions = firrtlOptions.copy(outputFileNameOverride = x) + firrtlOptions = firrtlOptions.copy(annotationFileNameOverride = x) }.text { "use this to override the default annotation file name, default is empty" } @@ -334,8 +380,20 @@ trait HasFirrtlOptions { "List which signal drives each clock of every descendent of specified module" } - parser.note("") + parser.opt[Unit]("split-modules") + .abbr("fsm") + .validate { x => + if (firrtlOptions.outputFileNameOverride.nonEmpty) + parser.failure("Cannot split-modules if output-file is specified") + else parser.success + } + .foreach { _ => + firrtlOptions = firrtlOptions.copy(emitOneFilePerModule = true) + }.text { + "Emit each module to its own file in the target directory." + } + parser.note("") } sealed trait FirrtlExecutionResult @@ -345,7 +403,7 @@ sealed trait FirrtlExecutionResult * the type of compile * * @param emitType The name of the compiler used, currently "high", "middle", "low", or "verilog" - * @param emitted The text result of the compilation, could be verilog or firrtl text. + * @param emitted The emitted result of compilation */ case class FirrtlExecutionSuccess(emitType: String, emitted: String) extends FirrtlExecutionResult diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala index 1a4b24cd..ab9f6ea4 100644 --- a/src/main/scala/firrtl/LoweringCompilers.scala +++ b/src/main/scala/firrtl/LoweringCompilers.scala @@ -116,25 +116,25 @@ import firrtl.transforms.BlackBoxSourceHelper * Will replace Chirrtl constructs with Firrtl */ class HighFirrtlCompiler extends Compiler { - def emitter = new FirrtlEmitter + def emitter = new HighFirrtlEmitter def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, HighForm) } /** Emits middle Firrtl input circuit */ class MiddleFirrtlCompiler extends Compiler { - def emitter = new FirrtlEmitter + def emitter = new MiddleFirrtlEmitter def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, MidForm) } /** Emits lowered input circuit */ class LowFirrtlCompiler extends Compiler { - def emitter = new FirrtlEmitter + def emitter = new LowFirrtlEmitter def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, LowForm) } /** Emits Verilog */ class VerilogCompiler extends Compiler { def emitter = new VerilogEmitter - def transforms: Seq[Transform] = - getLoweringTransforms(ChirrtlForm, LowForm) ++ Seq(new LowFirrtlOptimization, new BlackBoxSourceHelper) + def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, LowForm) ++ + Seq(new LowFirrtlOptimization, new BlackBoxSourceHelper) } diff --git a/src/main/scala/firrtl/annotations/Named.scala b/src/main/scala/firrtl/annotations/Named.scala index 4b39c977..0e365249 100644 --- a/src/main/scala/firrtl/annotations/Named.scala +++ b/src/main/scala/firrtl/annotations/Named.scala @@ -14,6 +14,12 @@ sealed trait Named { def serialize: String } +/** Name referring to the top of the circuit */ +final case object CircuitTopName extends Named { + def name: String = "CircuitTop" + def serialize: String = name +} + final case class CircuitName(name: String) extends Named { if(!validModuleName(name)) throw AnnotationException(s"Illegal circuit name: $name") def serialize: String = name diff --git a/src/test/scala/firrtlTests/AnnotationTests.scala b/src/test/scala/firrtlTests/AnnotationTests.scala index 8acada93..29f8f51a 100644 --- a/src/test/scala/firrtlTests/AnnotationTests.scala +++ b/src/test/scala/firrtlTests/AnnotationTests.scala @@ -2,7 +2,7 @@ package firrtlTests -import java.io.{File, FileWriter, StringWriter, Writer} +import java.io.{File, FileWriter, Writer} import firrtl.annotations.AnnotationYamlProtocol._ import firrtl.annotations._ @@ -20,14 +20,14 @@ trait AnnotationSpec extends LowTransformSpec { def transform = new CustomResolveAndCheck(LowForm) // Check if Annotation Exception is thrown - override def failingexecute(writer: Writer, annotations: AnnotationMap, input: String): Exception = { + override def failingexecute(annotations: AnnotationMap, input: String): Exception = { intercept[AnnotationException] { - compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), writer) + compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), Seq.empty) } } - def execute(writer: Writer, annotations: AnnotationMap, input: String, check: Annotation): Unit = { - val cr = compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), writer) - cr.annotations.get.annotations should be (Seq(check)) + def execute(annotations: AnnotationMap, input: String, check: Annotation): Unit = { + val cr = compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), Seq.empty) + cr.annotations.get.annotations should contain (check) } } @@ -53,9 +53,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { val cName = ComponentName("c", mName) "Loose and Sticky annotation on a node" should "pass through" in { - val w = new StringWriter() val ta = Annotation(cName, classOf[Transform], "") - execute(w, getAMap(ta), input, ta) + execute(getAMap(ta), input, ta) } "Annotations" should "be readable from file" in { diff --git a/src/test/scala/firrtlTests/AttachSpec.scala b/src/test/scala/firrtlTests/AttachSpec.scala index 5eed33bd..c29a7e43 100644 --- a/src/test/scala/firrtlTests/AttachSpec.scala +++ b/src/test/scala/firrtlTests/AttachSpec.scala @@ -12,14 +12,6 @@ import firrtl.passes._ import firrtl.Parser.IgnoreInfo class InoutVerilogSpec extends FirrtlFlatSpec { - private def executeTest(input: String, expected: Seq[String], compiler: Compiler) = { - val writer = new StringWriter() - compiler.compile(CircuitState(parse(input), ChirrtlForm), writer) - val lines = writer.toString().split("\n") map normalized - expected foreach { e => - lines should contain(e) - } - } behavior of "Analog" diff --git a/src/test/scala/firrtlTests/CInferMDirSpec.scala b/src/test/scala/firrtlTests/CInferMDirSpec.scala index 3721543b..773a0bf3 100644 --- a/src/test/scala/firrtlTests/CInferMDirSpec.scala +++ b/src/test/scala/firrtlTests/CInferMDirSpec.scala @@ -70,9 +70,8 @@ circuit foo : """.stripMargin val annotationMap = AnnotationMap(Nil) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) } } diff --git a/src/test/scala/firrtlTests/ChirrtlMemSpec.scala b/src/test/scala/firrtlTests/ChirrtlMemSpec.scala index 2bbe46c8..fd984661 100644 --- a/src/test/scala/firrtlTests/ChirrtlMemSpec.scala +++ b/src/test/scala/firrtlTests/ChirrtlMemSpec.scala @@ -77,10 +77,9 @@ circuit foo : """.stripMargin val annotationMap = AnnotationMap(Nil) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) } "Combinational Memory" should "have correct enable signals" in { @@ -104,9 +103,8 @@ circuit foo : """.stripMargin val annotationMap = AnnotationMap(Nil) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) } } diff --git a/src/test/scala/firrtlTests/CompilerTests.scala b/src/test/scala/firrtlTests/CompilerTests.scala index a9fce0c2..39d54755 100644 --- a/src/test/scala/firrtlTests/CompilerTests.scala +++ b/src/test/scala/firrtlTests/CompilerTests.scala @@ -2,8 +2,6 @@ package firrtlTests -import java.io.StringWriter - import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner @@ -29,13 +27,12 @@ import firrtl.{ */ 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(CircuitState(parse(input), ChirrtlForm), writer) - writer.toString() + val res = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm)) + res.getEmittedCircuit.value } } @@ -170,6 +167,6 @@ circuit Top : "endmodule\n" ).reduce(_ + "\n" + _) "A circuit's verilog output" should "match the given string" in { - (getOutput) should be (check) + getOutput should be (check) } } diff --git a/src/test/scala/firrtlTests/ConstantPropagationTests.scala b/src/test/scala/firrtlTests/ConstantPropagationTests.scala index 4506f4ec..95785717 100644 --- a/src/test/scala/firrtlTests/ConstantPropagationTests.scala +++ b/src/test/scala/firrtlTests/ConstantPropagationTests.scala @@ -3,7 +3,6 @@ package firrtlTests import org.scalatest.Matchers -import java.io.{StringWriter,Writer} import firrtl.ir.Circuit import firrtl.Parser.IgnoreInfo import firrtl.Parser diff --git a/src/test/scala/firrtlTests/DriverSpec.scala b/src/test/scala/firrtlTests/DriverSpec.scala index 4e1add39..9cbeb6f9 100644 --- a/src/test/scala/firrtlTests/DriverSpec.scala +++ b/src/test/scala/firrtlTests/DriverSpec.scala @@ -66,7 +66,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities val firrtlOptions = optionsManager.firrtlOptions val inputFileName = optionsManager.getBuildFileName("fir", firrtlOptions.inputFileNameOverride) inputFileName should be ("./cat.fir") - val outputFileName = optionsManager.getBuildFileName("v", firrtlOptions.outputFileNameOverride) + val outputFileName = firrtlOptions.getTargetFile(optionsManager) outputFileName should be ("./cat.v") } "input and output file names can be overridden, overrides do not use targetDir" in { @@ -79,7 +79,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities val firrtlOptions = optionsManager.firrtlOptions val inputFileName = optionsManager.getBuildFileName("fir", firrtlOptions.inputFileNameOverride) inputFileName should be ("./bob.fir") - val outputFileName = optionsManager.getBuildFileName("v", firrtlOptions.outputFileNameOverride) + val outputFileName = firrtlOptions.getTargetFile(optionsManager) outputFileName should be ("carol.v") } "various annotations can be created from command line, currently:" - { @@ -142,26 +142,31 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities annotationsTestFile.delete() } - val input = - """ - |circuit Dummy : - | module Dummy : - | input x : UInt<1> - | output y : UInt<1> - | y <= x - """.stripMargin - - "Driver produces files with different names depending on the compiler" - { - "compiler changes the default name of the output file" in { - + "Circuits are emitted on properly" - { + val input = + """|circuit Top : + | module Top : + | output foo : UInt<32> + | inst c of Child + | inst e of External + | foo <= tail(add(c.foo, e.foo), 1) + | module Child : + | output foo : UInt<32> + | inst e of External + | foo <= e.foo + | extmodule External : + | output foo : UInt<32> + """.stripMargin + + "To a single file with file extension depending on the compiler by default" in { Seq( - "low" -> "./Dummy.lo.fir", - "high" -> "./Dummy.hi.fir", - "middle" -> "./Dummy.mid.fir", - "verilog" -> "./Dummy.v" + "low" -> "./Top.lo.fir", + "high" -> "./Top.hi.fir", + "middle" -> "./Top.mid.fir", + "verilog" -> "./Top.v" ).foreach { case (compilerName, expectedOutputFileName) => val manager = new ExecutionOptionsManager("test") with HasFirrtlOptions { - commonOptions = CommonOptions(topName = "Dummy") + commonOptions = CommonOptions(topName = "Top") firrtlOptions = FirrtlExecutionOptions(firrtlSource = Some(input), compilerName = compilerName) } @@ -172,8 +177,33 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities file.delete() } } + "To a single file per module if OneFilePerModule is specified" in { + Seq( + "low" -> Seq("./Top.lo.fir", "./Child.lo.fir"), + "high" -> Seq("./Top.hi.fir", "./Child.hi.fir"), + "middle" -> Seq("./Top.mid.fir", "./Child.mid.fir"), + "verilog" -> Seq("./Top.v", "./Child.v") + ).foreach { case (compilerName, expectedOutputFileNames) => + println(s"$compilerName -> $expectedOutputFileNames") + val manager = new ExecutionOptionsManager("test") with HasFirrtlOptions { + commonOptions = CommonOptions(topName = "Top") + firrtlOptions = FirrtlExecutionOptions(firrtlSource = Some(input), + compilerName = compilerName, + emitOneFilePerModule = true) + } + + firrtl.Driver.execute(manager) + + for (name <- expectedOutputFileNames) { + val file = new File(name) + file.exists() should be (true) + file.delete() + } + } + } } + "Directory deleter is handy for cleaning up after tests" - { "for example making a directory tree, and deleting it looks like" in { FileUtils.makeDirectory("dog/fox/wolf") diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index 7dd439e4..6bf73a80 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -16,15 +16,13 @@ import firrtl.annotations import firrtl.util.BackendCompilationUtilities trait FirrtlRunners extends BackendCompilationUtilities { - def parse(str: String) = Parser.parse(str.split("\n").toIterator, IgnoreInfo) lazy val cppHarness = new File(s"/top.cpp") /** Compiles input Firrtl to Verilog */ def compileToVerilog(input: String, annotations: AnnotationMap = AnnotationMap(Seq.empty)): String = { val circuit = Parser.parse(input.split("\n").toIterator) val compiler = new VerilogCompiler - val writer = new java.io.StringWriter - compiler.compile(CircuitState(circuit, HighForm, Some(annotations)), writer) - writer.toString + val res = compiler.compileAndEmit(CircuitState(circuit, HighForm, Some(annotations))) + res.getEmittedCircuit.value } /** Compile a Firrtl file * @@ -40,13 +38,15 @@ trait FirrtlRunners extends BackendCompilationUtilities { val testDir = createTestDirectory(prefix) copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir")) - Driver.compile( - s"$testDir/$prefix.fir", - s"$testDir/$prefix.v", - new VerilogCompiler(), - Parser.IgnoreInfo, - customTransforms, - annotations) + val optionsManager = new ExecutionOptionsManager(prefix) with HasFirrtlOptions { + commonOptions = CommonOptions(topName = prefix, targetDirName = testDir.getPath) + firrtlOptions = FirrtlExecutionOptions( + infoModeName = "ignore", + customTransforms = customTransforms, + annotations = annotations.annotations.toList) + } + firrtl.Driver.execute(optionsManager) + testDir } /** Execute a Firrtl Test @@ -79,7 +79,7 @@ trait FirrtlRunners extends BackendCompilationUtilities { } } -trait FirrtlMatchers { +trait FirrtlMatchers extends Matchers { // Replace all whitespace with a single space and remove leading and // trailing whitespace // Note this is intended for single-line strings, no newlines @@ -87,11 +87,23 @@ trait FirrtlMatchers { require(!s.contains("\n")) s.replaceAll("\\s+", " ").trim } + def parse(str: String) = Parser.parse(str.split("\n").toIterator, IgnoreInfo) + /** Helper for executing tests + * compiler will be run on input then emitted result will each be split into + * lines and normalized. + */ + def executeTest(input: String, expected: Seq[String], compiler: Compiler) = { + val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm)) + val lines = finalState.getEmittedCircuit.value split "\n" map normalized + for (e <- expected) { + lines should contain (e) + } + } } abstract class FirrtlPropSpec extends PropSpec with PropertyChecks with FirrtlRunners with LazyLogging -abstract class FirrtlFlatSpec extends FlatSpec with Matchers with FirrtlRunners with FirrtlMatchers with LazyLogging +abstract class FirrtlFlatSpec extends FlatSpec with FirrtlRunners with FirrtlMatchers with LazyLogging /** Super class for execution driven Firrtl tests */ abstract class ExecutionTest(name: String, dir: String, vFiles: Seq[String] = Seq.empty) extends FirrtlPropSpec { diff --git a/src/test/scala/firrtlTests/InferReadWriteSpec.scala b/src/test/scala/firrtlTests/InferReadWriteSpec.scala index a5eea147..91dc911c 100644 --- a/src/test/scala/firrtlTests/InferReadWriteSpec.scala +++ b/src/test/scala/firrtlTests/InferReadWriteSpec.scala @@ -42,6 +42,7 @@ class InferReadWriteSpec extends SimpleTransformSpec { def passSeq = Seq(InferReadWriteCheckPass) } + def emitter = new MiddleFirrtlEmitter def transforms = Seq( new ChirrtlToHighFirrtl, new IRToWorkingIR, @@ -76,10 +77,9 @@ circuit sram6t : """.stripMargin val annotationMap = AnnotationMap(Seq(memlib.InferReadWriteAnnotation("sram6t"))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) } "Infer ReadWrite Ports" should "not infer readwrite ports for the difference clocks" in { @@ -108,9 +108,8 @@ circuit sram6t : """.stripMargin val annotationMap = AnnotationMap(Seq(memlib.InferReadWriteAnnotation("sram6t"))) - val writer = new java.io.StringWriter intercept[InferReadWriteCheckException] { - compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), writer) + compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) } } } diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala index 89862145..25a194d4 100644 --- a/src/test/scala/firrtlTests/InlineInstancesTests.scala +++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala @@ -2,8 +2,6 @@ package firrtlTests -import java.io.StringWriter - import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner @@ -49,9 +47,8 @@ class InlineInstancesTests extends LowTransformSpec { | i$b <= i$a | b <= i$b | i$a <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("Inline", CircuitName("Top"))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "The all instances of Simple" should "be inlined" in { @@ -83,9 +80,8 @@ class InlineInstancesTests extends LowTransformSpec { | b <= i1$b | i0$a <= a | i1$a <= i0$b""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("Simple", CircuitName("Top"))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "Only one instance of Simple" should "be inlined" in { @@ -119,9 +115,8 @@ class InlineInstancesTests extends LowTransformSpec { | input a : UInt<32> | output b : UInt<32> | b <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ComponentName("i0",ModuleName("Top", CircuitName("Top")))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "All instances of A" should "be inlined" in { @@ -165,9 +160,8 @@ class InlineInstancesTests extends LowTransformSpec { | i$b <= i$a | b <= i$b | i$a <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "Non-inlined instances" should "still prepend prefix" in { @@ -205,9 +199,8 @@ class InlineInstancesTests extends LowTransformSpec { | input a : UInt<32> | output b : UInt<32> | b <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } // ---- Errors ---- @@ -224,9 +217,8 @@ class InlineInstancesTests extends LowTransformSpec { | extmodule A : | input a : UInt<32> | output b : UInt<32>""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - failingexecute(writer, aMap, input) + failingexecute(aMap, input) } // 2) ext instance "External instance" should "not be inlined" in { @@ -241,9 +233,8 @@ class InlineInstancesTests extends LowTransformSpec { | extmodule A : | input a : UInt<32> | output b : UInt<32>""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - failingexecute(writer, aMap, input) + failingexecute(aMap, input) } // 3) no module "Inlined module" should "exist" in { @@ -253,9 +244,8 @@ class InlineInstancesTests extends LowTransformSpec { | input a : UInt<32> | output b : UInt<32> | b <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - failingexecute(writer, aMap, input) + failingexecute(aMap, input) } // 4) no inst "Inlined instance" should "exist" in { @@ -265,9 +255,8 @@ class InlineInstancesTests extends LowTransformSpec { | input a : UInt<32> | output b : UInt<32> | b <= a""".stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(InlineAnnotation(ModuleName("A", CircuitName("Top"))))) - failingexecute(writer, aMap, input) + failingexecute(aMap, input) } } diff --git a/src/test/scala/firrtlTests/IntegrationSpec.scala b/src/test/scala/firrtlTests/IntegrationSpec.scala index 335922bd..52fbb611 100644 --- a/src/test/scala/firrtlTests/IntegrationSpec.scala +++ b/src/test/scala/firrtlTests/IntegrationSpec.scala @@ -2,13 +2,51 @@ package firrtlTests +import firrtl._ import org.scalatest._ import org.scalatest.prop._ +import java.io.File + class GCDExecutionTest extends ExecutionTest("GCDTester", "/integration") class RightShiftExecutionTest extends ExecutionTest("RightShiftTester", "/integration") class MemExecutionTest extends ExecutionTest("MemTester", "/integration") +// This is a bit custom some kind of one off +class GCDSplitEmissionExecutionTest extends FirrtlFlatSpec { + "GCDTester" should "work even when the modules are emitted to different files" in { + val top = "GCDTester" + val testDir = createTestDirectory("GCDTesterSplitEmission") + val sourceFile = new File(testDir, s"$top.fir") + copyResourceToFile(s"/integration/$top.fir", sourceFile) + + val optionsManager = new ExecutionOptionsManager("GCDTesterSplitEmission") with HasFirrtlOptions { + commonOptions = CommonOptions(topName = top, targetDirName = testDir.getPath) + firrtlOptions = FirrtlExecutionOptions( + inputFileNameOverride = sourceFile.getPath, + compilerName = "verilog", + infoModeName = "ignore", + emitOneFilePerModule = true) + } + firrtl.Driver.execute(optionsManager) + + // expected filenames + val dutFile = new File(testDir, "DecoupledGCD.v") + val topFile = new File(testDir, s"$top.v") + dutFile should exist + topFile should exist + + // Copy harness over + val harness = new File(testDir, s"top.cpp") + copyResourceToFile(cppHarness.toString, harness) + + // topFile will be compiled by Verilator command by default but we need to also include dutFile + verilogToCpp(top, testDir, Seq(dutFile), harness).! + cppToExe(top, testDir).! + assert(executeExpectingSuccess(top, testDir)) + } +} + class RocketCompilationTest extends CompilationTest("rocket", "/regress") class RocketFirrtlCompilationTest extends CompilationTest("rocket-firrtl", "/regress") class BOOMRobCompilationTest extends CompilationTest("Rob", "/regress") diff --git a/src/test/scala/firrtlTests/MultiThreadingSpec.scala b/src/test/scala/firrtlTests/MultiThreadingSpec.scala index 1698c462..444faebc 100644 --- a/src/test/scala/firrtlTests/MultiThreadingSpec.scala +++ b/src/test/scala/firrtlTests/MultiThreadingSpec.scala @@ -13,10 +13,9 @@ class MultiThreadingSpec extends FirrtlPropSpec { property("The FIRRTL compiler should be thread safe") { // Run the compiler we're testing def runCompiler(input: Seq[String], compiler: firrtl.Compiler): String = { - val writer = new java.io.StringWriter val parsedInput = firrtl.Parser.parse(input) - compiler.compile(CircuitState(parsedInput, ChirrtlForm), writer) - writer.toString + val res = compiler.compileAndEmit(CircuitState(parsedInput, ChirrtlForm)) + res.getEmittedCircuit.value } // The parameters we're testing with val compilers = Seq( diff --git a/src/test/scala/firrtlTests/PassTests.scala b/src/test/scala/firrtlTests/PassTests.scala index c417c4fb..df56c097 100644 --- a/src/test/scala/firrtlTests/PassTests.scala +++ b/src/test/scala/firrtlTests/PassTests.scala @@ -9,50 +9,28 @@ import org.scalatest.junit.JUnitRunner import firrtl.ir.Circuit import firrtl.Parser.UseInfo import firrtl.passes.{Pass, PassExceptions, RemoveEmpty} -import firrtl.{ - Transform, - AnnotationMap, - PassBasedTransform, - CircuitState, - CircuitForm, - ChirrtlForm, - HighForm, - MidForm, - LowForm, - SimpleRun, - ChirrtlToHighFirrtl, - IRToWorkingIR, - ResolveAndCheck, - HighFirrtlToMiddleFirrtl, - MiddleFirrtlToLowFirrtl, - FirrtlEmitter, - Compiler, - Parser -} - +import firrtl._ // An example methodology for testing Firrtl Passes // Spec class should extend this class abstract class SimpleTransformSpec extends FlatSpec with Matchers with Compiler with LazyLogging { - def emitter = new FirrtlEmitter - // Utility function def parse(s: String): Circuit = Parser.parse(s.split("\n").toIterator, infoMode = UseInfo) def squash(c: Circuit): Circuit = RemoveEmpty.run(c) // Executes the test. Call in tests. - def execute(writer: Writer, annotations: AnnotationMap, input: String, check: String): Unit = { - compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), writer) - val actual = RemoveEmpty.run(parse(writer.toString)).serialize + def execute(annotations: AnnotationMap, input: String, check: String): Unit = { + val finalState = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotations))) + val actual = RemoveEmpty.run(parse(finalState.getEmittedCircuit.value)).serialize val expected = parse(check).serialize logger.debug(actual) logger.debug(expected) (actual) should be (expected) } // Executes the test, should throw an error - def failingexecute(writer: Writer, annotations: AnnotationMap, input: String): Exception = { + def failingexecute(annotations: AnnotationMap, input: String): Exception = { intercept[PassExceptions] { - compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), writer) + compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), Seq.empty) } } } @@ -65,6 +43,7 @@ class CustomResolveAndCheck(form: CircuitForm) extends PassBasedTransform { } trait LowTransformSpec extends SimpleTransformSpec { + def emitter = new LowFirrtlEmitter def transform: Transform def transforms = Seq( new ChirrtlToHighFirrtl(), @@ -78,6 +57,7 @@ trait LowTransformSpec extends SimpleTransformSpec { } trait MiddleTransformSpec extends SimpleTransformSpec { + def emitter = new MiddleFirrtlEmitter def transform: Transform def transforms = Seq( new ChirrtlToHighFirrtl(), @@ -90,6 +70,7 @@ trait MiddleTransformSpec extends SimpleTransformSpec { } trait HighTransformSpec extends SimpleTransformSpec { + def emitter = new HighFirrtlEmitter def transform: Transform def transforms = Seq( new ChirrtlToHighFirrtl(), diff --git a/src/test/scala/firrtlTests/ReplSeqMemTests.scala b/src/test/scala/firrtlTests/ReplSeqMemTests.scala index 01a4501b..1a5b44e6 100644 --- a/src/test/scala/firrtlTests/ReplSeqMemTests.scala +++ b/src/test/scala/firrtlTests/ReplSeqMemTests.scala @@ -9,6 +9,7 @@ import firrtl.passes.memlib._ import annotations._ class ReplSeqMemSpec extends SimpleTransformSpec { + def emitter = new LowFirrtlEmitter def transforms = Seq( new ChirrtlToHighFirrtl(), new IRToWorkingIR(), @@ -62,10 +63,9 @@ circuit Top : """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:Top:-o:"+confLoc))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() } @@ -85,10 +85,9 @@ circuit Top : """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:Top:-o:"+confLoc))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() } @@ -111,10 +110,9 @@ circuit CustomMemory : """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() } @@ -137,10 +135,9 @@ circuit CustomMemory : """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - parse(writer.toString) + parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() } @@ -213,10 +210,9 @@ circuit CustomMemory : val aMap = AnnotationMap(Seq( ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc), NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("CustomMemory",CircuitName("CustomMemory")))))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - val circuit = parse(writer.toString) + val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { case e: ExtModule => true case _ => false @@ -254,10 +250,9 @@ circuit CustomMemory : val aMap = AnnotationMap(Seq( ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc), NoDedupMemAnnotation(ComponentName("mem_1", ModuleName("CustomMemory",CircuitName("CustomMemory")))))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - val circuit = parse(writer.toString) + val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { case e: ExtModule => true case _ => false @@ -289,10 +284,9 @@ circuit CustomMemory : """ val confLoc = "ReplSeqMemTests.confTEMP" val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, Some(aMap)), writer) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) // Check correctness of firrtl - val circuit = parse(writer.toString) + val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { case e: ExtModule => true case _ => false diff --git a/src/test/scala/firrtlTests/UnitTests.scala b/src/test/scala/firrtlTests/UnitTests.scala index ec328818..3cf25d3a 100644 --- a/src/test/scala/firrtlTests/UnitTests.scala +++ b/src/test/scala/firrtlTests/UnitTests.scala @@ -107,7 +107,7 @@ class UnitTests extends FirrtlFlatSpec { (c: Circuit, p: Pass) => p.run(c) } val writer = new StringWriter() - (new FirrtlEmitter).emit(CircuitState(c_result, HighForm), writer) + (new HighFirrtlEmitter).emit(CircuitState(c_result, HighForm), writer) (parse(writer.toString())) should be (parse(check)) } @@ -129,7 +129,8 @@ class UnitTests extends FirrtlFlatSpec { intercept[PassException] { val c = Parser.parse(splitExpTestCode.split("\n").toIterator) val c2 = passes.foldLeft(c)((c, p) => p run c) - (new VerilogEmitter).emit(CircuitState(c2, LowForm), new StringWriter) + val writer = new StringWriter() + (new VerilogEmitter).emit(CircuitState(c2, LowForm), writer) } } @@ -140,7 +141,8 @@ class UnitTests extends FirrtlFlatSpec { InferTypes) val c = Parser.parse(splitExpTestCode.split("\n").toIterator) val c2 = passes.foldLeft(c)((c, p) => p run c) - (new VerilogEmitter).emit(CircuitState(c2, LowForm), new StringWriter) + val writer = new StringWriter() + (new VerilogEmitter).emit(CircuitState(c2, LowForm), writer) } "Simple compound expressions" should "be split" in { diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala index 862a9605..6928718a 100644 --- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala +++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala @@ -12,14 +12,6 @@ import firrtl.passes._ import firrtl.Parser.IgnoreInfo class DoPrimVerilog extends FirrtlFlatSpec { - private def executeTest(input: String, expected: Seq[String], compiler: Compiler) = { - val writer = new StringWriter() - compiler.compile(CircuitState(parse(input), ChirrtlForm), writer) - val lines = writer.toString().split("\n") map normalized - expected foreach { e => - lines should contain(e) - } - } "Xorr" should "emit correctly" in { val compiler = new VerilogCompiler val input = diff --git a/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala b/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala index 0c30b59e..39da2a33 100644 --- a/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala +++ b/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala @@ -2,8 +2,6 @@ package firrtlTests.fixed -import java.io.StringWriter - import firrtl.{CircuitState, ChirrtlForm, LowFirrtlCompiler, Parser, AnnotationMap} import firrtl.Parser.IgnoreInfo import firrtlTests.FirrtlFlatSpec @@ -41,11 +39,9 @@ class FixedPointMathSpec extends FirrtlFlatSpec { val lowerer = new LowFirrtlCompiler - val writer = new StringWriter() - - lowerer.compile(CircuitState(parse(input), ChirrtlForm), writer) + val res = lowerer.compileAndEmit(CircuitState(parse(input), ChirrtlForm)) - val output = writer.toString.split("\n") + val output = res.getEmittedCircuit.value split "\n" def inferredAddWidth: Int = { val binaryDifference = binaryPoint1 - binaryPoint2 diff --git a/src/test/scala/firrtlTests/fixed/FixedTypeInferenceSpec.scala b/src/test/scala/firrtlTests/fixed/FixedTypeInferenceSpec.scala index 44fa1d29..37209786 100644 --- a/src/test/scala/firrtlTests/fixed/FixedTypeInferenceSpec.scala +++ b/src/test/scala/firrtlTests/fixed/FixedTypeInferenceSpec.scala @@ -20,14 +20,6 @@ class FixedTypeInferenceSpec extends FirrtlFlatSpec { lines should contain(e) } } - private def executeTest(input: String, expected: Seq[String], compiler: Compiler) = { - val writer = new StringWriter() - compiler.compile(CircuitState(parse(input), ChirrtlForm), writer) - val lines = writer.toString().split("\n") map normalized - expected foreach { e => - lines should contain(e) - } - } "Fixed types" should "infer add correctly" in { val passes = Seq( diff --git a/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala b/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala index b037accf..8cd51b2a 100644 --- a/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala +++ b/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala @@ -2,8 +2,6 @@ package firrtlTests.transforms -import java.io.StringWriter - import firrtl.annotations.{Annotation, CircuitName, ModuleName} import firrtl.transforms._ import firrtl.{AnnotationMap, FIRRTLException, Transform, VerilogCompiler} @@ -80,13 +78,12 @@ class BlacklBoxSourceHelperTransformSpec extends LowTransformSpec { "annotated external modules" should "appear in output directory" in { - val writer = new StringWriter() val aMap = AnnotationMap(Seq( Annotation(moduleName, classOf[BlackBoxSourceHelper], BlackBoxTargetDir("test_run_dir").serialize), Annotation(moduleName, classOf[BlackBoxSourceHelper], BlackBoxResource("/blackboxes/AdderExtModule.v").serialize) )) - execute(writer, aMap, input, output) + execute(aMap, input, output) new java.io.File("test_run_dir/AdderExtModule.v").exists should be (true) new java.io.File(s"test_run_dir/${BlackBoxSourceHelper.FileListName}").exists should be (true) diff --git a/src/test/scala/firrtlTests/transforms/DedupTests.scala b/src/test/scala/firrtlTests/transforms/DedupTests.scala index 62015388..7148dd11 100644 --- a/src/test/scala/firrtlTests/transforms/DedupTests.scala +++ b/src/test/scala/firrtlTests/transforms/DedupTests.scala @@ -3,8 +3,6 @@ package firrtlTests package transform -import java.io.StringWriter - import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner @@ -48,9 +46,8 @@ class DedupModuleTests extends HighTransformSpec { | output x: UInt<1> | x <= UInt(1) """.stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Nil) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "The module A and B" should "be deduped" in { val input = @@ -86,9 +83,8 @@ class DedupModuleTests extends HighTransformSpec { | output x: UInt<1> | x <= UInt(1) """.stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Nil) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "The module A and B with comments" should "be deduped" in { val input = @@ -124,9 +120,8 @@ class DedupModuleTests extends HighTransformSpec { | output x: UInt<1> | x <= UInt(1) """.stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Nil) - execute(writer, aMap, input, check) + execute(aMap, input, check) } "The module B, but not A, with comments" should "be deduped if not annotated" in { val input = @@ -153,9 +148,8 @@ class DedupModuleTests extends HighTransformSpec { | output x: UInt<1> @[xx 1:1] | x <= UInt(1) """.stripMargin - val writer = new StringWriter() val aMap = new AnnotationMap(Seq(NoDedupAnnotation(ModuleName("A", CircuitName("Top"))))) - execute(writer, aMap, input, check) + execute(aMap, input, check) } } |
