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/transforms | |
| 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/transforms')
7 files changed, 77 insertions, 159 deletions
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) } |
