aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJack Koenig2017-03-06 14:51:20 -0600
committerGitHub2017-03-06 14:51:20 -0600
commit3d58123ae654a2101ba81304ca3863b3be12c4f3 (patch)
tree2e662485fef5327a2697dbd4a9b42a2cdc5bae5f /src
parentc89f74f19dd5162ee533a0a20825819bc52bc73e (diff)
Add ability to emit 1 file per module (#443)
Changes Emitters to also be Transforms and use Annotations for both telling an emitter to do emission as well as getting the emitted result. Helper functions ease the use of the new interface. Also adds a FirrtlExecutionOptions field as well as a command-line option. Use of Writers in Compilers and Emitters is now deprecated.
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Compiler.scala87
-rw-r--r--src/main/scala/firrtl/Driver.scala46
-rw-r--r--src/main/scala/firrtl/Emitter.scala207
-rw-r--r--src/main/scala/firrtl/ExecutionOptionsManager.scala82
-rw-r--r--src/main/scala/firrtl/LoweringCompilers.scala10
-rw-r--r--src/main/scala/firrtl/annotations/Named.scala6
-rw-r--r--src/test/scala/firrtlTests/AnnotationTests.scala15
-rw-r--r--src/test/scala/firrtlTests/AttachSpec.scala8
-rw-r--r--src/test/scala/firrtlTests/CInferMDirSpec.scala5
-rw-r--r--src/test/scala/firrtlTests/ChirrtlMemSpec.scala10
-rw-r--r--src/test/scala/firrtlTests/CompilerTests.scala9
-rw-r--r--src/test/scala/firrtlTests/ConstantPropagationTests.scala1
-rw-r--r--src/test/scala/firrtlTests/DriverSpec.scala68
-rw-r--r--src/test/scala/firrtlTests/FirrtlSpec.scala38
-rw-r--r--src/test/scala/firrtlTests/InferReadWriteSpec.scala9
-rw-r--r--src/test/scala/firrtlTests/InlineInstancesTests.scala29
-rw-r--r--src/test/scala/firrtlTests/IntegrationSpec.scala38
-rw-r--r--src/test/scala/firrtlTests/MultiThreadingSpec.scala5
-rw-r--r--src/test/scala/firrtlTests/PassTests.scala37
-rw-r--r--src/test/scala/firrtlTests/ReplSeqMemTests.scala36
-rw-r--r--src/test/scala/firrtlTests/UnitTests.scala8
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala8
-rw-r--r--src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala8
-rw-r--r--src/test/scala/firrtlTests/fixed/FixedTypeInferenceSpec.scala8
-rw-r--r--src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala5
-rw-r--r--src/test/scala/firrtlTests/transforms/DedupTests.scala14
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)
}
}