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/main | |
| 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/main')
26 files changed, 526 insertions, 539 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) } |
