diff options
| author | Jack Koenig | 2018-02-27 18:07:11 -0800 |
|---|---|---|
| committer | GitHub | 2018-02-27 18:07:11 -0800 |
| commit | c7eb1570dfb1c7701ea32d1209982a053f3cec1d (patch) | |
| tree | 3f509b202d82841c5dad5588d1f953a25d389b44 /src | |
| parent | b90fc784a1819c1d7905910130a7da022214bc22 (diff) | |
Refactor Annotations (#721)
- Old Annotation renamed to deprecated LegacyAnnotation
- Annotation is now a trait that can be extended
- New JsonProtocol for Annotation [de]serialization
- Replace AnnotationMap with AnnotationSeq
- Deprecate Transform.getMyAnnotations
- Update Transforms
- Turn on deprecation warnings
- Remove deprecated Driver.compile
- Make AnnotationTests abstract with Legacy and Json subclasses
- Add functionality to convert LegacyAnnotations of built-in annos
This will give a noisy warning and is more of a best effort than a
robust solution.
Fixes #475 Closes #609
Diffstat (limited to 'src')
46 files changed, 923 insertions, 769 deletions
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala index 504ec85c..d43f415a 100644 --- a/src/main/scala/firrtl/Compiler.scala +++ b/src/main/scala/firrtl/Compiler.scala @@ -97,12 +97,12 @@ final class RenameMap private () { }.mkString("\n") } -/** - * Container of all annotations for a Firrtl compiler. - */ -case class AnnotationMap(annotations: Seq[Annotation]) { - def get(id: Class[_]): Seq[Annotation] = annotations.filter(a => a.transform == id) - def get(named: Named): Seq[Annotation] = annotations.filter(n => n == named) +/** Container of all annotations for a Firrtl compiler */ +class AnnotationSeq private (private[firrtl] val underlying: List[Annotation]) { + def toSeq: Seq[Annotation] = underlying.toSeq +} +object AnnotationSeq { + def apply(xs: Seq[Annotation]) = new AnnotationSeq(xs.toList) } /** Current State of the Circuit @@ -117,8 +117,8 @@ case class AnnotationMap(annotations: Seq[Annotation]) { case class CircuitState( circuit: Circuit, form: CircuitForm, - annotations: Option[AnnotationMap] = None, - renames: Option[RenameMap] = None) { + annotations: AnnotationSeq, + renames: Option[RenameMap]) { /** Helper for getting just an emitted circuit */ def emittedCircuitOption: Option[EmittedCircuit] = @@ -130,20 +130,15 @@ case class CircuitState( throw new FIRRTLException(s"No EmittedCircuit found! Did you delete any annotations?\n$deletedAnnotations") } /** 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) - } - def deletedAnnotations: Seq[Annotation] = { - val deletedOpt = annotations map (_.annotations collect { - case DeletedAnnotation(xformName, anno) => - DeletedAnnotation(xformName, anno) - }) - deletedOpt.getOrElse(Seq.empty) - } + def emittedComponents: Seq[EmittedComponent] = + annotations.collect { case emitted: EmittedAnnotation[_] => emitted.value } + def deletedAnnotations: Seq[Annotation] = + annotations.collect { case anno: DeletedAnnotation => anno } +} +object CircuitState { + def apply(circuit: Circuit, form: CircuitForm): CircuitState = apply(circuit, form, Seq()) + def apply(circuit: Circuit, form: CircuitForm, annotations: AnnotationSeq) = + new CircuitState(circuit, form, annotations, None) } /** Current form of the Firrtl Circuit @@ -222,15 +217,19 @@ abstract class Transform extends LazyLogging { * @return A transformed Firrtl AST */ protected def execute(state: CircuitState): CircuitState + /** Convenience method to get annotations relevant to this Transform * * @param state The [[CircuitState]] form which to extract annotations * @return A collection of annotations */ - final def getMyAnnotations(state: CircuitState): Seq[Annotation] = state.annotations match { - case Some(annotations) => annotations.get(this.getClass) //TODO(azidar): ++ annotations.get(classOf[Transform]) - case None => Nil + @deprecated("Just collect the actual Annotation types the transform wants", "1.1") + final def getMyAnnotations(state: CircuitState): Seq[Annotation] = { + val msg = "getMyAnnotations is deprecated, use collect and match on concrete types" + Driver.dramaticWarning(msg) + state.annotations.collect { case a: LegacyAnnotation if a.transform == this.getClass => a } } + /** Perform the transform and update annotations. * * @param state Input Firrtl AST @@ -253,23 +252,23 @@ abstract class Transform extends LazyLogging { } logger.trace(s"Circuit:\n${result.circuit.serialize}") logger.info(s"======== Finished Transform $name ========\n") - CircuitState(result.circuit, result.form, Some(AnnotationMap(remappedAnnotations)), None) + CircuitState(result.circuit, result.form, remappedAnnotations, None) } /** Propagate annotations and update their names. * - * @param inAnno input AnnotationMap - * @param resAnno result AnnotationMap + * @param inAnno input AnnotationSeq + * @param resAnno result AnnotationSeq * @param renameOpt result RenameMap * @return the updated annotations */ final private def propagateAnnotations( - inAnno: Option[AnnotationMap], - resAnno: Option[AnnotationMap], - renameOpt: Option[RenameMap]): Seq[Annotation] = { + inAnno: AnnotationSeq, + resAnno: AnnotationSeq, + renameOpt: Option[RenameMap]): AnnotationSeq = { val newAnnotations = { - val inSet = inAnno.getOrElse(AnnotationMap(Seq.empty)).annotations.toSet - val resSet = resAnno.getOrElse(AnnotationMap(Seq.empty)).annotations.toSet + val inSet = inAnno.toSet + val resSet = resAnno.toSet val deleted = (inSet -- resSet).map { case DeletedAnnotation(xFormName, delAnno) => DeletedAnnotation(s"$xFormName+$name", delAnno) case anno => DeletedAnnotation(name, anno) @@ -283,7 +282,7 @@ abstract class Transform extends LazyLogging { val renames = renameOpt.getOrElse(RenameMap()) for { anno <- newAnnotations.toSeq - newAnno <- anno.update(renames.get(anno.target).getOrElse(Seq(anno.target))) + newAnno <- anno.update(renames) } yield newAnno } } @@ -437,12 +436,7 @@ trait Compiler extends LazyLogging { 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) + compile(state.copy(annotations = emitAnno +: state.annotations), customTransforms) } /** Perform compilation diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 3c6f687f..77412a0e 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -5,6 +5,7 @@ package firrtl import scala.collection._ import scala.io.Source import scala.sys.process.{BasicIO,stringSeqToProcess} +import scala.util.{Try, Success, Failure} import scala.util.control.ControlThrowable import java.io.{File, FileNotFoundException} @@ -41,44 +42,6 @@ import firrtl.Utils.throwInternalError */ object Driver { - //noinspection ScalaDeprecation - // 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, - compiler: Compiler, - infoMode: InfoMode = IgnoreInfo, - customTransforms: Seq[Transform] = Seq.empty, - annotations: AnnotationMap = AnnotationMap(Seq.empty) - ): String = { - val outputBuffer = new java.io.CharArrayWriter - try { - val parsedInput = Parser.parse(Source.fromFile(input).getLines(), infoMode) - compiler.compile( - CircuitState(parsedInput, ChirrtlForm, Some(annotations)), - outputBuffer, - customTransforms) - } - - catch { - // Rethrow the exceptions which are expected or due to the runtime environment (out of memory, stack overflow) - case p: ControlThrowable => throw p - case p: PassException => throw p - case p: FIRRTLException => throw p - // Treat remaining exceptions as internal errors. - case e: Exception => throwInternalError(exception = Some(e)) - } - - val outputFile = new java.io.PrintWriter(output) - val outputString = outputBuffer.toString - outputFile.write(outputString) - outputFile.close() - outputString - } - /** Print a warning message * * @param message error message @@ -155,26 +118,35 @@ object Driver { if (!file.exists) { throw new FileNotFoundException(s"Annotation file $file not found!") } - val yaml = io.Source.fromFile(file).getLines().mkString("\n").parseYaml - yaml.convertTo[List[Annotation]] - + // Try new protocol first + JsonProtocol.deserializeTry(file).getOrElse { + val annos = Try { + val yaml = io.Source.fromFile(file).getLines().mkString("\n").parseYaml + yaml.convertTo[List[LegacyAnnotation]] + } + annos match { + case Success(result) => + val msg = s"$file is a YAML file!\n" + + (" "*9) + "YAML Annotation files are deprecated! Use JSON" + Driver.dramaticWarning(msg) + result + case Failure(_) => throw new InvalidAnnotationFileException(file.toString) + } + } } - val targetDirAnno = - List(Annotation( - CircuitName("All"), - classOf[BlackBoxSourceHelper], - BlackBoxTargetDir(optionsManager.targetDirName).serialize - )) + val targetDirAnno = List(BlackBoxTargetDirAnno(optionsManager.targetDirName)) // Output Annotations val outputAnnos = firrtlConfig.getEmitterAnnos(optionsManager) val globalAnnos = Seq(TargetDirAnnotation(optionsManager.targetDirName)) ++ - (if (firrtlConfig.dontCheckCombLoops) Seq(DontCheckCombLoopsAnnotation()) else Seq()) ++ - (if (firrtlConfig.noDCE) Seq(NoDCEAnnotation()) else Seq()) + (if (firrtlConfig.dontCheckCombLoops) Seq(DontCheckCombLoopsAnnotation) else Seq()) ++ + (if (firrtlConfig.noDCE) Seq(NoDCEAnnotation) else Seq()) - targetDirAnno ++ outputAnnos ++ globalAnnos ++ firrtlConfig.annotations ++ loadedAnnos + val annos = targetDirAnno ++ outputAnnos ++ globalAnnos ++ + firrtlConfig.annotations ++ loadedAnnos + LegacyAnnotation.convertLegacyAnnos(annos) } /** @@ -229,21 +201,18 @@ object Driver { optionsManager.makeTargetDir() maybeFinalState = Some(firrtlConfig.compiler.compile( - CircuitState(parsedInput, - ChirrtlForm, - Some(AnnotationMap(annos))), + CircuitState(parsedInput, ChirrtlForm, annos), firrtlConfig.customTransforms )) } - - catch { - // Rethrow the exceptions which are expected or due to the runtime environment (out of memory, stack overflow) - case p: ControlThrowable => throw p - case p: PassException => throw p - case p: FIRRTLException => throw p - // Treat remaining exceptions as internal errors. - case e: Exception => throwInternalError(exception = Some(e)) - } + catch { + // Rethrow the exceptions which are expected or due to the runtime environment (out of memory, stack overflow) + case p: ControlThrowable => throw p + case p: PassException => throw p + case p: FIRRTLException => throw p + // Treat remaining exceptions as internal errors. + case e: Exception => throwInternalError(exception = Some(e)) + } val finalState = maybeFinalState.get @@ -273,11 +242,9 @@ object Driver { optionsManager.firrtlOptions.outputAnnotationFileName match { case "" => case file => - val filename = optionsManager.getBuildFileName("anno", file) + val filename = optionsManager.getBuildFileName("anno.json", file) val outputFile = new java.io.PrintWriter(filename) - finalState.annotations.foreach { - finalAnnos => outputFile.write(finalAnnos.annotations.toYaml.prettyPrint) - } + outputFile.write(JsonProtocol.serialize(finalState.annotations)) outputFile.close() } diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index cf356dcb..a94ed37f 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -24,18 +24,11 @@ import scala.collection.mutable.{ArrayBuffer, LinkedHashMap, HashSet} case class EmitterException(message: String) extends PassException(message) // ***** 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 - } +sealed trait EmitAnnotation extends NoTargetAnnotation { + val emitter: Class[_ <: Emitter] } -object EmitCircuitAnnotation extends EmitAnnotation("emitCircuit") -object EmitAllModulesAnnotation extends EmitAnnotation("emitAllModules") +case class EmitCircuitAnnotation(emitter: Class[_ <: Emitter]) extends EmitAnnotation +case class EmitAllModulesAnnotation(emitter: Class[_ <: Emitter]) extends EmitAnnotation // ***** Annotations for results of emission ***** sealed abstract class EmittedComponent { @@ -49,60 +42,22 @@ 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 - } +/** Traits for Annotations containing emitted components */ +sealed trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation { + val value: T } +sealed trait EmittedCircuitAnnotation[T <: EmittedCircuit] extends EmittedAnnotation[T] +sealed trait EmittedModuleAnnotation[T <: EmittedModule] extends EmittedAnnotation[T] -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 - } -} +case class EmittedFirrtlCircuitAnnotation(value: EmittedFirrtlCircuit) + extends EmittedCircuitAnnotation[EmittedFirrtlCircuit] +case class EmittedVerilogCircuitAnnotation(value: EmittedVerilogCircuit) + extends EmittedCircuitAnnotation[EmittedVerilogCircuit] +case class EmittedFirrtlModuleAnnotation(value: EmittedFirrtlModule) + extends EmittedModuleAnnotation[EmittedFirrtlModule] +case class EmittedVerilogModuleAnnotation(value: EmittedVerilogModule) + extends EmittedModuleAnnotation[EmittedVerilogModule] sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Emitter { def inputForm = form @@ -140,19 +95,15 @@ sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Em } override def execute(state: CircuitState): CircuitState = { - val newAnnos = getMyAnnotations(state).flatMap { - case EmitCircuitAnnotation() => + val newAnnos = state.annotations.flatMap { + case EmitCircuitAnnotation(_) => Seq(EmittedFirrtlCircuitAnnotation.apply( EmittedFirrtlCircuit(state.circuit.main, state.circuit.serialize))) - case EmitAllModulesAnnotation() => + case EmitAllModulesAnnotation(_) => emitAllModules(state.circuit) map (EmittedFirrtlModuleAnnotation(_)) case _ => Seq() } - val annos = newAnnos ++ (state.annotations match { - case None => Seq.empty - case Some(a) => a.annotations - }) - state.copy(annotations = Some(AnnotationMap(annos))) + state.copy(annotations = newAnnos ++ state.annotations) } // Old style, deprecated @@ -750,13 +701,13 @@ class VerilogEmitter extends SeqTransform with Emitter { } override def execute(state: CircuitState): CircuitState = { - val newAnnos = getMyAnnotations(state).flatMap { - case EmitCircuitAnnotation() => + val newAnnos = state.annotations.flatMap { + case EmitCircuitAnnotation(_) => val writer = new java.io.StringWriter emit(state, writer) Seq(EmittedVerilogCircuitAnnotation(EmittedVerilogCircuit(state.circuit.main, writer.toString))) - case EmitAllModulesAnnotation() => + case EmitAllModulesAnnotation(_) => val circuit = runTransforms(state).circuit val moduleMap = circuit.modules.map(m => m.name -> m).toMap @@ -769,10 +720,6 @@ class VerilogEmitter extends SeqTransform with Emitter { } case _ => Seq() } - val annos = newAnnos ++ (state.annotations match { - case None => Seq.empty - case Some(a) => a.annotations - }) - state.copy(annotations = Some(AnnotationMap(annos))) + state.copy(annotations = newAnnos ++ state.annotations) } } diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala index de5c9898..1aac0ad6 100644 --- a/src/main/scala/firrtl/ExecutionOptionsManager.scala +++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala @@ -67,8 +67,8 @@ case class CommonOptions( } } -/** [[annotations.GlobalCircuitAnnotation]] that contains the [[CommonOptions]] target directory */ -object TargetDirAnnotation extends GlobalCircuitAnnotation +/** Annotation that contains the [[CommonOptions]] target directory */ +case class TargetDirAnnotation(value: String) extends SingleStringAnnotation trait HasCommonOptions { self: ExecutionOptionsManager => @@ -408,7 +408,7 @@ trait HasFirrtlOptions { .valueName ("<circuit>") .foreach { x => firrtlOptions = firrtlOptions.copy( - annotations = firrtlOptions.annotations :+ InferReadWriteAnnotation(x), + annotations = firrtlOptions.annotations :+ InferReadWriteAnnotation, customTransforms = firrtlOptions.customTransforms :+ new passes.memlib.InferReadWrite ) }.text { @@ -420,7 +420,7 @@ trait HasFirrtlOptions { .valueName ("-c:<circuit>:-i:<filename>:-o:<filename>") .foreach { x => firrtlOptions = firrtlOptions.copy( - annotations = firrtlOptions.annotations :+ ReplSeqMemAnnotation(x), + annotations = firrtlOptions.annotations :+ ReplSeqMemAnnotation.parse(x), customTransforms = firrtlOptions.customTransforms :+ new passes.memlib.ReplSeqMem ) } @@ -433,7 +433,7 @@ trait HasFirrtlOptions { .valueName ("-c:<circuit>:-m:<module>:-o:<filename>") .foreach { x => firrtlOptions = firrtlOptions.copy( - annotations = firrtlOptions.annotations :+ ClockListAnnotation(x), + annotations = firrtlOptions.annotations :+ ClockListAnnotation.parse(x), customTransforms = firrtlOptions.customTransforms :+ new passes.clocklist.ClockListTransform ) } diff --git a/src/main/scala/firrtl/annotations/Annotation.scala b/src/main/scala/firrtl/annotations/Annotation.scala index f96724c6..b16509a1 100644 --- a/src/main/scala/firrtl/annotations/Annotation.scala +++ b/src/main/scala/firrtl/annotations/Annotation.scala @@ -8,16 +8,80 @@ import firrtl.annotations.AnnotationYamlProtocol._ case class AnnotationException(message: String) extends Exception(message) -final case class Annotation(target: Named, transform: Class[_ <: Transform], value: String) { +/** Base type of auxiliary information */ +trait Annotation { + /** Update the target based on how signals are renamed */ + def update(renames: RenameMap): Seq[Annotation] + + /** Pretty Print + * + * @note In [[logger.LogLevel.Debug]] this is called on every Annotation after every Transform + */ + def serialize: String = this.toString +} + +/** If an Annotation does not target any [[Named]] thing in the circuit, then all updates just + * return the Annotation itself + */ +trait NoTargetAnnotation extends Annotation { + def update(renames: RenameMap) = Seq(this) +} + +/** An Annotation that targets a single [[Named]] thing */ +trait SingleTargetAnnotation[T <: Named] extends Annotation { + val target: T + + /** Create another instance of this Annotation */ + def duplicate(n: T): Annotation + + // This mess of @unchecked and try-catch is working around the fact that T is unknown due to type + // erasure. We cannot that newTarget is of type T, but a CastClassException will be thrown upon + // invoking duplicate if newTarget cannot be cast to T (only possible in the concrete subclass) + def update(renames: RenameMap): Seq[Annotation] = + renames.get(target).map(_.map(newT => (newT: @unchecked) match { + case newTarget: T @unchecked => + try { + duplicate(newTarget) + } catch { + case _: java.lang.ClassCastException => + val msg = s"${this.getClass.getName} target ${target.getClass.getName} " + + s"cannot be renamed to ${newTarget.getClass}" + throw AnnotationException(msg) + } + })).getOrElse(List(this)) +} + +trait SingleStringAnnotation extends NoTargetAnnotation { + def value: String +} + +object Annotation { + @deprecated("This returns a LegacyAnnotation, use an explicit Annotation type", "1.1") + def apply(target: Named, transform: Class[_ <: Transform], value: String) = + new LegacyAnnotation(target, transform, value) + @deprecated("This uses LegacyAnnotation, use an explicit Annotation type", "1.1") + def unapply(a: LegacyAnnotation): Option[(Named, Class[_ <: Transform], String)] = + Some((a.target, a.transform, a.value)) +} + +// Constructor is private so that we can still construct these internally without deprecation +// warnings +final case class LegacyAnnotation private[firrtl] ( + target: Named, + transform: Class[_ <: Transform], + value: String) extends SingleTargetAnnotation[Named] { val targetString: String = target.serialize val transformClass: String = transform.getName + def targets(named: Named): Boolean = named == target + def targets(transform: Class[_ <: Transform]): Boolean = transform == this.transform + /** * This serialize is basically a pretty printer, actual serialization is handled by * AnnotationYamlProtocol * @return a nicer string than the raw case class default */ - def serialize: String = { + override def serialize: String = { s"Annotation(${target.serialize},${transform.getCanonicalName},$value)" } @@ -27,34 +91,89 @@ final case class Annotation(target: Named, transform: Class[_ <: Transform], val } def propagate(from: Named, tos: Seq[Named], dup: Named=>Annotation): Seq[Annotation] = tos.map(dup(_)) def check(from: Named, tos: Seq[Named], which: Annotation): Unit = {} - def duplicate(n: Named) = Annotation(n, transform, value) + def duplicate(n: Named) = new LegacyAnnotation(n, transform, value) } -object DeletedAnnotation { - def apply(xFormName: String, anno: Annotation): Annotation = - Annotation(anno.target, classOf[Transform], s"""DELETED by $xFormName\n${AnnotationUtils.toYaml(anno)}""") +// Private so that LegacyAnnotation can only be constructed via deprecated Annotation.apply +private[firrtl] object LegacyAnnotation { + // ***** Everything below here is to help people migrate off of old annotations ***** + def errorIllegalAnno(name: String) = + throw new Exception(s"Old-style annotations that look like $name are no longer supported") + + private val OldDeletedRegex = """(?s)DELETED by ([^\n]*)\n(.*)""".r + private val PinsRegex = "pins:(.*)".r + private val SourceRegex = "source (.+)".r + private val SinkRegex = "sink (.+)".r - private val deletedRegex = """(?s)DELETED by ([^\n]*)\n(.*)""".r - def unapply(a: Annotation): Option[(String, Annotation)] = a match { - case Annotation(named, t, deletedRegex(xFormName, annoString)) if t == classOf[Transform] => - Some((xFormName, AnnotationUtils.fromYaml(annoString))) - case _ => None + import firrtl.transforms._ + import firrtl.passes._ + import firrtl.passes.memlib._ + import firrtl.passes.wiring._ + import firrtl.passes.clocklist._ + // Attempt to convert common Annotations and error on the rest of old-style build-in annotations + def convertLegacyAnno(anno: LegacyAnnotation): Annotation = anno match { + // All old-style Emitter annotations are illegal + case LegacyAnnotation(_,_,"emitCircuit") => errorIllegalAnno("EmitCircuitAnnotation") + case LegacyAnnotation(_,_,"emitAllModules") => errorIllegalAnno("EmitAllModulesAnnotation") + case LegacyAnnotation(_,_,value) if value.startsWith("emittedFirrtlCircuit") => + errorIllegalAnno("EmittedFirrtlCircuitAnnotation") + case LegacyAnnotation(_,_,value) if value.startsWith("emittedFirrtlModule") => + errorIllegalAnno("EmittedFirrtlModuleAnnotation") + case LegacyAnnotation(_,_,value) if value.startsWith("emittedVerilogCircuit") => + errorIllegalAnno("EmittedVerilogCircuitAnnotation") + case LegacyAnnotation(_,_,value) if value.startsWith("emittedVerilogModule") => + errorIllegalAnno("EmittedVerilogModuleAnnotation") + // People shouldn't be trying to pass deleted annotations to Firrtl + case LegacyAnnotation(_,_,OldDeletedRegex(_,_)) => errorIllegalAnno("DeletedAnnotation") + // Some annotations we'll try to support + case LegacyAnnotation(named, t, _) if t == classOf[InlineInstances] => InlineAnnotation(named) + case LegacyAnnotation(n: ModuleName, t, outputConfig) if t == classOf[ClockListTransform] => + ClockListAnnotation(n, outputConfig) + case LegacyAnnotation(CircuitName(_), transform, "") if transform == classOf[InferReadWrite] => + InferReadWriteAnnotation + case LegacyAnnotation(_,_,PinsRegex(pins)) => PinAnnotation(pins.split(" ")) + case LegacyAnnotation(_, t, value) if t == classOf[ReplSeqMem] => + val args = value.split(" ") + require(args.size == 2, "Something went wrong, stop using legacy ReplSeqMemAnnotation") + ReplSeqMemAnnotation(args(0), args(1)) + case LegacyAnnotation(c: ComponentName, transform, "nodedupmem!") + if transform == classOf[ResolveMemoryReference] => NoDedupMemAnnotation(c) + case LegacyAnnotation(m: ModuleName, transform, "nodedup!") + if transform == classOf[DedupModules] => NoDedupAnnotation(m) + case LegacyAnnotation(c: ComponentName, _, SourceRegex(pin)) => SourceAnnotation(c, pin) + case LegacyAnnotation(n, _, SinkRegex(pin)) => SinkAnnotation(n, pin) + case LegacyAnnotation(m: ModuleName, t, text) if t == classOf[BlackBoxSourceHelper] => + text.split("\n", 3).toList match { + case "resource" :: id :: _ => BlackBoxResourceAnno(m, id) + case "inline" :: name :: text :: _ => BlackBoxInlineAnno(m, name, text) + case "targetDir" :: targetDir :: _ => BlackBoxTargetDirAnno(targetDir) + case _ => errorIllegalAnno("BlackBoxSourceAnnotation") + } + case LegacyAnnotation(_, transform, "noDCE!") if transform == classOf[DeadCodeElimination] => + NoDCEAnnotation + case LegacyAnnotation(c: ComponentName, _, "DONTtouch!") => DontTouchAnnotation(c) + case LegacyAnnotation(c: ModuleName, _, "optimizableExtModule!") => + OptimizableExtModuleAnnotation(c) + case other => other + } + def convertLegacyAnnos(annos: Seq[Annotation]): Seq[Annotation] = { + var warned: Boolean = false + annos.map { + case legacy: LegacyAnnotation => + val annox = convertLegacyAnno(legacy) + if (!warned && (annox ne legacy)) { + val msg = s"A LegacyAnnotation was automatically converted.\n" + (" "*9) + + "This functionality will soon be removed. Please migrate to new annotations." + Driver.dramaticWarning(msg) + warned = true + } + annox + case other => other + } } } -/** Parent class to create global annotations - * - * These annotations are Circuit-level and available to every transform - */ -abstract class GlobalCircuitAnnotation { - private lazy val marker = this.getClass.getName - def apply(value: String): Annotation = - Annotation(CircuitTopName, classOf[Transform], s"$marker:$value") - def unapply(a: Annotation): Option[String] = a match { - // Assumes transform is already filtered appropriately - case Annotation(CircuitTopName, _, str) if str.startsWith(marker) => - Some(str.stripPrefix(s"$marker:")) - case _ => None - } +case class DeletedAnnotation(xFormName: String, anno: Annotation) extends NoTargetAnnotation { + override def serialize: String = s"""DELETED by $xFormName\n${anno.serialize}""" } diff --git a/src/main/scala/firrtl/annotations/AnnotationUtils.scala b/src/main/scala/firrtl/annotations/AnnotationUtils.scala index 240c46d6..0fc192a6 100644 --- a/src/main/scala/firrtl/annotations/AnnotationUtils.scala +++ b/src/main/scala/firrtl/annotations/AnnotationUtils.scala @@ -3,15 +3,22 @@ package firrtl package annotations +import org.json4s._ +import org.json4s.native.JsonMethods._ +import org.json4s.native.Serialization +import org.json4s.native.Serialization.{read, write, writePretty} + import net.jcazevedo.moultingyaml._ import firrtl.annotations.AnnotationYamlProtocol._ import firrtl.ir._ import firrtl.Utils.error +class InvalidAnnotationFileException(msg: String) extends Exception(msg) + object AnnotationUtils { - def toYaml(a: Annotation): String = a.toYaml.prettyPrint - def fromYaml(s: String): Annotation = s.parseYaml.convertTo[Annotation] + def toYaml(a: LegacyAnnotation): String = a.toYaml.prettyPrint + def fromYaml(s: String): LegacyAnnotation = s.parseYaml.convertTo[LegacyAnnotation] /** Returns true if a valid Module name */ val SerializedModuleName = """([a-zA-Z_][a-zA-Z_0-9~!@#$%^*\-+=?/]*)""".r diff --git a/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala b/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala index 9018d494..e0337d6e 100644 --- a/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala +++ b/src/main/scala/firrtl/annotations/AnnotationYamlProtocol.scala @@ -7,23 +7,24 @@ import net.jcazevedo.moultingyaml._ object AnnotationYamlProtocol extends DefaultYamlProtocol { // bottom depends on top - implicit object AnnotationYamlFormat extends YamlFormat[Annotation] { - def write(a: Annotation) = YamlObject( + implicit object AnnotationYamlFormat extends YamlFormat[LegacyAnnotation] { + def write(a: LegacyAnnotation) = YamlObject( YamlString("targetString") -> YamlString(a.targetString), YamlString("transformClass") -> YamlString(a.transformClass), YamlString("value") -> YamlString(a.value) ) - def read(yamlValue: YamlValue): Annotation = { + def read(yamlValue: YamlValue): LegacyAnnotation = { try { yamlValue.asYamlObject.getFields( YamlString("targetString"), YamlString("transformClass"), YamlString("value")) match { case Seq(YamlString(targetString), YamlString(transformClass), YamlString(value)) => - Annotation( - toTarget(targetString), Class.forName(transformClass).asInstanceOf[Class[_ <: Transform]], value) - case _ => deserializationError("Annotation expected") + LegacyAnnotation(toTarget(targetString), + Class.forName(transformClass).asInstanceOf[Class[_ <: Transform]], + value) + case _ => deserializationError("LegacyAnnotation expected") } } catch { diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala new file mode 100644 index 00000000..5005ccb0 --- /dev/null +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -0,0 +1,84 @@ + +package firrtl +package annotations + +import scala.util.Try + +import org.json4s._ +import org.json4s.native.JsonMethods._ +import org.json4s.native.Serialization +import org.json4s.native.Serialization.{read, write, writePretty} + +import firrtl.ir._ +import firrtl.Utils.error + +object JsonProtocol { + + // Helper for error messages + private def prettifyJsonInput(in: JsonInput): String = { + def defaultToString(base: String, obj: Any): String = s"$base@${obj.hashCode.toHexString}" + in match { + case FileInput(file) => file.toString + case StringInput(o) => defaultToString("String", o) + case ReaderInput(o) => defaultToString("Reader", o) + case StreamInput(o) => defaultToString("Stream", o) + } + } + + class TransformClassSerializer extends CustomSerializer[Class[_ <: Transform]](format => ( + { case JString(s) => Class.forName(s).asInstanceOf[Class[_ <: Transform]] }, + { case x: Class[_] => JString(x.getName) } + )) + // TODO Reduce boilerplate? + class NamedSerializer extends CustomSerializer[Named](format => ( + { case JString(s) => AnnotationUtils.toNamed(s) }, + { case named: Named => JString(named.serialize) } + )) + class CircuitNameSerializer extends CustomSerializer[CircuitName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[CircuitName] }, + { case named: CircuitName => JString(named.serialize) } + )) + class ModuleNameSerializer extends CustomSerializer[ModuleName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ModuleName] }, + { case named: ModuleName => JString(named.serialize) } + )) + class ComponentNameSerializer extends CustomSerializer[ComponentName](format => ( + { case JString(s) => AnnotationUtils.toNamed(s).asInstanceOf[ComponentName] }, + { case named: ComponentName => JString(named.serialize) } + )) + + /** Construct Json formatter for annotations */ + def jsonFormat(tags: Seq[Class[_ <: Annotation]]) = { + Serialization.formats(FullTypeHints(tags.toList)).withTypeHintFieldName("class") + + new TransformClassSerializer + new NamedSerializer + new CircuitNameSerializer + + new ModuleNameSerializer + new ComponentNameSerializer + } + + /** Serialize annotations to a String for emission */ + def serialize(annos: Seq[Annotation]): String = serializeTry(annos).get + + def serializeTry(annos: Seq[Annotation]): Try[String] = { + val tags = annos.map(_.getClass).distinct + implicit val formats = jsonFormat(tags) + Try(writePretty(annos)) + } + + def deserialize(in: JsonInput): Seq[Annotation] = deserializeTry(in).get + + def deserializeTry(in: JsonInput): Try[Seq[Annotation]] = Try({ + def throwError() = throw new InvalidAnnotationFileException(prettifyJsonInput(in)) + val parsed = parse(in) + val annos = parsed match { + case JArray(objs) => objs + case _ => throwError() + } + // Gather classes so we can deserialize arbitrary Annotations + val classes = annos.map({ + case JObject(("class", JString(c)) :: tail) => c + case _ => throwError() + }).distinct + val loaded = classes.map(Class.forName(_).asInstanceOf[Class[_ <: Annotation]]) + implicit val formats = jsonFormat(loaded) + read[List[Annotation]](in) + }) +} diff --git a/src/main/scala/firrtl/annotations/Named.scala b/src/main/scala/firrtl/annotations/Named.scala index 0e365249..d3a70643 100644 --- a/src/main/scala/firrtl/annotations/Named.scala +++ b/src/main/scala/firrtl/annotations/Named.scala @@ -10,7 +10,6 @@ import AnnotationUtils.{validModuleName, validComponentName, toExp} * Named classes associate an annotation with a component in a Firrtl circuit */ sealed trait Named { - def name: String def serialize: String } diff --git a/src/main/scala/firrtl/package.scala b/src/main/scala/firrtl/package.scala new file mode 100644 index 00000000..c0b79462 --- /dev/null +++ b/src/main/scala/firrtl/package.scala @@ -0,0 +1,8 @@ + +import firrtl.AnnotationSeq +import firrtl.annotations.Annotation + +package object firrtl { + implicit def seqToAnnoSeq(xs: Seq[Annotation]) = AnnotationSeq(xs) + implicit def annoSeqToSeq(as: AnnotationSeq): Seq[Annotation] = as.underlying +} diff --git a/src/main/scala/firrtl/passes/Inline.scala b/src/main/scala/firrtl/passes/Inline.scala index 2e15f09c..0ba0c5d9 100644 --- a/src/main/scala/firrtl/passes/Inline.scala +++ b/src/main/scala/firrtl/passes/Inline.scala @@ -10,14 +10,9 @@ import firrtl.annotations._ // Datastructures import scala.collection.mutable -// Tags an annotation to be consumed by this pass -object InlineAnnotation { - def apply(target: Named): Annotation = Annotation(target, classOf[InlineInstances], "") - - def unapply(a: Annotation): Option[Named] = a match { - case Annotation(named, t, _) if t == classOf[InlineInstances] => Some(named) - case _ => None - } +/** Indicates that something should be inlined */ +case class InlineAnnotation(target: Named) extends SingleTargetAnnotation[Named] { + def duplicate(n: Named) = InlineAnnotation(n) } // Only use on legal Firrtl. Specifically, the restriction of @@ -37,18 +32,17 @@ class InlineInstances extends Transform { }.toSet, instNames) case InlineAnnotation(ModuleName(mod, cir)) => (modNames + ModuleName(mod, cir), instNames) case InlineAnnotation(ComponentName(com, mod)) => (modNames, instNames + ComponentName(com, mod)) - case _ => throw new PassException("Annotation must be InlineAnnotation") + case _ => (modNames, instNames) } } def execute(state: CircuitState): CircuitState = { // TODO Add error check for more than one annotation for inlining - // TODO Propagate other annotations - getMyAnnotations(state) match { - case Nil => CircuitState(state.circuit, state.form) - case myAnnotations => - val (modNames, instNames) = collectAnns(state.circuit, myAnnotations) - run(state.circuit, modNames, instNames, state.annotations) + val (modNames, instNames) = collectAnns(state.circuit, state.annotations) + if (modNames.nonEmpty || instNames.nonEmpty) { + run(state.circuit, modNames, instNames, state.annotations) + } else { + state } } @@ -92,7 +86,7 @@ class InlineInstances extends Transform { } - def run(c: Circuit, modsToInline: Set[ModuleName], instsToInline: Set[ComponentName], annos: Option[AnnotationMap]): CircuitState = { + def run(c: Circuit, modsToInline: Set[ModuleName], instsToInline: Set[ComponentName], annos: AnnotationSeq): CircuitState = { def getInstancesOf(c: Circuit, modules: Set[String]): Set[String] = c.modules.foldLeft(Set[String]()) { (set, d) => d match { diff --git a/src/main/scala/firrtl/passes/clocklist/ClockList.scala b/src/main/scala/firrtl/passes/clocklist/ClockList.scala index be4d99fc..fcc3cd5e 100644 --- a/src/main/scala/firrtl/passes/clocklist/ClockList.scala +++ b/src/main/scala/firrtl/passes/clocklist/ClockList.scala @@ -43,7 +43,7 @@ class ClockList(top: String, writer: Writer) extends Pass { // Inline the clock-only circuit up to the specified top module val modulesToInline = (c.modules.collect { case Module(_, n, _, _) if n != top => ModuleName(n, CircuitName(c.main)) }).toSet val inlineTransform = new InlineInstances - val inlinedCircuit = inlineTransform.run(onlyClockCircuit, modulesToInline, Set(), None).circuit + val inlinedCircuit = inlineTransform.run(onlyClockCircuit, modulesToInline, Set(), Seq()).circuit val topModule = inlinedCircuit.modules.find(_.name == top).getOrElse(throwInternalError(Some("no top module"))) // Build a hashmap of connections to use for getOrigins diff --git a/src/main/scala/firrtl/passes/clocklist/ClockListTransform.scala b/src/main/scala/firrtl/passes/clocklist/ClockListTransform.scala index 24f25525..6c7b2e18 100644 --- a/src/main/scala/firrtl/passes/clocklist/ClockListTransform.scala +++ b/src/main/scala/firrtl/passes/clocklist/ClockListTransform.scala @@ -15,8 +15,13 @@ import memlib.AnalysisUtils._ import memlib._ import Mappers._ +case class ClockListAnnotation(target: ModuleName, outputConfig: String) extends + SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = ClockListAnnotation(n, outputConfig) +} + object ClockListAnnotation { - def apply(t: String): Annotation = { + def parse(t: String): ClockListAnnotation = { val usage = """ [Optional] ClockList List which signal drives each clock of every descendent of specified module @@ -45,16 +50,7 @@ Usage: case None => } val target = ModuleName(passModule, CircuitName(passCircuit)) - Annotation(target, classOf[ClockListTransform], outputConfig) - } - - def apply(target: ModuleName, outputConfig: String): Annotation = - Annotation(target, classOf[ClockListTransform], outputConfig) - - def unapply(a: Annotation): Option[(ModuleName, String)] = a match { - case Annotation(ModuleName(m, c), t, outputConfig) if t == classOf[ClockListTransform] => - Some((ModuleName(m, c), outputConfig)) - case _ => None + ClockListAnnotation(target, outputConfig) } } @@ -63,13 +59,16 @@ class ClockListTransform extends Transform { def outputForm = LowForm def passSeq(top: String, writer: Writer): Seq[Pass] = Seq(new ClockList(top, writer)) - def execute(state: CircuitState): CircuitState = getMyAnnotations(state) match { - case Seq(ClockListAnnotation(ModuleName(top, CircuitName(state.circuit.main)), out)) => - val outputFile = new PrintWriter(out) - val newC = (new ClockList(top, outputFile)).run(state.circuit) - outputFile.close() - CircuitState(newC, state.form, state.annotations) - case Nil => state - case seq => error(s"Found illegal clock list annotation(s): $seq") + def execute(state: CircuitState): CircuitState = { + val annos = state.annotations.collect { case a: ClockListAnnotation => a } + annos match { + case Seq(ClockListAnnotation(ModuleName(top, CircuitName(state.circuit.main)), out)) => + val outputFile = new PrintWriter(out) + val newC = (new ClockList(top, outputFile)).run(state.circuit) + outputFile.close() + CircuitState(newC, state.form, state.annotations) + case Nil => state + case seq => error(s"Found illegal clock list annotation(s): $seq") + } } } diff --git a/src/main/scala/firrtl/passes/memlib/DecorateMems.scala b/src/main/scala/firrtl/passes/memlib/DecorateMems.scala index ad3616ad..c5302a38 100644 --- a/src/main/scala/firrtl/passes/memlib/DecorateMems.scala +++ b/src/main/scala/firrtl/passes/memlib/DecorateMems.scala @@ -16,11 +16,11 @@ class CreateMemoryAnnotations(reader: Option[YamlFileReader]) extends Transform import CustomYAMLProtocol._ val configs = r.parse[Config] val cN = CircuitName(state.circuit.main) - val oldAnnos = state.annotations.getOrElse(AnnotationMap(Seq.empty)).annotations + val oldAnnos = state.annotations val (as, pins) = configs.foldLeft((oldAnnos, Seq.empty[String])) { case ((annos, pins), config) => val source = SourceAnnotation(ComponentName(config.source.name, ModuleName(config.source.module, cN)), config.pin.name) (annos, pins :+ config.pin.name) } - state.copy(annotations = Some(AnnotationMap(as :+ PinAnnotation(cN, pins.toSeq)))) + state.copy(annotations = PinAnnotation(pins.toSeq) +: as) } } diff --git a/src/main/scala/firrtl/passes/memlib/InferReadWrite.scala b/src/main/scala/firrtl/passes/memlib/InferReadWrite.scala index 73fec1ee..661d6df4 100644 --- a/src/main/scala/firrtl/passes/memlib/InferReadWrite.scala +++ b/src/main/scala/firrtl/passes/memlib/InferReadWrite.scala @@ -13,15 +13,7 @@ import firrtl.passes.memlib.AnalysisUtils.{Connects, getConnects, getOrigin} import WrappedExpression.weq import annotations._ -object InferReadWriteAnnotation { - def apply(t: String) = Annotation(CircuitName(t), classOf[InferReadWrite], "") - def apply(target: CircuitName) = Annotation(target, classOf[InferReadWrite], "") - def unapply(a: Annotation): Option[(CircuitName)] = a match { - case Annotation(CircuitName(t), transform, "") if transform == classOf[InferReadWrite] => - Some(CircuitName(t)) - case _ => None - } -} +case object InferReadWriteAnnotation extends NoTargetAnnotation // This pass examine the enable signals of the read & write ports of memories // whose readLatency is greater than 1 (usually SeqMem in Chisel). @@ -159,10 +151,13 @@ class InferReadWrite extends Transform with SeqTransformBased { ResolveKinds, ResolveGenders ) - def execute(state: CircuitState): CircuitState = getMyAnnotations(state) match { - case Nil => state - case Seq(InferReadWriteAnnotation(CircuitName(state.circuit.main))) => + def execute(state: CircuitState): CircuitState = { + val runTransform = state.annotations.contains(InferReadWriteAnnotation) + if (runTransform) { val ret = runTransforms(state) CircuitState(ret.circuit, outputForm, ret.annotations, ret.renames) + } else { + state + } } } diff --git a/src/main/scala/firrtl/passes/memlib/ReplaceMemMacros.scala b/src/main/scala/firrtl/passes/memlib/ReplaceMemMacros.scala index bf0612ac..5ac9e63e 100644 --- a/src/main/scala/firrtl/passes/memlib/ReplaceMemMacros.scala +++ b/src/main/scala/firrtl/passes/memlib/ReplaceMemMacros.scala @@ -14,19 +14,8 @@ import firrtl.annotations._ import wiring._ -/** Annotates the name of the pin to add for WiringTransform - */ -object PinAnnotation { - def apply(target: CircuitName, pins: Seq[String]): Annotation = { - Annotation(target, classOf[ReplaceMemMacros], s"pins:${pins.mkString(" ")}") - } - private val matcher = "pins:(.*)".r - def unapply(a: Annotation): Option[(CircuitName, Seq[String])] = a match { - case Annotation(CircuitName(c), _, matcher(rest)) => - Some((CircuitName(c), rest.split(" "))) - case _ => None - } -} +/** Annotates the name of the pins to add for WiringTransform */ +case class PinAnnotation(pins: Seq[String]) extends NoTargetAnnotation /** Replace DefAnnotatedMemory with memory blackbox + wrapper + conf file. * This will not generate wmask ports if not needed. @@ -221,19 +210,17 @@ class ReplaceMemMacros(writer: ConfWriter) extends Transform { val modules = c.modules map updateMemMods(namespace, nameMap, memMods) // print conf writer.serialize() - val pins = getMyAnnotations(state) match { - case Nil => Nil - case Seq(PinAnnotation(CircuitName(c), pins)) => pins + val pannos = state.annotations.collect { case a: PinAnnotation => a } + val pins = pannos match { + case Seq() => Nil + case Seq(PinAnnotation(pins)) => pins case _ => throwInternalError(Some(s"execute: getMyAnnotations - ${getMyAnnotations(state)}")) } - val annos = (pins.foldLeft(Seq[Annotation]()) { (seq, pin) => - seq ++ memMods.collect { - case m: ExtModule => SinkAnnotation(ModuleName(m.name, CircuitName(c.main)), pin) + val annos = pins.foldLeft(Seq[Annotation]()) { (seq, pin) => + seq ++ memMods.collect { + case m: ExtModule => SinkAnnotation(ModuleName(m.name, CircuitName(c.main)), pin) } - }) ++ (state.annotations match { - case None => Seq.empty - case Some(a) => a.annotations - }) - CircuitState(c.copy(modules = modules ++ memMods), inputForm, Some(AnnotationMap(annos))) + } ++ state.annotations + CircuitState(c.copy(modules = modules ++ memMods), inputForm, annos) } } diff --git a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala index 8cbf9da7..311813db 100644 --- a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala +++ b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala @@ -64,13 +64,15 @@ class ConfWriter(filename: String) { } } +case class ReplSeqMemAnnotation(inputFileName: String, outputConfig: String) extends NoTargetAnnotation + object ReplSeqMemAnnotation { - def apply(t: String): Annotation = { + def parse(t: String): ReplSeqMemAnnotation = { val usage = """ [Optional] ReplSeqMem Pass to replace sequential memories with blackboxes + configuration file -Usage: +Usage: --replSeqMem -c:<circuit>:-i:<filename>:-o:<filename> *** Note: sub-arguments to --replSeqMem should be delimited by : and not white space! @@ -80,30 +82,15 @@ Required Arguments: Optional Arguments: -i<filename> Specify the input configuration file (for additional optimizations) -""" +""" val passOptions = PassConfigUtil.getPassOptions(t, usage) val outputConfig = passOptions.getOrElse( - OutputConfigFileName, + OutputConfigFileName, error("No output config file provided for ReplSeqMem!" + usage) ) val inputFileName = PassConfigUtil.getPassOptions(t).getOrElse(InputConfigFileName, "") - val passCircuit = passOptions.getOrElse( - PassCircuitName, - error("No circuit name specified for ReplSeqMem!" + usage) - ) - val target = CircuitName(passCircuit) - Annotation(target, classOf[ReplSeqMem], s"$inputFileName $outputConfig") - } - - def apply(target: CircuitName, inputFileName: String, outputConfig: String): Annotation = - Annotation(target, classOf[ReplSeqMem], s"$inputFileName $outputConfig") - - private val matcher = "([^ ]*) ([^ ]+)".r - def unapply(a: Annotation): Option[(CircuitName, String, String)] = a match { - case Annotation(CircuitName(c), t, matcher(inputFileName, outputConfig)) if t == classOf[ReplSeqMem] => - Some((CircuitName(c), inputFileName, outputConfig)) - case _ => None + ReplSeqMemAnnotation(inputFileName, outputConfig) } } @@ -135,12 +122,13 @@ class ReplSeqMem extends Transform { new SimpleMidTransform(ResolveKinds), new SimpleMidTransform(ResolveGenders)) - def execute(state: CircuitState): CircuitState = getMyAnnotations(state) match { - case Nil => state // Do nothing if there are no annotations - case p => (p.collectFirst { case a if (a.target == CircuitName(state.circuit.main)) => a }) match { - case Some(ReplSeqMemAnnotation(target, inputFileName, outputConfig)) => + def execute(state: CircuitState): CircuitState = { + val annos = state.annotations.collect { case a: ReplSeqMemAnnotation => a } + annos match { + case Nil => state // Do nothing if there are no annotations + case Seq(ReplSeqMemAnnotation(inputFileName, outputConfig)) => val inConfigFile = { - if (inputFileName.isEmpty) None + if (inputFileName.isEmpty) None else if (new File(inputFileName).exists) Some(new YamlFileReader(inputFileName)) else error("Input configuration file does not exist!") } diff --git a/src/main/scala/firrtl/passes/memlib/ResolveMemoryReference.scala b/src/main/scala/firrtl/passes/memlib/ResolveMemoryReference.scala index e132e369..d195ea55 100644 --- a/src/main/scala/firrtl/passes/memlib/ResolveMemoryReference.scala +++ b/src/main/scala/firrtl/passes/memlib/ResolveMemoryReference.scala @@ -8,15 +8,9 @@ import AnalysisUtils.eqMems import firrtl.Mappers._ import firrtl.annotations._ -/** A component, e.g. register etc. Must be declared only once under the TopAnnotation - */ -object NoDedupMemAnnotation { - def apply(target: ComponentName): Annotation = Annotation(target, classOf[ResolveMemoryReference], s"nodedupmem!") - - def unapply(a: Annotation): Option[ComponentName] = a match { - case Annotation(ComponentName(n, mn), _, "nodedupmem!") => Some(ComponentName(n, mn)) - case _ => None - } +/** A component, e.g. register etc. Must be declared only once under the TopAnnotation */ +case class NoDedupMemAnnotation(target: ComponentName) extends SingleTargetAnnotation[ComponentName] { + def duplicate(n: ComponentName) = NoDedupMemAnnotation(n) } /** Resolves annotation ref to memories that exactly match (except name) another memory @@ -46,10 +40,8 @@ class ResolveMemoryReference extends Transform { c copy (modules = c.modules map (m => m map updateMemStmts(m.name, uniqueMems, noDeDupeMems))) } def execute(state: CircuitState): CircuitState = { - val noDedups = getMyAnnotations(state) match { - case Nil => Seq.empty - case annos => - annos.collect { case NoDedupMemAnnotation(ComponentName(cn, _)) => cn } + val noDedups = state.annotations.collect { + case NoDedupMemAnnotation(ComponentName(cn, _)) => cn } state.copy(circuit=run(state.circuit, noDedups)) } diff --git a/src/main/scala/firrtl/passes/wiring/WiringTransform.scala b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala index 01e6f83a..9a82f8a0 100644 --- a/src/main/scala/firrtl/passes/wiring/WiringTransform.scala +++ b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala @@ -14,32 +14,16 @@ import WiringUtils._ /** A class for all exceptions originating from firrtl.passes.wiring */ case class WiringException(msg: String) extends PassException(msg) -/** An extractor of annotated source components */ -object SourceAnnotation { - def apply(target: ComponentName, pin: String): Annotation = - Annotation(target, classOf[WiringTransform], s"source $pin") - - private val matcher = "source (.+)".r - def unapply(a: Annotation): Option[(ComponentName, String)] = a match { - case Annotation(ComponentName(n, m), _, matcher(pin)) => - Some((ComponentName(n, m), pin)) - case _ => None - } +/** A component, e.g. register etc. Must be declared only once under the TopAnnotation */ +case class SourceAnnotation(target: ComponentName, pin: String) extends + SingleTargetAnnotation[ComponentName] { + def duplicate(n: ComponentName) = this.copy(target = n) } -/** An extractor of annotation sink components or modules */ -object SinkAnnotation { - def apply(target: Named, pin: String): Annotation = - Annotation(target, classOf[WiringTransform], s"sink $pin") - - private val matcher = "sink (.+)".r - def unapply(a: Annotation): Option[(Named, String)] = a match { - case Annotation(ModuleName(n, c), _, matcher(pin)) => - Some((ModuleName(n, c), pin)) - case Annotation(ComponentName(n, m), _, matcher(pin)) => - Some((ComponentName(n, m), pin)) - case _ => None - } +/** A module, e.g. ExtModule etc., that should add the input pin */ +case class SinkAnnotation(target: Named, pin: String) extends + SingleTargetAnnotation[Named] { + def duplicate(n: Named) = this.copy(target = n) } /** Wires a Module's Source Component to one or more Sink @@ -64,26 +48,30 @@ class WiringTransform extends Transform { new Wiring(w), ToWorkingIR ) - - def execute(state: CircuitState): CircuitState = getMyAnnotations(state) match { - case Nil => state - case p => - val sinks = mutable.HashMap[String, Seq[Named]]() - val sources = mutable.HashMap[String, ComponentName]() - p.foreach { - case SinkAnnotation(m, pin) => - sinks(pin) = sinks.getOrElse(pin, Seq.empty) :+ m - case SourceAnnotation(c, pin) => - sources(pin) = c - } - (sources.size, sinks.size) match { - case (0, p) => state - case (s, p) if (p > 0) => - val wis = sources.foldLeft(Seq[WiringInfo]()) { case (seq, (pin, source)) => - seq :+ WiringInfo(source, sinks(pin), pin) - } - transforms(wis).foldLeft(state) { (in, xform) => xform.runTransform(in) } - case _ => error("Wrong number of sources or sinks!") - } + def execute(state: CircuitState): CircuitState = { + val annos = state.annotations.collect { + case a @ (_: SinkAnnotation | _: SourceAnnotation) => a + } + annos match { + case Seq() => state + case p => + val sinks = mutable.HashMap[String, Seq[Named]]() + val sources = mutable.HashMap[String, ComponentName]() + p.foreach { + case SinkAnnotation(m, pin) => + sinks(pin) = sinks.getOrElse(pin, Seq.empty) :+ m + case SourceAnnotation(c, pin) => + sources(pin) = c + } + (sources.size, sinks.size) match { + case (0, p) => state + case (s, p) if (p > 0) => + val wis = sources.foldLeft(Seq[WiringInfo]()) { case (seq, (pin, source)) => + seq :+ WiringInfo(source, sinks(pin), pin) + } + transforms(wis).foldLeft(state) { (in, xform) => xform.runTransform(in) } + case _ => error("Wrong number of sources or sinks!") + } + } } } diff --git a/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala b/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala index 80a8df55..aed9b1f8 100644 --- a/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala +++ b/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala @@ -5,89 +5,59 @@ package firrtl.transforms import java.io.{File, FileNotFoundException, FileOutputStream, PrintWriter} import firrtl._ -import firrtl.annotations.{Annotation, ModuleName} +import firrtl.Utils.throwInternalError +import firrtl.annotations._ import scala.collection.mutable.ArrayBuffer +sealed trait BlackBoxHelperAnno extends Annotation -trait BlackBoxSource { - def serialize: String - def name: String +case class BlackBoxTargetDirAnno(targetDir: String) extends BlackBoxHelperAnno + with NoTargetAnnotation { + override def serialize: String = s"targetDir\n$targetDir" } -object BlackBoxSource { - val MaxFields = 3 - - def parse(s: String): Option[BlackBoxSource] = { - s.split("\n", MaxFields).toList match { - case "resource" :: id :: _ => Some(BlackBoxResource(id)) - case "inline" :: name :: text :: _ => Some(BlackBoxInline(name, text)) - case "targetDir" :: targetDir :: _ => Some(BlackBoxTargetDir(targetDir)) - case _ => throw new FIRRTLException(s"Error: Bad BlackBox annotations $s") - } - } -} - -case class BlackBoxTargetDir(targetDir: String) extends BlackBoxSource { - def serialize: String = s"targetDir\n$targetDir" - def name: String = targetDir -} - -case class BlackBoxResource(resourceId: String) extends BlackBoxSource { - def serialize: String = s"resource\n$resourceId" - def name: String = resourceId.split("/").last -} - -case class BlackBoxInline(name: String, text: String) extends BlackBoxSource { - def serialize: String = s"inline\n$name\n$text" +case class BlackBoxResourceAnno(target: ModuleName, resourceId: String) extends BlackBoxHelperAnno + with SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = this.copy(target = n) + override def serialize: String = s"resource\n$resourceId" } -object BlackBoxSourceAnnotation { - def apply(targetDir: ModuleName, value: String): Annotation = { - assert(BlackBoxSource.parse(value).isDefined) - Annotation(targetDir, classOf[BlackBoxSourceHelper], value) - } - - def unapply(a: Annotation): Option[(ModuleName, BlackBoxSource)] = a match { - case Annotation(ModuleName(n, c), _, text) => Some((ModuleName(n, c), BlackBoxSource.parse(text).get)) - case _ => None - } +case class BlackBoxInlineAnno(target: ModuleName, name: String, text: String) extends BlackBoxHelperAnno + with SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = this.copy(target = n) + override def serialize: String = s"inline\n$name\n$text" } -/** - * This transform handles the moving of verilator source for black boxes into the +/** Handle source for Verilog ExtModules (BlackBoxes) + * + * This transform handles the moving of Verilog source for black boxes into the * target directory so that it can be accessed by verilator or other backend compilers * While parsing it's annotations it looks for a BlackBoxTargetDir annotation that - * will set the directory where the verilog will be written. This annotation is typically be + * will set the directory where the Verilog will be written. This annotation is typically be * set by the execution harness, or directly in the tests */ class BlackBoxSourceHelper extends firrtl.Transform { - private var targetDir: File = new File(".") - private val fileList = new ArrayBuffer[String] + private val DefaultTargetDir = new File(".") override def inputForm: CircuitForm = LowForm override def outputForm: CircuitForm = LowForm - /** - * parse the annotations and convert the generic annotations to specific information - * required to find the verilog - * @note Side effect is that while converting a magic target dir annotation is found and sets the target + /** Collect BlackBoxHelperAnnos and and find the target dir if specified * @param annos a list of generic annotations for this transform - * @return + * @return BlackBoxHelperAnnos and target directory */ - def getSources(annos: Seq[Annotation]): Seq[BlackBoxSource] = { - annos.flatMap { anno => BlackBoxSource.parse(anno.value) } - .flatMap { - case BlackBoxTargetDir(dest) => - targetDir = new File(dest) - if(! targetDir.exists()) { FileUtils.makeDirectory(targetDir.getAbsolutePath) } - None - case b: BlackBoxSource => Some(b) - case _ => None + def collectAnnos(annos: Seq[Annotation]): (Set[BlackBoxHelperAnno], File) = + annos.foldLeft((Set.empty[BlackBoxHelperAnno], DefaultTargetDir)) { + case ((acc, tdir), anno) => anno match { + case BlackBoxTargetDirAnno(dir) => + val targetDir = new File(dir) + if (!targetDir.exists()) { FileUtils.makeDirectory(targetDir.getAbsolutePath) } + (acc, targetDir) + case a: BlackBoxHelperAnno => (acc + a, tdir) + case _ => (acc, tdir) } - .sortBy(a => a.name) - .distinct - } + } /** * write the verilog source for each annotation to the target directory @@ -96,31 +66,28 @@ class BlackBoxSourceHelper extends firrtl.Transform { * @return A transformed Firrtl AST */ override def execute(state: CircuitState): CircuitState = { - val resultState = getMyAnnotations(state) match { - case Nil => state - case myAnnotations => - val sources = getSources(myAnnotations) - sources.foreach { - case BlackBoxResource(resourceId) => - val name = resourceId.split("/").last - val outFile = new File(targetDir, name) - BlackBoxSourceHelper.copyResourceToFile(resourceId,outFile) - fileList += outFile.getAbsolutePath - case BlackBoxInline(name, text) => - val outFile = new File(targetDir, name) - val writer = new PrintWriter(outFile) - writer.write(text) - writer.close() - fileList += outFile.getAbsolutePath - case _ => - } - state + val (annos, targetDir) = collectAnnos(state.annotations) + val fileList = annos.foldLeft(List.empty[String]) { + case (fileList, anno) => anno match { + case BlackBoxResourceAnno(_, resourceId) => + val name = resourceId.split("/").last + val outFile = new File(targetDir, name) + BlackBoxSourceHelper.copyResourceToFile(resourceId,outFile) + outFile.getAbsolutePath +: fileList + case BlackBoxInlineAnno(_, name, text) => + val outFile = new File(targetDir, name) + val writer = new PrintWriter(outFile) + writer.write(text) + writer.close() + outFile.getAbsolutePath +: fileList + case _ => throwInternalError() + } } // If we have BlackBoxes, generate the helper file. // If we don't, make sure it doesn't exist or we'll confuse downstream processing // that triggers behavior on the existence of the file val helperFile = new File(targetDir, BlackBoxSourceHelper.FileListName) - if(fileList.nonEmpty) { + if (fileList.nonEmpty) { val writer = new PrintWriter(helperFile) writer.write(fileList.map { fileName => s"-v $fileName" }.mkString("\n")) writer.close() @@ -128,7 +95,7 @@ class BlackBoxSourceHelper extends firrtl.Transform { helperFile.delete() } - resultState + state } } diff --git a/src/main/scala/firrtl/transforms/CheckCombLoops.scala b/src/main/scala/firrtl/transforms/CheckCombLoops.scala index 98d6c3d1..6bd62cfa 100644 --- a/src/main/scala/firrtl/transforms/CheckCombLoops.scala +++ b/src/main/scala/firrtl/transforms/CheckCombLoops.scala @@ -22,15 +22,7 @@ object CheckCombLoops { } -object DontCheckCombLoopsAnnotation { - private val marker = "DontCheckCombLoops!" - private val transform = classOf[CheckCombLoops] - def apply(): Annotation = Annotation(CircuitTopName, transform, marker) - def unapply(a: Annotation): Boolean = a match { - case Annotation(_, targetXform, value) if targetXform == transform && value == marker => true - case _ => false - } -} +case object DontCheckCombLoopsAnnotation extends NoTargetAnnotation /** Finds and detects combinational logic loops in a circuit, if any * exist. Returns the input circuit with no modifications. @@ -220,9 +212,7 @@ class CheckCombLoops extends Transform { } def execute(state: CircuitState): CircuitState = { - val dontRun = getMyAnnotations(state).collectFirst { - case DontCheckCombLoopsAnnotation() => true - }.getOrElse(false) + val dontRun = state.annotations.contains(DontCheckCombLoopsAnnotation) if (dontRun) { logger.warn("Skipping Combinational Loop Detection") state diff --git a/src/main/scala/firrtl/transforms/ConstantPropagation.scala b/src/main/scala/firrtl/transforms/ConstantPropagation.scala index 04ad2cb2..57b88890 100644 --- a/src/main/scala/firrtl/transforms/ConstantPropagation.scala +++ b/src/main/scala/firrtl/transforms/ConstantPropagation.scala @@ -485,11 +485,8 @@ class ConstantPropagation extends Transform { } def execute(state: CircuitState): CircuitState = { - val dontTouches: Seq[(String, String)] = state.annotations match { - case Some(aMap) => aMap.annotations.collect { - case DontTouchAnnotation(ComponentName(c, ModuleName(m, _))) => m -> c - } - case None => Seq.empty + val dontTouches: Seq[(String, String)] = state.annotations.collect { + case DontTouchAnnotation(ComponentName(c, ModuleName(m, _))) => m -> c } // Map from module name to component names val dontTouchMap: Map[String, Set[String]] = diff --git a/src/main/scala/firrtl/transforms/DeadCodeElimination.scala b/src/main/scala/firrtl/transforms/DeadCodeElimination.scala index 054705c0..8b6b5c85 100644 --- a/src/main/scala/firrtl/transforms/DeadCodeElimination.scala +++ b/src/main/scala/firrtl/transforms/DeadCodeElimination.scala @@ -320,23 +320,14 @@ class DeadCodeElimination extends Transform { } def execute(state: CircuitState): CircuitState = { - val (dontTouches: Seq[LogicNode], doTouchExtMods: Seq[String], noDCE: Option[Boolean]) = - state.annotations match { - case Some(aMap) => - // TODO Do with single walk over annotations - val dontTouches = aMap.annotations.collect { - case DontTouchAnnotation(component) => LogicNode(component) - } - val optExtMods = aMap.annotations.collect { - case OptimizableExtModuleAnnotation(ModuleName(name, _)) => name - } - val noDCE = aMap.annotations.collectFirst { - case NoDCEAnnotation() => true - } - (dontTouches, optExtMods, noDCE) - case None => (Seq.empty, Seq.empty, None) - } - if (noDCE.getOrElse(false)) { + val dontTouches: Seq[LogicNode] = state.annotations.collect { + case DontTouchAnnotation(component) => LogicNode(component) + } + val doTouchExtMods: Seq[String] = state.annotations.collect { + case OptimizableExtModuleAnnotation(ModuleName(name, _)) => name + } + val noDCE = state.annotations.contains(NoDCEAnnotation) + if (noDCE) { logger.info("Skipping DCE") state } else { diff --git a/src/main/scala/firrtl/transforms/Dedup.scala b/src/main/scala/firrtl/transforms/Dedup.scala index 717481b9..f22415f0 100644 --- a/src/main/scala/firrtl/transforms/Dedup.scala +++ b/src/main/scala/firrtl/transforms/Dedup.scala @@ -14,16 +14,10 @@ import scala.collection.mutable /** A component, e.g. register etc. Must be declared only once under the TopAnnotation */ -object NoDedupAnnotation { - def apply(target: ModuleName): Annotation = Annotation(target, classOf[DedupModules], s"nodedup!") - - def unapply(a: Annotation): Option[ModuleName] = a match { - case Annotation(ModuleName(n, c), _, "nodedup!") => Some(ModuleName(n, c)) - case _ => None - } +case class NoDedupAnnotation(target: ModuleName) extends SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = NoDedupAnnotation(n) } - // Only use on legal Firrtl. Specifically, the restriction of // instance loops must have been checked, or else this pass can // infinitely recurse @@ -150,7 +144,7 @@ class DedupModules extends Transform { } def execute(state: CircuitState): CircuitState = { - val noDedups = getMyAnnotations(state).collect { case NoDedupAnnotation(ModuleName(m, c)) => m } + val noDedups = state.annotations.collect { case NoDedupAnnotation(ModuleName(m, c)) => m } val (newC, renameMap) = run(state.circuit, noDedups) state.copy(circuit = newC, renames = Some(renameMap)) } diff --git a/src/main/scala/firrtl/transforms/Flatten.scala b/src/main/scala/firrtl/transforms/Flatten.scala index cc40c569..6654667a 100644 --- a/src/main/scala/firrtl/transforms/Flatten.scala +++ b/src/main/scala/firrtl/transforms/Flatten.scala @@ -10,13 +10,8 @@ import scala.collection.mutable import firrtl.passes.{InlineInstances,PassException} /** Tags an annotation to be consumed by this transform */ -object FlattenAnnotation { - def apply(target: Named): Annotation = Annotation(target, classOf[Flatten], "") - - def unapply(a: Annotation): Option[Named] = a match { - case Annotation(named, t, _) if t == classOf[Flatten] => Some(named) - case _ => None - } +case class FlattenAnnotation(target: Named) extends SingleTargetAnnotation[Named] { + def duplicate(n: Named) = FlattenAnnotation(n) } /** @@ -39,7 +34,7 @@ class Flatten extends Transform { }.toSet, instNames) case FlattenAnnotation(ModuleName(mod, cir)) => (modNames + ModuleName(mod, cir), instNames) case FlattenAnnotation(ComponentName(com, mod)) => (modNames, instNames + ComponentName(com, mod)) - case _ => throw new PassException("Annotation must be InlineDeepAnnotation") + case _ => throw new PassException("Annotation must be a FlattenAnnotation") } } @@ -107,7 +102,8 @@ class Flatten extends Transform { } override def execute(state: CircuitState): CircuitState = { - getMyAnnotations(state) match { + val annos = state.annotations.collect { case a @ FlattenAnnotation(_) => a } + annos match { case Nil => CircuitState(state.circuit, state.form) case myAnnotations => val c = state.circuit diff --git a/src/main/scala/firrtl/transforms/OptimizationAnnotations.scala b/src/main/scala/firrtl/transforms/OptimizationAnnotations.scala index 2336710a..42b45813 100644 --- a/src/main/scala/firrtl/transforms/OptimizationAnnotations.scala +++ b/src/main/scala/firrtl/transforms/OptimizationAnnotations.scala @@ -6,29 +6,17 @@ import firrtl.annotations._ import firrtl.passes.PassException /** Indicate that DCE should not be run */ -object NoDCEAnnotation { - val marker = "noDCE!" - val transform = classOf[DeadCodeElimination] - def apply(): Annotation = Annotation(CircuitTopName, transform, marker) - def unapply(a: Annotation): Boolean = a match { - case Annotation(_, targetXform, value) if targetXform == transform && value == marker => true - case _ => false - } -} +case object NoDCEAnnotation extends NoTargetAnnotation /** A component that should be preserved * * DCE treats the component as a top-level sink of the circuit */ -object DontTouchAnnotation { - private val marker = "DONTtouch!" - def apply(target: ComponentName): Annotation = Annotation(target, classOf[Transform], marker) - - def unapply(a: Annotation): Option[ComponentName] = a match { - case Annotation(component: ComponentName, _, value) if value == marker => Some(component) - case _ => None - } +case class DontTouchAnnotation(target: ComponentName) extends SingleTargetAnnotation[ComponentName] { + def duplicate(n: ComponentName) = this.copy(n) +} +object DontTouchAnnotation { class DontTouchNotFoundException(module: String, component: String) extends PassException( s"Component marked DONT Touch ($module.$component) not found!\n" + "Perhaps it is an aggregate type? Currently only leaf components are supported.\n" + @@ -48,12 +36,7 @@ object DontTouchAnnotation { * * @note Unlike [[DontTouchAnnotation]], we don't care if the annotation is deleted */ -object OptimizableExtModuleAnnotation { - private val marker = "optimizableExtModule!" - def apply(target: ModuleName): Annotation = Annotation(target, classOf[Transform], marker) - - def unapply(a: Annotation): Option[ModuleName] = a match { - case Annotation(component: ModuleName, _, value) if value == marker => Some(component) - case _ => None - } +case class OptimizableExtModuleAnnotation(target: ModuleName) extends + SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = this.copy(n) } diff --git a/src/test/resources/annotations/InvalidLegacyAnnotations.anno b/src/test/resources/annotations/InvalidLegacyAnnotations.anno new file mode 100644 index 00000000..75bb3b96 --- /dev/null +++ b/src/test/resources/annotations/InvalidLegacyAnnotations.anno @@ -0,0 +1,26 @@ +- targetString: CircuitTop + transformClass: firrtl.VerilogEmitter + value: emitCircuit +- targetString: CircuitTop + transformClass: firrtl.VerilogEmitter + value: emitAllModules +- targetString: CircuitTop + transformClass: firrtl.Transform + value: emittedFirrtlCircuit:0 +- targetString: CircuitTop + transformClass: firrtl.Transform + value: emittedVerilogCircuit:0 +- targetString: CircuitTop + transformClass: firrtl.Transform + value: emittedFirrtlModule:0 +- targetString: CircuitTop + transformClass: firrtl.Transform + value: emittedVerilogModule:0 +- targetString: foo + transformClass: firrtl.Transform + value: | + DELETED by DeadCodeElimination + targetString: foo + transformClass: firrtl.passes.InlineInstances + value: '' + diff --git a/src/test/resources/annotations/LegacyAnnotations.anno b/src/test/resources/annotations/LegacyAnnotations.anno new file mode 100644 index 00000000..395fa56d --- /dev/null +++ b/src/test/resources/annotations/LegacyAnnotations.anno @@ -0,0 +1,50 @@ +- targetString: foo + transformClass: firrtl.passes.InlineInstances + value: '' +- targetString: foo.bar + transformClass: firrtl.passes.clocklist.ClockListTransform + value: output +- targetString: foo + transformClass: firrtl.passes.memlib.InferReadWrite + value: '' +- targetString: foo + transformClass: firrtl.passes.memlib.ReplSeqMem + value: input output +- targetString: foo.bar.x + transformClass: firrtl.passes.memlib.ResolveMemoryReference + value: nodedupmem! +- targetString: foo.bar + transformClass: firrtl.transforms.DedupModules + value: nodedup! +- targetString: foo.bar.x + transformClass: firrtl.passes.wiring.WiringTransform + value: source pin +- targetString: foo.bar.x + transformClass: firrtl.passes.wiring.WiringTransform + value: sink pin +- targetString: foo.bar + transformClass: firrtl.transforms.BlackBoxSourceHelper + value: |- + resource + resource +- targetString: foo.bar + transformClass: firrtl.transforms.BlackBoxSourceHelper + value: |- + inline + name + text +- targetString: foo.bar + transformClass: firrtl.transforms.BlackBoxSourceHelper + value: |- + targetDir + targetdir +- targetString: CircuitTop + transformClass: firrtl.transforms.DeadCodeElimination + value: noDCE! +- targetString: foo.bar.x + transformClass: firrtl.Transform + value: DONTtouch! +- targetString: foo.bar + transformClass: firrtl.Transform + value: optimizableExtModule! + diff --git a/src/test/resources/annotations/SampleAnnotations.anno.json b/src/test/resources/annotations/SampleAnnotations.anno.json new file mode 100644 index 00000000..e4d912a2 --- /dev/null +++ b/src/test/resources/annotations/SampleAnnotations.anno.json @@ -0,0 +1,39 @@ +[ + { + "class":"firrtl.passes.InlineAnnotation", + "target":"Top.Foo" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"Top.Bar" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"Top.Foo.x" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"Top.Foo.y" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"Top" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"OtherTop" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"OtherTop.Foo.x" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"OtherTop.Bar" + }, + { + "class":"firrtl.passes.InlineAnnotation", + "target":"OtherTop.Foo.y" + } +] + diff --git a/src/test/scala/firrtlTests/AnnotationTests.scala b/src/test/scala/firrtlTests/AnnotationTests.scala index 7b7e7839..85814713 100644 --- a/src/test/scala/firrtlTests/AnnotationTests.scala +++ b/src/test/scala/firrtlTests/AnnotationTests.scala @@ -25,32 +25,22 @@ trait AnnotationSpec extends LowTransformSpec { // Check if Annotation Exception is thrown override def failingexecute(input: String, annotations: Seq[Annotation]): Exception = { intercept[AnnotationException] { - compile(CircuitState(parse(input), ChirrtlForm, Some(AnnotationMap(annotations))), Seq.empty) + compile(CircuitState(parse(input), ChirrtlForm, annotations), Seq.empty) } } def execute(input: String, check: Annotation, annotations: Seq[Annotation]): Unit = { - val cr = compile(CircuitState(parse(input), ChirrtlForm, Some(AnnotationMap(annotations))), Seq.empty) - cr.annotations.get.annotations should contain (check) + val cr = compile(CircuitState(parse(input), ChirrtlForm, annotations), Seq.empty) + cr.annotations.toSeq should contain (check) } } +// Abstract but with lots of tests defined so that we can use the same tests +// for Legacy and newer Annotations +abstract class AnnotationTests extends AnnotationSpec with Matchers { + def anno(s: String, value: String ="this is a value", mod: String = "Top"): Annotation + def manno(mod: String): Annotation -/** - * Tests for Annotation Permissibility and Tenacity - * - * WARNING(izraelevitz): Not a complete suite of tests, requires the LowerTypes - * pass and ConstProp pass to correctly populate its RenameMap before Strict, Rigid, Firm, - * Unstable, Fickle, and Insistent can be tested. - */ -class AnnotationTests extends AnnotationSpec with Matchers { - def getAMap(a: Annotation): Option[AnnotationMap] = Some(AnnotationMap(Seq(a))) - def getAMap(as: Seq[Annotation]): Option[AnnotationMap] = Some(AnnotationMap(as)) - def anno(s: String, value: String ="this is a value", mod: String = "Top"): Annotation = - Annotation(ComponentName(s, ModuleName(mod, CircuitName("Top"))), classOf[Transform], value) - def manno(mod: String): Annotation = - Annotation(ModuleName(mod, CircuitName("Top")), classOf[Transform], "some value") - - "Loose and Sticky annotation on a node" should "pass through" in { + "Annotation on a node" should "pass through" in { val input: String = """circuit Top : | module Top : @@ -61,71 +51,6 @@ class AnnotationTests extends AnnotationSpec with Matchers { execute(input, ta, Seq(ta)) } - "Annotations" should "be readable from file" in { - val annotationStream = getClass.getResourceAsStream("/annotations/SampleAnnotations.anno") - val annotationsYaml = scala.io.Source.fromInputStream(annotationStream).getLines().mkString("\n").parseYaml - val annotationArray = annotationsYaml.convertTo[Array[Annotation]] - annotationArray.length should be (9) - annotationArray(0).targetString should be ("ModC") - annotationArray(7).transformClass should be ("firrtl.passes.InlineInstances") - val expectedValue = "TopOfDiamond\nWith\nSome new lines" - annotationArray(7).value should be (expectedValue) - } - - "Badly formatted serializations" should "return reasonable error messages" in { - var badYaml = - """ - |- transformClass: firrtl.passes.InlineInstances - | targetString: circuit.module.. - | value: ModC.this params 16 32 - """.stripMargin.parseYaml - - var thrown = intercept[Exception] { - badYaml.convertTo[Array[Annotation]] - } - thrown.getMessage should include ("Illegal component name") - - badYaml = - """ - |- transformClass: firrtl.passes.InlineInstances - | targetString: .circuit.module.component - | value: ModC.this params 16 32 - """.stripMargin.parseYaml - - thrown = intercept[Exception] { - badYaml.convertTo[Array[Annotation]] - } - thrown.getMessage should include ("Illegal circuit name") - } - - "Round tripping annotations through text file" should "preserve annotations" in { - val annos: Array[Annotation] = Seq( - InlineAnnotation(CircuitName("fox")), - InlineAnnotation(ModuleName("dog", CircuitName("bear"))), - InlineAnnotation(ComponentName("chocolate", ModuleName("like", CircuitName("i")))), - PinAnnotation(CircuitName("Pinniped"), Seq("sea-lion", "monk-seal")) - ).toArray - - val annoFile = new File("temp-anno") - val writer = new FileWriter(annoFile) - writer.write(annos.toYaml.prettyPrint) - writer.close() - - val yaml = io.Source.fromFile(annoFile).getLines().mkString("\n").parseYaml - annoFile.delete() - - val readAnnos = yaml.convertTo[Array[Annotation]] - - annos.zip(readAnnos).foreach { case (beforeAnno, afterAnno) => - beforeAnno.targetString should be (afterAnno.targetString) - beforeAnno.target should be (afterAnno.target) - beforeAnno.transformClass should be (afterAnno.transformClass) - beforeAnno.transform should be (afterAnno.transform) - - beforeAnno should be (afterAnno) - } - } - "Deleting annotations" should "create a DeletedAnnotation" in { val compiler = new VerilogCompiler val input = @@ -136,12 +61,15 @@ class AnnotationTests extends AnnotationSpec with Matchers { class DeletingTransform extends Transform { val inputForm = LowForm val outputForm = LowForm - def execute(state: CircuitState) = state.copy(annotations = None) + def execute(state: CircuitState) = state.copy(annotations = Seq()) } + val transform = new DeletingTransform + val tname = transform.name val inlineAnn = InlineAnnotation(CircuitName("Top")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(inlineAnn)), Seq(new DeletingTransform)) - result.annotations.get.annotations.head should matchPattern { - case DeletedAnnotation(x, inlineAnn) => + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, Seq(inlineAnn)), Seq(transform)) + println(result.annotations.head) + result.annotations.head should matchPattern { + case DeletedAnnotation(`tname`, `inlineAnn`) => } val exception = (intercept[FIRRTLException] { result.getEmittedCircuit @@ -149,6 +77,7 @@ class AnnotationTests extends AnnotationSpec with Matchers { val deleted = result.deletedAnnotations exception.str should be (s"No EmittedCircuit found! Did you delete any annotations?\n$deleted") } + "Renaming" should "propagate in Lowering of memories" in { val compiler = new VerilogCompiler // Uncomment to help debugging failing tests @@ -170,8 +99,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { |""".stripMargin val annos = Seq(anno("m.r.data.b", "sub"), anno("m.r.data", "all"), anno("m", "mem"), dontTouch("Top.m")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("m_a", "mem")) resultAnno should contain (anno("m_b_0", "mem")) resultAnno should contain (anno("m_b_1", "mem")) @@ -185,7 +114,6 @@ class AnnotationTests extends AnnotationSpec with Matchers { } "Renaming" should "propagate in RemoveChirrtl and Lowering of memories" in { val compiler = new VerilogCompiler - Logger.setClassLogLevels(Map(compiler.getClass.getName -> LogLevel.Debug)) val input = """circuit Top : | module Top : @@ -195,8 +123,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { | read mport r = m[in], clk |""".stripMargin val annos = Seq(anno("r.b", "sub"), anno("r", "all"), anno("m", "mem"), dontTouch("Top.m")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("m_a", "mem")) resultAnno should contain (anno("m_b_0", "mem")) resultAnno should contain (anno("m_b_1", "mem")) @@ -225,8 +153,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { |""".stripMargin val annos = Seq(anno("zero"), anno("x.a"), anno("x.b"), anno("y[0]"), anno("y[1]"), anno("y[2]"), dontTouch("Top.x")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("x_a")) resultAnno should not contain (anno("zero")) resultAnno should not contain (anno("x.a")) @@ -267,8 +195,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { anno("write.a"), anno("write.b[0]"), anno("write.b[1]"), dontTouch("Top.r"), dontTouch("Top.w") ) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should not contain (anno("in.a")) resultAnno should not contain (anno("in.b[0]")) resultAnno should not contain (anno("in.b[1]")) @@ -321,8 +249,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { |""".stripMargin val annos = Seq(anno("in"), anno("out"), anno("w"), anno("n"), anno("r"), dontTouch("Top.r"), dontTouch("Top.w")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("in_a")) resultAnno should contain (anno("in_b_0")) resultAnno should contain (anno("in_b_1")) @@ -357,8 +285,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { |""".stripMargin val annos = Seq(anno("in.b"), anno("out.b"), anno("w.b"), anno("n.b"), anno("r.b"), dontTouch("Top.r"), dontTouch("Top.w")) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("in_b_0")) resultAnno should contain (anno("in_b_1")) resultAnno should contain (anno("out_b_0")) @@ -388,8 +316,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { anno("out.a"), anno("out.b[0]"), anno("out.b[1]"), anno("n.a"), anno("n.b[0]"), anno("n.b[1]") ) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should not contain (anno("in.a")) resultAnno should not contain (anno("in.b[0]")) resultAnno should not contain (anno("in.b[1]")) @@ -438,17 +366,17 @@ class AnnotationTests extends AnnotationSpec with Matchers { anno("foo", mod = "Dead"), anno("bar", mod = "Dead"), anno("foo", mod = "DeadExt"), anno("bar", mod = "DeadExt") ) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) /* Uncomment to help debug println(result.circuit.serialize) - result.annotations.get.annotations.foreach{ a => + result.annotations.foreach{ a => a match { case DeletedAnnotation(xform, anno) => println(s"$xform deleted: ${a.target}") case Annotation(target, _, _) => println(s"not deleted: $target") } } */ - val resultAnno = result.annotations.get.annotations + val resultAnno = result.annotations.toSeq resultAnno should contain (manno("Top")) resultAnno should contain (anno("foo", mod = "Top")) @@ -488,8 +416,8 @@ class AnnotationTests extends AnnotationSpec with Matchers { val annos = Seq( anno("x", mod = "Child"), anno("y", mod = "Child_1"), manno("Child"), manno("Child_1") ) - val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, getAMap(annos)), Nil) - val resultAnno = result.annotations.get.annotations + val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annos), Nil) + val resultAnno = result.annotations.toSeq resultAnno should contain (anno("x", mod = "Child")) resultAnno should contain (anno("y", mod = "Child")) resultAnno should contain (manno("Child")) @@ -503,3 +431,83 @@ class AnnotationTests extends AnnotationSpec with Matchers { require(x == y) } } + +class LegacyAnnotationTests extends AnnotationTests { + def anno(s: String, value: String ="this is a value", mod: String = "Top"): Annotation = + Annotation(ComponentName(s, ModuleName(mod, CircuitName("Top"))), classOf[Transform], value) + def manno(mod: String): Annotation = + Annotation(ModuleName(mod, CircuitName("Top")), classOf[Transform], "some value") + + "LegacyAnnotations" should "be readable from file" in { + val annotationStream = getClass.getResourceAsStream("/annotations/SampleAnnotations.anno") + val annotationsYaml = scala.io.Source.fromInputStream(annotationStream).getLines().mkString("\n").parseYaml + val annotationArray = annotationsYaml.convertTo[Array[LegacyAnnotation]] + annotationArray.length should be (9) + annotationArray(0).targetString should be ("ModC") + annotationArray(7).transformClass should be ("firrtl.passes.InlineInstances") + val expectedValue = "TopOfDiamond\nWith\nSome new lines" + annotationArray(7).value should be (expectedValue) + } + + "Badly formatted LegacyAnnotation serializations" should "return reasonable error messages" in { + var badYaml = + """ + |- transformClass: firrtl.passes.InlineInstances + | targetString: circuit.module.. + | value: ModC.this params 16 32 + """.stripMargin.parseYaml + + var thrown = intercept[Exception] { + badYaml.convertTo[Array[LegacyAnnotation]] + } + thrown.getMessage should include ("Illegal component name") + + badYaml = + """ + |- transformClass: firrtl.passes.InlineInstances + | targetString: .circuit.module.component + | value: ModC.this params 16 32 + """.stripMargin.parseYaml + + thrown = intercept[Exception] { + badYaml.convertTo[Array[LegacyAnnotation]] + } + thrown.getMessage should include ("Illegal circuit name") + } +} + +class JsonAnnotationTests extends AnnotationTests { + // Helper annotations + case class SimpleAnno(target: ComponentName, value: String) extends + SingleTargetAnnotation[ComponentName] { + def duplicate(n: ComponentName) = this.copy(target = n) + } + case class ModuleAnno(target: ModuleName) extends SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName) = this.copy(target = n) + } + + def anno(s: String, value: String ="this is a value", mod: String = "Top"): SimpleAnno = + SimpleAnno(ComponentName(s, ModuleName(mod, CircuitName("Top"))), value) + def manno(mod: String): Annotation = ModuleAnno(ModuleName(mod, CircuitName("Top"))) + + "Round tripping annotations through text file" should "preserve annotations" in { + val annos: Array[Annotation] = Seq( + InlineAnnotation(CircuitName("fox")), + InlineAnnotation(ModuleName("dog", CircuitName("bear"))), + InlineAnnotation(ComponentName("chocolate", ModuleName("like", CircuitName("i")))), + PinAnnotation(Seq("sea-lion", "monk-seal")) + ).toArray + + val annoFile = new File("temp-anno") + val writer = new FileWriter(annoFile) + writer.write(JsonProtocol.serialize(annos)) + writer.close() + + val text = io.Source.fromFile(annoFile).getLines().mkString("\n") + annoFile.delete() + + val readAnnos = JsonProtocol.deserializeTry(text).get + + annos should be (readAnnos) + } +} diff --git a/src/test/scala/firrtlTests/CInferMDirSpec.scala b/src/test/scala/firrtlTests/CInferMDirSpec.scala index 299142d9..a0f55794 100644 --- a/src/test/scala/firrtlTests/CInferMDirSpec.scala +++ b/src/test/scala/firrtlTests/CInferMDirSpec.scala @@ -68,8 +68,7 @@ circuit foo : bar <= io.in """.stripMargin - val annotationMap = AnnotationMap(Nil) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } diff --git a/src/test/scala/firrtlTests/CheckCombLoopsSpec.scala b/src/test/scala/firrtlTests/CheckCombLoopsSpec.scala index 6c8a2f20..06cbb8e4 100644 --- a/src/test/scala/firrtlTests/CheckCombLoopsSpec.scala +++ b/src/test/scala/firrtlTests/CheckCombLoopsSpec.scala @@ -45,7 +45,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { |""".stripMargin val writer = new java.io.StringWriter - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } "Simple combinational loop" should "throw an exception" in { @@ -66,7 +66,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { val writer = new java.io.StringWriter intercept[CheckCombLoops.CombLoopException] { - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } } @@ -87,7 +87,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { val writer = new java.io.StringWriter intercept[CheckCombLoops.CombLoopException] { - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } } @@ -119,7 +119,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { val writer = new java.io.StringWriter intercept[CheckCombLoops.CombLoopException] { - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } } @@ -147,7 +147,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { val writer = new java.io.StringWriter intercept[CheckCombLoops.CombLoopException] { - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } } @@ -171,7 +171,7 @@ class CheckCombLoopsSpec extends SimpleTransformSpec { val writer = new java.io.StringWriter intercept[CheckCombLoops.CombLoopException] { - compile(CircuitState(parse(input), ChirrtlForm, None), writer) + compile(CircuitState(parse(input), ChirrtlForm), writer) } } } diff --git a/src/test/scala/firrtlTests/ChirrtlMemSpec.scala b/src/test/scala/firrtlTests/ChirrtlMemSpec.scala index d039cc96..74d39286 100644 --- a/src/test/scala/firrtlTests/ChirrtlMemSpec.scala +++ b/src/test/scala/firrtlTests/ChirrtlMemSpec.scala @@ -78,8 +78,7 @@ circuit foo : io.out <= bar """.stripMargin - val annotationMap = AnnotationMap(Nil) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } @@ -104,14 +103,13 @@ circuit foo : io.out <= bar """.stripMargin - val annotationMap = AnnotationMap(Nil) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } ignore should "Memories should not have validif on port clocks when declared in a when" in { - val input = + val input = """;buildInfoPackage: chisel3, version: 3.0-SNAPSHOT, scalaVersion: 2.11.11, sbtVersion: 0.13.16, builtAtString: 2017-10-06 20:55:20.367, builtAtMillis: 1507323320367 |circuit Stack : | module Stack : @@ -157,8 +155,7 @@ circuit foo : | skip @[Stack.scala 19:16] | io.dataOut <= out @[Stack.scala 31:14] """.stripMargin - val annotationMap = AnnotationMap(Nil) - val res = (new LowFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), Nil).circuit + val res = (new LowFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res search { case Connect(_, WSubField(WSubField(WRef("stack_mem", _, _, _), "_T_35",_, _), "clk", _, _), WRef("clock", _, _, _)) => true case Connect(_, WSubField(WSubField(WRef("stack_mem", _, _, _), "_T_17",_, _), "clk", _, _), WRef("clock", _, _, _)) => true @@ -179,8 +176,7 @@ circuit foo : | read mport bar = mem[addr], clock | out <= bar |""".stripMargin - val annotationMap = AnnotationMap(Nil) - val res = (new LowFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), Nil).circuit + val res = (new LowFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res search { case Connect(_, WSubField(WSubField(WRef("mem", _, _, _), "bar",_, _), "clk", _, _), WRef("clock", _, _, _)) => true }) @@ -201,8 +197,7 @@ circuit foo : | read mport bar = mem[addr], local | out <= bar |""".stripMargin - val annotationMap = AnnotationMap(Nil) - val res = new LowFirrtlCompiler().compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), Nil).circuit + val res = new LowFirrtlCompiler().compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res search { case Connect(_, WSubField(WSubField(WRef("mem", _, _, _), "bar",_, _), "clk", _, _), WRef("clock", _, _, _)) => true }) @@ -223,9 +218,7 @@ circuit foo : | read mport bar = mem[addr], asClock(local) | out <= bar |""".stripMargin - val annotationMap = AnnotationMap(Nil) - - val res = new LowFirrtlCompiler().compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), Nil).circuit + val res = new LowFirrtlCompiler().compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res search { case Connect(_, WSubField(WSubField(WRef("mem", _, _, _), "bar",_, _), "clk", _, _), DoPrim(AsClock, Seq(WRef("clock", _, _, _)), Nil, _)) => true }) @@ -246,9 +239,7 @@ circuit foo : | read mport bar = mem[addr], asClock(clock) | out <= bar |""".stripMargin - val annotationMap = AnnotationMap(Nil) - - val res = (new HighFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm, Some(annotationMap)), Nil).circuit + val res = (new HighFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res search { case Connect(_, SubField(SubField(Reference("mem", _), "bar", _), "clk", _), DoPrim(AsClock, Seq(Reference("clock", _)), _, _)) => true }) diff --git a/src/test/scala/firrtlTests/DCETests.scala b/src/test/scala/firrtlTests/DCETests.scala index e28ab432..97c1c146 100644 --- a/src/test/scala/firrtlTests/DCETests.scala +++ b/src/test/scala/firrtlTests/DCETests.scala @@ -20,7 +20,7 @@ class DCETests extends FirrtlFlatSpec { new SimpleTransform(RemoveEmpty, LowForm) ) private def exec(input: String, check: String, annos: Seq[Annotation] = List.empty): Unit = { - val state = CircuitState(parse(input), ChirrtlForm, Some(AnnotationMap(annos))) + val state = CircuitState(parse(input), ChirrtlForm, annos) val finalState = (new LowFirrtlCompiler).compileAndEmit(state, customTransforms) val res = finalState.getEmittedCircuit.value // Convert to sets for comparison diff --git a/src/test/scala/firrtlTests/DriverSpec.scala b/src/test/scala/firrtlTests/DriverSpec.scala index 0327cf8b..406e5f42 100644 --- a/src/test/scala/firrtlTests/DriverSpec.scala +++ b/src/test/scala/firrtlTests/DriverSpec.scala @@ -2,15 +2,32 @@ package firrtlTests -import java.io.File +import java.io.{File, FileWriter} import org.scalatest.{FreeSpec, Matchers} -import firrtl.passes.InlineInstances -import firrtl.passes.memlib.{InferReadWrite, ReplSeqMem} -import firrtl.transforms.BlackBoxSourceHelper +import firrtl.passes.{InlineAnnotation, InlineInstances} +import firrtl.passes.memlib.{ + InferReadWrite, + InferReadWriteAnnotation, + ReplSeqMem, + ReplSeqMemAnnotation +} +import firrtl.transforms.BlackBoxTargetDirAnno import firrtl._ +import firrtl.annotations._ import firrtl.util.BackendCompilationUtilities +class ExceptingTransform extends Transform { + def inputForm = HighForm + def outputForm = HighForm + def execute(state: CircuitState): CircuitState = { + throw new ExceptingTransform.CustomException("I ran!") + } +} +object ExceptingTransform { + case class CustomException(msg: String) extends Exception +} + //noinspection ScalaStyle class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities { "CommonOptions are some simple options available across the chisel3 ecosystem" - { @@ -117,9 +134,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities val firrtlOptions = optionsManager.firrtlOptions firrtlOptions.annotations.length should be (3) - firrtlOptions.annotations.foreach { annotation => - annotation.transform shouldBe classOf[InlineInstances] - } + firrtlOptions.annotations.foreach(_ shouldBe an [InlineAnnotation]) } "infer-rw annotation" in { val optionsManager = new ExecutionOptionsManager("test") with HasFirrtlOptions @@ -130,9 +145,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities val firrtlOptions = optionsManager.firrtlOptions firrtlOptions.annotations.length should be (1) - firrtlOptions.annotations.foreach { annotation => - annotation.transform shouldBe classOf[InferReadWrite] - } + firrtlOptions.annotations.head should be (InferReadWriteAnnotation) } "repl-seq-mem annotation" in { val optionsManager = new ExecutionOptionsManager("test") with HasFirrtlOptions @@ -143,8 +156,8 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities val firrtlOptions = optionsManager.firrtlOptions firrtlOptions.annotations.length should be (1) - firrtlOptions.annotations.foreach { annotation => - annotation.transform shouldBe classOf[ReplSeqMem] + firrtlOptions.annotations.head should matchPattern { + case ReplSeqMemAnnotation("infile1", "outfile1") => } } } @@ -161,7 +174,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities optionsManager.firrtlOptions.annotations.length should be (0) val annos = Driver.getAnnotations(optionsManager) annos.length should be (12) // 9 from circuit plus 3 general purpose - annos.count(_.transformClass == "firrtl.passes.InlineInstances") should be (9) + annos.count(_.isInstanceOf[InlineAnnotation]) should be (9) annoFile.delete() } @@ -178,12 +191,83 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities optionsManager.firrtlOptions.annotations.length should be (0) val annos = Driver.getAnnotations(optionsManager) annos.length should be (12) // 9 from circuit plus 3 general purpose - annos.count(_.transformClass == "firrtl.passes.InlineInstances") should be (9) + annos.count(_.isInstanceOf[InlineAnnotation]) should be (9) annotationsTestFile.delete() } + // Deprecated + "Supported LegacyAnnotations will be converted automagically" in { + val testDir = createTestDirectory("test") + val annoFilename = "LegacyAnnotations.anno" + val annotationsTestFile = new File(testDir, annoFilename) + val optionsManager = new ExecutionOptionsManager("test") with HasFirrtlOptions { + commonOptions = commonOptions.copy(topName = "test", targetDirName = testDir.toString) + firrtlOptions = firrtlOptions.copy( + annotationFileNames = List(annotationsTestFile.toString) + ) + } + copyResourceToFile(s"/annotations/$annoFilename", annotationsTestFile) + val annos = Driver.getAnnotations(optionsManager) + + val cname = CircuitName("foo") + val mname = ModuleName("bar", cname) + val compname = ComponentName("x", mname) + import firrtl.passes.clocklist._ + import firrtl.passes.memlib._ + import firrtl.passes.wiring._ + import firrtl.transforms._ + val expected = List( + InlineAnnotation(cname), + ClockListAnnotation(mname, "output"), + InferReadWriteAnnotation, + ReplSeqMemAnnotation("input", "output"), + NoDedupMemAnnotation(compname), + NoDedupAnnotation(mname), + SourceAnnotation(compname, "pin"), + SinkAnnotation(compname, "pin"), + BlackBoxResourceAnno(mname, "resource"), + BlackBoxInlineAnno(mname, "name", "text"), + BlackBoxTargetDirAnno("targetdir"), + NoDCEAnnotation, + DontTouchAnnotation(compname), + OptimizableExtModuleAnnotation(mname) + ) + for (e <- expected) { + annos should contain (e) + } + } + + // Deprecated + "UNsupported LegacyAnnotations should throw errors" in { + val testDir = createTestDirectory("test") + val annoFilename = "InvalidLegacyAnnotations.anno" + val annotationsTestFile = new File(testDir, annoFilename) + copyResourceToFile(s"/annotations/$annoFilename", annotationsTestFile) + + import net.jcazevedo.moultingyaml._ + val text = io.Source.fromFile(annotationsTestFile).mkString + val yamlAnnos = text.parseYaml match { case YamlArray(xs) => xs } + + // Since each one should error, emit each one to an anno file and try to read it + for ((anno, i) <- yamlAnnos.zipWithIndex) { + val annoFile = new File(testDir, s"anno_$i.anno") + val fw = new FileWriter(annoFile) + fw.write(YamlArray(anno).prettyPrint) + fw.close() + val optionsManager = new ExecutionOptionsManager("test") with HasFirrtlOptions { + commonOptions = commonOptions.copy(topName = "test", targetDirName = testDir.toString) + firrtlOptions = firrtlOptions.copy( + annotationFileNames = List(annoFile.toString) + ) + } + (the [Exception] thrownBy { + Driver.getAnnotations(optionsManager) + }).getMessage should include ("Old-style annotations") + } + } + "Annotations can be read from multiple files" in { - val filename = "SampleAnnotations.anno" + val filename = "SampleAnnotations.anno.json" val optionsManager = new ExecutionOptionsManager("test") with HasFirrtlOptions { commonOptions = commonOptions.copy(topName = "a.fir") firrtlOptions = firrtlOptions.copy( @@ -195,7 +279,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities optionsManager.firrtlOptions.annotations.length should be (0) val annos = Driver.getAnnotations(optionsManager) annos.length should be (21) // 18 from files plus 3 general purpose - annos.count(_.transformClass == "firrtl.passes.InlineInstances") should be (18) + annos.count(_.isInstanceOf[InlineAnnotation]) should be (18) annotationsTestFile.delete() } @@ -208,15 +292,15 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities Array("--infer-rw", "circuit", "-faf", annoFile.toString) ) should be (true) - copyResourceToFile("/annotations/SampleAnnotations.anno", annoFile) + copyResourceToFile("/annotations/SampleAnnotations.anno.json", annoFile) val firrtlOptions = optionsManager.firrtlOptions firrtlOptions.annotations.length should be (1) // infer-rw - val anns = Driver.getAnnotations(optionsManager).groupBy(_.transform) - anns(classOf[BlackBoxSourceHelper]).length should be (1) // built-in to getAnnotations - anns(classOf[InferReadWrite]).length should be (1) // --infer-rw - anns(classOf[InlineInstances]).length should be (9) // annotations file + val anns = Driver.getAnnotations(optionsManager) + anns should contain (BlackBoxTargetDirAnno(".")) // built in to getAnnotations + anns should contain (InferReadWriteAnnotation) // --infer-rw + anns.collect { case a: InlineAnnotation => a }.length should be (9) // annotations file annoFile.delete() } diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index b71e51e2..861d1745 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -22,10 +22,10 @@ trait FirrtlRunners extends BackendCompilationUtilities { val cppHarnessResourceName: String = "/firrtl/testTop.cpp" /** Compiles input Firrtl to Verilog */ - def compileToVerilog(input: String, annotations: AnnotationMap = AnnotationMap(Seq.empty)): String = { + def compileToVerilog(input: String, annotations: AnnotationSeq = Seq.empty): String = { val circuit = Parser.parse(input.split("\n").toIterator) val compiler = new VerilogCompiler - val res = compiler.compileAndEmit(CircuitState(circuit, HighForm, Some(annotations))) + val res = compiler.compileAndEmit(CircuitState(circuit, HighForm, annotations)) res.getEmittedCircuit.value } /** Compile a Firrtl file @@ -38,7 +38,7 @@ trait FirrtlRunners extends BackendCompilationUtilities { prefix: String, srcDir: String, customTransforms: Seq[Transform] = Seq.empty, - annotations: AnnotationMap = new AnnotationMap(Seq.empty)): File = { + annotations: AnnotationSeq = Seq.empty): File = { val testDir = createTestDirectory(prefix) copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir")) @@ -47,7 +47,7 @@ trait FirrtlRunners extends BackendCompilationUtilities { firrtlOptions = FirrtlExecutionOptions( infoModeName = "ignore", customTransforms = customTransforms, - annotations = annotations.annotations.toList) + annotations = annotations.toList) } firrtl.Driver.execute(optionsManager) @@ -65,7 +65,7 @@ trait FirrtlRunners extends BackendCompilationUtilities { srcDir: String, verilogPrefixes: Seq[String] = Seq.empty, customTransforms: Seq[Transform] = Seq.empty, - annotations: AnnotationMap = new AnnotationMap(Seq.empty)) = { + annotations: AnnotationSeq = Seq.empty) = { val testDir = compileFirrtlTest(prefix, srcDir, customTransforms, annotations) val harness = new File(testDir, s"top.cpp") copyResourceToFile(cppHarnessResourceName, harness) @@ -111,8 +111,7 @@ trait FirrtlMatchers extends Matchers { expected: Seq[String], compiler: Compiler, annotations: Seq[Annotation] = Seq.empty) = { - val annoMap = AnnotationMap(annotations) - val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annoMap))) + val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, annotations)) val lines = finalState.getEmittedCircuit.value split "\n" map normalized for (e <- expected) { lines should contain (e) diff --git a/src/test/scala/firrtlTests/InferReadWriteSpec.scala b/src/test/scala/firrtlTests/InferReadWriteSpec.scala index 82c9d65f..34e228be 100644 --- a/src/test/scala/firrtlTests/InferReadWriteSpec.scala +++ b/src/test/scala/firrtlTests/InferReadWriteSpec.scala @@ -71,8 +71,8 @@ circuit sram6t : T_5 <= io.wdata """.stripMargin - val annotationMap = AnnotationMap(Seq(memlib.InferReadWriteAnnotation("sram6t"))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + val annos = Seq(memlib.InferReadWriteAnnotation) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } @@ -102,8 +102,8 @@ circuit sram6t : io.dataOut <= _T_22 """.stripMargin - val annotationMap = AnnotationMap(Seq(memlib.InferReadWriteAnnotation("sram6t"))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + val annos = Seq(memlib.InferReadWriteAnnotation) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } @@ -133,9 +133,9 @@ circuit sram6t : T_5 <= io.wdata """.stripMargin - val annotationMap = AnnotationMap(Seq(memlib.InferReadWriteAnnotation("sram6t"))) + val annos = Seq(memlib.InferReadWriteAnnotation) intercept[InferReadWriteCheckException] { - compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(annotationMap))) + compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) } } } diff --git a/src/test/scala/firrtlTests/PassTests.scala b/src/test/scala/firrtlTests/PassTests.scala index 6727533e..847643ef 100644 --- a/src/test/scala/firrtlTests/PassTests.scala +++ b/src/test/scala/firrtlTests/PassTests.scala @@ -21,7 +21,7 @@ abstract class SimpleTransformSpec extends FlatSpec with FirrtlMatchers with Com // Executes the test. Call in tests. // annotations cannot have default value because scalatest trait Suite has a default value def execute(input: String, check: String, annotations: Seq[Annotation]): Unit = { - val finalState = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(AnnotationMap(annotations)))) + val finalState = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annotations)) val actual = RemoveEmpty.run(parse(finalState.getEmittedCircuit.value)).serialize val expected = parse(check).serialize logger.debug(actual) @@ -32,7 +32,7 @@ abstract class SimpleTransformSpec extends FlatSpec with FirrtlMatchers with Com // No default to be consistent with execute def failingexecute(input: String, annotations: Seq[Annotation]): Exception = { intercept[PassExceptions] { - compile(CircuitState(parse(input), ChirrtlForm, Some(AnnotationMap(annotations))), Seq.empty) + compile(CircuitState(parse(input), ChirrtlForm, annotations), Seq.empty) } } } diff --git a/src/test/scala/firrtlTests/ReplSeqMemTests.scala b/src/test/scala/firrtlTests/ReplSeqMemTests.scala index 7cbfeafe..dcc23235 100644 --- a/src/test/scala/firrtlTests/ReplSeqMemTests.scala +++ b/src/test/scala/firrtlTests/ReplSeqMemTests.scala @@ -63,8 +63,8 @@ circuit Top : io2.commit_entry.bits.info <- R1 """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:Top:-o:"+confLoc))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq(ReplSeqMemAnnotation.parse("-c:Top:-o:"+confLoc)) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl println(res.annotations) parse(res.getEmittedCircuit.value) @@ -86,8 +86,8 @@ circuit Top : write mport T_155 = mem[p_address], clock """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:Top:-o:"+confLoc))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq(ReplSeqMemAnnotation.parse("-c:Top:-o:"+confLoc)) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() @@ -111,8 +111,8 @@ circuit CustomMemory : skip """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq(ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:"+confLoc)) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() @@ -136,8 +136,8 @@ circuit CustomMemory : skip """.stripMargin val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq(ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:"+confLoc)) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) (new java.io.File(confLoc)).delete() @@ -209,10 +209,10 @@ circuit CustomMemory : skip """ val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq( - ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc), - NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("CustomMemory",CircuitName("CustomMemory")))))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq( + ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:"+confLoc), + NoDedupMemAnnotation(ComponentName("mem_0", ModuleName("CustomMemory",CircuitName("CustomMemory"))))) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { @@ -249,10 +249,10 @@ circuit CustomMemory : skip """ val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq( - ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc), - NoDedupMemAnnotation(ComponentName("mem_1", ModuleName("CustomMemory",CircuitName("CustomMemory")))))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq( + ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:"+confLoc), + NoDedupMemAnnotation(ComponentName("mem_1", ModuleName("CustomMemory",CircuitName("CustomMemory"))))) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { @@ -285,8 +285,8 @@ circuit CustomMemory : skip """ val confLoc = "ReplSeqMemTests.confTEMP" - val aMap = AnnotationMap(Seq(ReplSeqMemAnnotation("-c:CustomMemory:-o:"+confLoc))) - val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, Some(aMap))) + val annos = Seq(ReplSeqMemAnnotation.parse("-c:CustomMemory:-o:"+confLoc)) + val res = compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos)) // Check correctness of firrtl val circuit = parse(res.getEmittedCircuit.value) val numExtMods = circuit.modules.count { diff --git a/src/test/scala/firrtlTests/UnitTests.scala b/src/test/scala/firrtlTests/UnitTests.scala index 018a35f6..a38a8def 100644 --- a/src/test/scala/firrtlTests/UnitTests.scala +++ b/src/test/scala/firrtlTests/UnitTests.scala @@ -24,7 +24,7 @@ class UnitTests extends FirrtlFlatSpec { val c = transforms.foldLeft(CircuitState(parse(input), UnknownForm)) { (c: CircuitState, t: Transform) => t.runTransform(c) }.circuit - CircuitState(c, UnknownForm, None, None) + CircuitState(c, UnknownForm, Seq(), None) } "Pull muxes" should "not be exponential in runtime" in { diff --git a/src/test/scala/firrtlTests/WiringTests.scala b/src/test/scala/firrtlTests/WiringTests.scala index 6da73157..4f8fd9fe 100644 --- a/src/test/scala/firrtlTests/WiringTests.scala +++ b/src/test/scala/firrtlTests/WiringTests.scala @@ -705,7 +705,7 @@ class WiringTests extends FirrtlFlatSpec { (c: Circuit, p: Pass) => p.run(c) } val wiringXForm = new WiringTransform() - val retC = wiringXForm.execute(CircuitState(c, MidForm, Some(AnnotationMap(Seq(source, sink))), None)).circuit + val retC = wiringXForm.execute(CircuitState(c, MidForm, Seq(source, sink))).circuit (parse(retC.serialize).serialize) should be (parse(check).serialize) } @@ -743,7 +743,7 @@ class WiringTests extends FirrtlFlatSpec { (c: Circuit, p: Pass) => p.run(c) } val wiringXForm = new WiringTransform() - val retC = wiringXForm.execute(CircuitState(c, MidForm, Some(AnnotationMap(Seq(source, sink))), None)).circuit + val retC = wiringXForm.execute(CircuitState(c, MidForm, Seq(source, sink))).circuit (parse(retC.serialize).serialize) should be (parse(check).serialize) } @@ -789,7 +789,7 @@ class WiringTests extends FirrtlFlatSpec { (c: Circuit, p: Pass) => p.run(c) } val wiringXForm = new WiringTransform() - val retC = wiringXForm.execute(CircuitState(c, MidForm, Some(AnnotationMap(Seq(source, sink))), None)).circuit + val retC = wiringXForm.execute(CircuitState(c, MidForm, Seq(source, sink))).circuit (parse(retC.serialize).serialize) should be (parse(check).serialize) } diff --git a/src/test/scala/firrtlTests/annotationTests/TargetDirAnnotationSpec.scala b/src/test/scala/firrtlTests/annotationTests/TargetDirAnnotationSpec.scala index 60cbf0fc..eb061d8f 100644 --- a/src/test/scala/firrtlTests/annotationTests/TargetDirAnnotationSpec.scala +++ b/src/test/scala/firrtlTests/annotationTests/TargetDirAnnotationSpec.scala @@ -14,11 +14,9 @@ class FindTargetDirTransform(expected: String) extends Transform { var run = false def execute(state: CircuitState): CircuitState = { run = true - state.annotations.foreach { aMap => - aMap.annotations.collectFirst { - case TargetDirAnnotation(expected) => - foundTargetDir = true - } + state.annotations.collectFirst { + case TargetDirAnnotation(expected) => + foundTargetDir = true } state } diff --git a/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala b/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala index 39da2a33..a4319e8b 100644 --- a/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala +++ b/src/test/scala/firrtlTests/fixed/FixedPointMathSpec.scala @@ -2,7 +2,7 @@ package firrtlTests.fixed -import firrtl.{CircuitState, ChirrtlForm, LowFirrtlCompiler, Parser, AnnotationMap} +import firrtl.{CircuitState, ChirrtlForm, LowFirrtlCompiler, Parser} import firrtl.Parser.IgnoreInfo import firrtlTests.FirrtlFlatSpec diff --git a/src/test/scala/firrtlTests/fixed/RemoveFixedTypeSpec.scala b/src/test/scala/firrtlTests/fixed/RemoveFixedTypeSpec.scala index 34a22c26..8645fa62 100644 --- a/src/test/scala/firrtlTests/fixed/RemoveFixedTypeSpec.scala +++ b/src/test/scala/firrtlTests/fixed/RemoveFixedTypeSpec.scala @@ -185,7 +185,7 @@ class RemoveFixedTypeSpec extends FirrtlFlatSpec { } val chirrtlTransform = new CheckChirrtlTransform - chirrtlTransform.execute(CircuitState(parse(input), ChirrtlForm, Some(new AnnotationMap(Seq.empty)))) + chirrtlTransform.execute(CircuitState(parse(input), ChirrtlForm)) } "Fixed point numbers" should "remove nested AsFixedPoint" in { diff --git a/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala b/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala index bf294fe9..4c550c46 100644 --- a/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala +++ b/src/test/scala/firrtlTests/transforms/BlacklBoxSourceHelperSpec.scala @@ -4,39 +4,12 @@ package firrtlTests.transforms import firrtl.annotations.{Annotation, CircuitName, ModuleName} import firrtl.transforms._ -import firrtl.{AnnotationMap, FIRRTLException, Transform, VerilogCompiler} +import firrtl.{FIRRTLException, Transform, VerilogCompiler} import firrtlTests.{HighTransformSpec, LowTransformSpec} import org.scalacheck.Test.Failed import org.scalatest.{FreeSpec, Matchers, Succeeded} -/** - * Tests inline instances transformation - */ -class BlacklBoxSourceHelperSpec extends FreeSpec with Matchers { - "BlackBoxSourceAnnotations" - { - val modName = ModuleName("dog", CircuitName("fox")) - val resource = "file://somefile.v" - - "should parse and unparse" in { - - val serialized = BlackBoxResource(resource).serialize - BlackBoxSource.parse(serialized) match { - case Some(BlackBoxResource(id)) => - id should be (resource) - Succeeded - case _ => Failed - } - } - "should fail on unsupported kinds" in { - intercept[FIRRTLException] { - BlackBoxSourceAnnotation(modName, "bad value") - } - BlackBoxSourceAnnotation(modName, BlackBoxResource(resource).serialize).isInstanceOf[Annotation] should be(true) - } - } -} - class BlacklBoxSourceHelperTransformSpec extends LowTransformSpec { def transform: Transform = new BlackBoxSourceHelper @@ -79,8 +52,8 @@ class BlacklBoxSourceHelperTransformSpec extends LowTransformSpec { "annotated external modules" should "appear in output directory" in { val annos = Seq( - Annotation(moduleName, classOf[BlackBoxSourceHelper], BlackBoxTargetDir("test_run_dir").serialize), - Annotation(moduleName, classOf[BlackBoxSourceHelper], BlackBoxResource("/blackboxes/AdderExtModule.v").serialize) + BlackBoxTargetDirAnno("test_run_dir"), + BlackBoxResourceAnno(moduleName, "/blackboxes/AdderExtModule.v") ) execute(input, output, annos) diff --git a/src/test/scala/firrtlTests/transforms/DedupTests.scala b/src/test/scala/firrtlTests/transforms/DedupTests.scala index 74c4b4e7..e88bd506 100644 --- a/src/test/scala/firrtlTests/transforms/DedupTests.scala +++ b/src/test/scala/firrtlTests/transforms/DedupTests.scala @@ -8,7 +8,7 @@ import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner import firrtl.ir.Circuit -import firrtl.{Parser, AnnotationMap} +import firrtl.Parser import firrtl.passes.PassExceptions import firrtl.annotations.{ Named, |
