From 69e27b2fd9e02d4e3a024eec0cafce5b4b46c10a Mon Sep 17 00:00:00 2001 From: Adam Izraelevitz Date: Mon, 14 Sep 2020 10:17:49 -0700 Subject: Documentation and minor plugin changes. (#1573) * Added documentation. Bugfix in plugin. Moved plugin APIs to separate package * Revert reg naming behavior (omit underscore) * Added documentation and a test * Addressed reviewer feedback.--- build.sbt | 1 + .../main/scala/chisel3/experimental/package.scala | 9 - core/src/main/scala/chisel3/internal/Builder.scala | 2 +- .../scala/chisel3/internal/plugin/package.scala | 22 ++ docs/src/cookbooks/naming.md | 62 ++++++ docs/src/explanations/naming.md | 229 +++++++++++++++++++++ plugin/src/main/resources/scalac-plugin.xml | 2 +- .../chisel3/internal/plugin/ChiselPlugin.scala | 138 +++++++++++++ .../scala-2.12/chisel3/plugin/ChiselPlugin.scala | 138 ------------- .../scala/chiselTests/naming/NamePluginSpec.scala | 18 ++ 10 files changed, 472 insertions(+), 149 deletions(-) create mode 100644 core/src/main/scala/chisel3/internal/plugin/package.scala create mode 100644 docs/src/cookbooks/naming.md create mode 100644 docs/src/explanations/naming.md create mode 100644 plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala delete mode 100644 plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala diff --git a/build.sbt b/build.sbt index 8512eb0e..b3eb93d2 100644 --- a/build.sbt +++ b/build.sbt @@ -233,6 +233,7 @@ lazy val docs = project // new documentation project .in(file("docs-target")) // important: it must not be docs/ .dependsOn(chisel) .enablePlugins(MdocPlugin) + .settings(usePluginSettings: _*) .settings(commonSettings) .settings( scalacOptions += "-language:reflectiveCalls", diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index 71fd186c..12631512 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -141,13 +141,4 @@ package object experimental { val prefix = chisel3.internal.prefix // Use to remove prefixes not in provided scope val noPrefix = chisel3.internal.noPrefix - // Used by Chisel's compiler plugin to automatically name signals - def autoNameRecursively[T <: Any](name: String, nameMe: T): T = { - chisel3.internal.Builder.nameRecursively( - name.replace(" ", ""), - nameMe, - (id: chisel3.internal.HasId, n: String) => id.autoSeed(n) - ) - nameMe - } } diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index e55f1af7..741a2498 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -119,7 +119,7 @@ private[chisel3] trait HasId extends InstanceId { * @param seed Seed for the name of this component * @return this object */ - def autoSeed(seed: String): this.type = { + private [chisel3] def autoSeed(seed: String): this.type = { auto_seed = Some(seed) for(hook <- auto_postseed_hooks) { hook(seed) } prefix_seed = Builder.getPrefix() diff --git a/core/src/main/scala/chisel3/internal/plugin/package.scala b/core/src/main/scala/chisel3/internal/plugin/package.scala new file mode 100644 index 00000000..6d78b1c3 --- /dev/null +++ b/core/src/main/scala/chisel3/internal/plugin/package.scala @@ -0,0 +1,22 @@ +// See LICENSE for license details. + +package chisel3.internal + +package object plugin { + /** Used by Chisel's compiler plugin to automatically name signals + * DO NOT USE in your normal Chisel code!!! + * + * @param name The name to use + * @param nameMe The thing to be named + * @tparam T The type of the thing to be named + * @return The thing, but now named + */ + def autoNameRecursively[T <: Any](name: String, nameMe: T): T = { + chisel3.internal.Builder.nameRecursively( + name.replace(" ", ""), + nameMe, + (id: chisel3.internal.HasId, n: String) => id.autoSeed(n) + ) + nameMe + } +} diff --git a/docs/src/cookbooks/naming.md b/docs/src/cookbooks/naming.md new file mode 100644 index 00000000..5f57826f --- /dev/null +++ b/docs/src/cookbooks/naming.md @@ -0,0 +1,62 @@ +--- +layout: docs +title: "Naming Cookbook" +section: "chisel3" +--- + +```scala mdoc:invisible +import chisel3.internal.plugin._ +import chisel3._ +import chisel3.experimental.prefix +import chisel3.experimental.noPrefix +import chisel3.stage.ChiselStage +``` + +### I still have _T signals, can this be fixed? + +First check - is the compiler plugin properly enabled? Scalac plugins are enabled via the scalac option +`-Xplugin:`. You can check which compiler plugins are enabled by running `show Compile / scalacOptions` in +the sbt prompt. + +If the plugin is enabled, these signals could be intermediate values which are consumed by either assertions or when +predicates. In these cases, the compiler plugin often can't find a good prefix for the generated intermediate signals. +We recommend you manually insert calls to `prefix` to fix these cases. We did this to Rocket Chip and saw huge benefits! + +### I still see _GEN signals, can this be fixed? + +`_GEN` signals are usually generated from the FIRRTL compiler, rather than the Chisel library. We are working on +renaming these signals with more context-dependent names, but it is a work in progress. Thanks for caring! + +### My module names are super unstable - I change one thing and Queue_1 becomes Queue_42. Help! + +This is the infamous `Queue` instability problem. In general, these cases are best solved at the source - the module +itself! If you overwrite `desiredName` to include parameter information (see the +[explanation](../explanations/naming.md#set-a-module-name) for more info), then this can avoid this problem permanantly. +We've done this with some Chisel utilities with great results! + +### I want to add some hardware or assertions, but each time I do all the signal names get bumped! + +This is the classic "ECO" problem, and we provide descriptions in [explanation](../explanations/naming.md). In short, +we recommend wrapping all additional logic in a prefix scope, which enables a unique namespace. This should prevent +name collisions, which are what triggers all those annoying signal name bumps! + +### I want to force a signal (or instance) name to something, how do I do that? + +Use the `.suggestName` method, which is on all classes which subtype 'Data'. + +### All this prefixing is annoying, how do I fix it? + +You can use the `noPrefix { ... }` to strip the prefix from all signals generated in that scope. + +```scala mdoc +class ExampleNoPrefix extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = noPrefix { in + in + in } + + out := add +} + +println(ChiselStage.emitVerilog(new Example7)) +``` diff --git a/docs/src/explanations/naming.md b/docs/src/explanations/naming.md new file mode 100644 index 00000000..69870813 --- /dev/null +++ b/docs/src/explanations/naming.md @@ -0,0 +1,229 @@ +--- +layout: docs +title: "Naming" +section: "chisel3" +--- + +Historically, Chisel has had trouble reliably capturing the names of signals. The reasons for this are due to (1) +primarily relying on reflection to find names, (2) using `@chiselName` macro which had unreliable behavior. + +Chisel 3.4 introduced a custom Scala compiler plugin which enables reliabe and automatic capturing of signal names, when +they are declared. In addition, this release includes prolific use of a new prefixing API which enables more stable +naming of signals programmatically generated from function calls. + +This document explains how naming now works in Chisel for signal and module names. For cookbook examples on how to fix +systemic name-stability issues, please refer to the naming [cookbook](../cookbooks/naming.md). + +### Compiler Plugin + +```scala mdoc +import chisel3.internal.plugin._ +import chisel3._ +import chisel3.experimental.prefix +import chisel3.experimental.noPrefix +import chisel3.stage.ChiselStage +``` + +With the release of Chisel 3.4, users should add the following line to their build.sbt settings to get the improved +naming: + +```scala +addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % chiselVersion cross CrossVersion.full) +``` + +This plugin will run after the 'typer' phase of the Scala compiler. It looks for any user code which is of the form +`val x = y`, where `x` is of type `chisel3.Data`, `chisel3.MemBase`, or `chisel3.experimental.BaseModule`. For each +line which fits this criteria, it rewrites that line. In the following examples, the commented line is the what the +line above is rewritten to. + +If the line is within a bundle declaration or is a module instantiation, it is rewritten to replace the right hand +side with a call to `autoNameRecursively`, which names the signal/module. + +```scala mdoc +class MyBundle extends Bundle { + val foo = Input(UInt(3.W)) + // val foo = autoNameRecursively("foo", Input(UInt(3.W))) +} +class Example1 extends MultiIOModule { + val io = IO(new MyBundle()) + // val io = autoNameRecursively("io", IO(new MyBundle())) +} +println(ChiselStage.emitVerilog(new Example1)) +``` + +Otherwise, it is rewritten to also include the name as a prefix to any signals generated while executing the right-hand- +side of the val declaration: + +```scala mdoc +class Example2 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + // val in = autoNameRecursively("in", prefix("in")(IO(Input(UInt(2.W))))) + + val out = IO(Output(UInt(2.W))) + // val out = autoNameRecursively("out", prefix("out")(IO(Output(UInt(2.W))))) + + def inXin() = in * in + + val add = 3.U + inXin() + // val add = autoNameRecursively("add", prefix("add")(3.U + inXin())) + // Note that the intermediate result of the multiplication is prefixed with `add` + + out := add + 1.U +} + +println(ChiselStage.emitVerilog(new Example2)) +``` + +Note that the naming also works if the hardware type is nested in an `Option` or a subtype of `Iterable`: + +```scala mdoc +class Example3 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + // val in = autoNameRecursively("in", prefix("in")(IO(Input(UInt(2.W))))) + + val out = IO(Output(UInt())) + // val out = autoNameRecursively("out", prefix("out")(IO(Output(UInt(2.W))))) + + def inXin() = in * in + + val opt = Some(3.U + inXin()) + // Note that the intermediate result of the inXin() is prefixed with `opt`: + // val opt = autoNameRecursively("opt", prefix("opt")(Some(3.U + inXin()))) + + out := opt.get + 1.U +} + +println(ChiselStage.emitVerilog(new Example3)) +``` + +### Prefixing + +As shown above, the compiler plugin automatically attempts to prefix some of your signals for you. However, you as a +user can also add your own prefixes. This is especially for ECO-type fixes where you need to add some logic to a module +but don't want to influence other names in the module. + +In the following example, we prefix additional logic with "ECO", where `Example4` is pre-ECO and `Example5` is post-ECO: + +```scala mdoc +class Example4 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = in + in + in + + out := add + 1.U +} + +class Example5 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = in + in + in + + out := prefix("ECO") { add + 1.U + in } +} + +println(ChiselStage.emitVerilog(new Example4)) +println(ChiselStage.emitVerilog(new Example5)) + +``` + +Also note that the prefixes append to each other (including the prefix generated by the compiler plugin): + +```scala mdoc +class Example6 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = prefix("foo") { in + in + in } + + out := add +} + +println(ChiselStage.emitVerilog(new Example6)) +``` + +Sometimes you may want to disable the prefixing. This might occur if you are writing a library function and +don't want the prefixing behavior. In this case, you can use the `noPrefix` object: + +```scala mdoc +class Example7 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = noPrefix { in + in + in } + + out := add +} + +println(ChiselStage.emitVerilog(new Example7)) +``` + +### Suggest a Signal's Name (or the instance name of a Module) + +If you want to specify the name of a signal, you can always use the `.suggestName` API. Please note that the suggested +name will still be prefixed (including by the plugin). You can always use the `noPrefix` object to strip this. + +```scala mdoc +class Example8 extends MultiIOModule { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(UInt())) + + val add = (in + (in + in).suggestName("foo")) + + out := add +} + +println(ChiselStage.emitVerilog(new Example8)) +``` + +### Set a Module Name + +If you want to specify the module's name (not the instance name of a module), you can always override the `desiredName` +value. Note that you can parameterize the name by the module's parameters. This is an excellent way to make your module +names more stable and is highly recommended to do. + +```scala mdoc +class Example9(width: Int) extends MultiIOModule { + override val desiredName = s"EXAMPLE9WITHWIDTH$width" + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt())) + + val add = (in + (in + in).suggestName("foo")) + + out := add +} + +println(ChiselStage.emitVerilog(new Example9(8))) +println(ChiselStage.emitVerilog(new Example9(1))) +``` + +### Reflection Naming + +Regardless of whether the compiler plugin is enabled or not, after Chisel constructs a module, it attempts to name all +members of the Module. This will name all vals which are fields of the module class, but it will not name any +vals in nested functions or scopes. + +If the plugin successfully names a signal, the reflection naming will do nothing. We plan to deprecate all reflection +naming in a future Chisel release, but are leaving it to allow the plugin naming to be optional (but recommended). + +For example, the signals in the following module are in a nested scope; the plugin successfully names them, but +reflection naming cannot: + +```scala mdoc +class Example10 extends MultiIOModule { + { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt())) + + val add = in + in + + out := add + } +} +``` + +### @chiselName + +This macro is no longer recommended as its functionality is entirely replaced by the compiler plugin. Feel free to +delete from your Chisel designs! \ No newline at end of file diff --git a/plugin/src/main/resources/scalac-plugin.xml b/plugin/src/main/resources/scalac-plugin.xml index b2b44fc6..abe51d12 100644 --- a/plugin/src/main/resources/scalac-plugin.xml +++ b/plugin/src/main/resources/scalac-plugin.xml @@ -1,4 +1,4 @@ chiselplugin - chisel3.plugin.ChiselPlugin + chisel3.internal.plugin.ChiselPlugin diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala new file mode 100644 index 00000000..2197430c --- /dev/null +++ b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala @@ -0,0 +1,138 @@ +// See LICENSE for license details. + +package chisel3.internal.plugin + +import scala.tools.nsc +import nsc.{Global, Phase} +import nsc.plugins.Plugin +import nsc.plugins.PluginComponent +import scala.reflect.internal.Flags +import scala.tools.nsc.transform.TypingTransformers + +// The plugin to be run by the Scala compiler during compilation of Chisel code +class ChiselPlugin(val global: Global) extends Plugin { + val name = "chiselplugin" + val description = "Plugin for Chisel 3 Hardware Description Language" + val components: List[PluginComponent] = List[PluginComponent](new ChiselComponent(global)) +} + +// The component of the chisel plugin. Not sure exactly what the difference is between +// a Plugin and a PluginComponent. +class ChiselComponent(val global: Global) extends PluginComponent with TypingTransformers { + import global._ + val runsAfter: List[String] = List[String]("typer") + val phaseName: String = "chiselcomponent" + def newPhase(_prev: Phase): ChiselComponentPhase = new ChiselComponentPhase(_prev) + class ChiselComponentPhase(prev: Phase) extends StdPhase(prev) { + override def name: String = phaseName + def apply(unit: CompilationUnit): Unit = { + // This plugin doesn't work on Scala 2.11. Rather than complicate the sbt build flow, + // instead we just check the version and if its an early Scala version, the plugin does nothing + if(scala.util.Properties.versionNumberString.split('.')(1).toInt >= 12) { + unit.body = new MyTypingTransformer(unit).transform(unit.body) + } + } + } + + class MyTypingTransformer(unit: CompilationUnit) + extends TypingTransformer(unit) { + + // Determines if the chisel plugin should match on this type + def shouldMatch(q: Type, bases: Seq[Tree]): Boolean = { + + // If subtype of Data or BaseModule, its a match! + def terminate(t: Type): Boolean = bases.exists { base => t <:< inferType(base) } + + // Recurse through subtype hierarchy finding containers + // Seen is only updated when we recurse into type parameters, thus it is typically small + def recShouldMatch(s: Type, seen: Set[Type]): Boolean = { + def outerMatches(t: Type): Boolean = { + val str = t.toString + str.startsWith("Option[") || str.startsWith("Iterable[") + } + if (terminate(s)) { + true + } else if (seen.contains(s)) { + false + } else if (outerMatches(s)) { + // These are type parameters, loops *are* possible here + recShouldMatch(s.typeArgs.head, seen + s) + } else { + // This is the standard inheritance hierarchy, Scalac catches loops here + s.parents.exists( p => recShouldMatch(p, seen) ) + } + } + + // If doesn't match container pattern, exit early + def earlyExit(t: Type): Boolean = { + !(t.matchesPattern(inferType(tq"Iterable[_]")) || t.matchesPattern(inferType(tq"Option[_]"))) + } + + // First check if a match, then check early exit, then recurse + if(terminate(q)){ + true + } else if(earlyExit(q)) { + false + } else { + recShouldMatch(q, Set.empty) + } + } + + // Given a type tree, infer the type and return it + def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe + + // Indicates whether a ValDef is properly formed to get name + def okVal(dd: ValDef, bases: Tree*): Boolean = { + + // These were found through trial and error + def okFlags(mods: Modifiers): Boolean = { + val badFlags = Set( + Flag.PARAM, + Flag.SYNTHETIC, + Flag.DEFERRED, + Flags.TRIEDCOOKING, + Flags.CASEACCESSOR, + Flags.PARAMACCESSOR + ) + badFlags.forall{ x => !mods.hasFlag(x)} + } + + // Ensure expression isn't null, as you can't call `null.autoName("myname")` + val isNull = dd.rhs match { + case Literal(Constant(null)) => true + case _ => false + } + okFlags(dd.mods) && shouldMatch(inferType(dd.tpt), bases) && !isNull && dd.rhs != EmptyTree + } + + // Whether this val is directly enclosed by a Bundle type + def inBundle(dd: ValDef): Boolean = { + dd.symbol.logicallyEnclosingMember.thisType <:< inferType(tq"chisel3.Bundle") + } + + // Method called by the compiler to modify source tree + override def transform(tree: Tree): Tree = tree match { + // If a Data and in a Bundle, just get the name but not a prefix + case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data") && inBundle(dd) => + val TermName(str: String) = name + val newRHS = transform(rhs) + val named = q"chisel3.internal.plugin.autoNameRecursively($str, $newRHS)" + treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) + // If a Data or a Memory, get the name and a prefix + case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data", tq"chisel3.MemBase[_]") => + val TermName(str: String) = name + val newRHS = transform(rhs) + val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" + val named = q"chisel3.internal.plugin.autoNameRecursively($str, $prefixed)" + treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) + // If an instance, just get a name but no prefix + case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.experimental.BaseModule") => + val TermName(str: String) = name + val newRHS = transform(rhs) + val named = q"chisel3.internal.plugin.autoNameRecursively($str, $newRHS)" + treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) + // Otherwise, continue + case _ => super.transform(tree) + } + } +} diff --git a/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala deleted file mode 100644 index 3b3b52e9..00000000 --- a/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala +++ /dev/null @@ -1,138 +0,0 @@ -// See LICENSE for license details. - -package chisel3.plugin - -import scala.tools.nsc -import nsc.{Global, Phase} -import nsc.plugins.Plugin -import nsc.plugins.PluginComponent -import scala.reflect.internal.Flags -import scala.tools.nsc.transform.TypingTransformers - -// The plugin to be run by the Scala compiler during compilation of Chisel code -class ChiselPlugin(val global: Global) extends Plugin { - val name = "chiselplugin" - val description = "Plugin for Chisel 3 Hardware Description Language" - val components = List[PluginComponent](new ChiselComponent(global)) -} - -// The component of the chisel plugin. Not sure exactly what the difference is between -// a Plugin and a PluginComponent. -class ChiselComponent(val global: Global) extends PluginComponent with TypingTransformers { - import global._ - val runsAfter = List[String]("typer") - val phaseName: String = "chiselcomponent" - def newPhase(_prev: Phase): ChiselComponentPhase = new ChiselComponentPhase(_prev) - class ChiselComponentPhase(prev: Phase) extends StdPhase(prev) { - override def name: String = phaseName - def apply(unit: CompilationUnit): Unit = { - // This plugin doesn't work on Scala 2.11. Rather than complicate the sbt build flow, - // instead we just check the version and if its an early Scala version, the plugin does nothing - if(scala.util.Properties.versionNumberString.split('.')(1).toInt >= 12) { - unit.body = new MyTypingTransformer(unit).transform(unit.body) - } - } - } - - class MyTypingTransformer(unit: CompilationUnit) - extends TypingTransformer(unit) { - - // Determines if the chisel plugin should match on this type - def shouldMatch(q: Type, bases: Seq[Tree]): Boolean = { - - // If subtype of Data or BaseModule, its a match! - def terminate(t: Type): Boolean = bases.exists { base => t <:< inferType(base) } - - // Recurse through subtype hierarchy finding containers - // Seen is only updated when we recurse into type parameters, thus it is typically small - def recShouldMatch(s: Type, seen: Set[Type]): Boolean = { - def outerMatches(t: Type): Boolean = { - val str = t.toString - str.startsWith("Option[") || str.startsWith("Iterable[") - } - if (terminate(s)) { - true - } else if (seen.contains(s)) { - false - } else if (outerMatches(s)) { - // These are type parameters, loops *are* possible here - recShouldMatch(s.typeArgs.head, seen + s) - } else { - // This is the standard inheritance hierarchy, Scalac catches loops here - s.parents.exists( p => recShouldMatch(p, seen) ) - } - } - - // If doesn't match container pattern, exit early - def earlyExit(t: Type): Boolean = { - !(t.matchesPattern(inferType(tq"Iterable[_]")) || t.matchesPattern(inferType(tq"Option[_]"))) - } - - // First check if a match, then check early exit, then recurse - if(terminate(q)){ - true - } else if(earlyExit(q)) { - false - } else { - recShouldMatch(q, Set.empty) - } - } - - // Given a type tree, infer the type and return it - def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe - - // Indicates whether a ValDef is properly formed to get name - def okVal(dd: ValDef, bases: Tree*): Boolean = { - - // These were found through trial and error - def okFlags(mods: Modifiers): Boolean = { - val badFlags = Set( - Flag.PARAM, - Flag.SYNTHETIC, - Flag.DEFERRED, - Flags.TRIEDCOOKING, - Flags.CASEACCESSOR, - Flags.PARAMACCESSOR - ) - badFlags.forall{ x => !mods.hasFlag(x)} - } - - // Ensure expression isn't null, as you can't call `null.autoName("myname")` - val isNull = dd.rhs match { - case Literal(Constant(null)) => true - case _ => false - } - okFlags(dd.mods) && shouldMatch(inferType(dd.tpt), bases) && !isNull && dd.rhs != EmptyTree - } - - // Whether this val is directly enclosed by a Bundle type - def inBundle(dd: ValDef): Boolean = { - dd.symbol.logicallyEnclosingMember.thisType <:< inferType(tq"chisel3.Bundle") - } - - // Method called by the compiler to modify source tree - override def transform(tree: Tree): Tree = tree match { - // If a Data and in a Bundle, just get the name but not a prefix - case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data") && inBundle(dd) => - val TermName(str: String) = name - val newRHS = super.transform(rhs) - val named = q"chisel3.experimental.autoNameRecursively($str, $newRHS)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) - // If a Data or a Memory, get the name and a prefix - case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data", tq"chisel3.MemBase[_]") => - val TermName(str: String) = name - val newRHS = super.transform(rhs) - val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)" - val named = q"chisel3.experimental.autoNameRecursively($str, $prefixed)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) - // If an instance, just get a name but no prefix - case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.experimental.BaseModule") => - val TermName(str: String) = name - val newRHS = super.transform(rhs) - val named = q"chisel3.experimental.autoNameRecursively($str, $newRHS)" - treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named) - // Otherwise, continue - case _ => super.transform(tree) - } - } -} diff --git a/src/test/scala/chiselTests/naming/NamePluginSpec.scala b/src/test/scala/chiselTests/naming/NamePluginSpec.scala index fc90264d..946b557a 100644 --- a/src/test/scala/chiselTests/naming/NamePluginSpec.scala +++ b/src/test/scala/chiselTests/naming/NamePluginSpec.scala @@ -203,5 +203,23 @@ class NamePluginSpec extends ChiselFlatSpec with Utils { y } } + + "Nested val declarations" should "all be named" in { + class Test extends MultiIOModule { + { + val a = { + val b = { + val c = Wire(UInt(3.W)) + Wire(UInt(3.W)) + } + Wire(UInt(3.W)) + } + } + } + + aspectTest(() => new Test) { + top: Test => Select.wires(top).map(_.instanceName) should be (List("a_b_c", "a_b", "a")) + } + } } -- cgit v1.2.3