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/scala/firrtl/annotations/Annotation.scala | |
| 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/scala/firrtl/annotations/Annotation.scala')
| -rw-r--r-- | src/main/scala/firrtl/annotations/Annotation.scala | 169 |
1 files changed, 144 insertions, 25 deletions
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}""" } |
