diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 56dce4d592d..bdcbc7f0d86 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -88,6 +88,8 @@ object Module extends SourceInfoDoc { def reset: Reset = Builder.forcedReset /** Returns the current Module */ def currentModule: Option[BaseModule] = Builder.currentModule + /** Returns the current nested module prefix */ + def currentModulePrefix: String = Builder.getModulePrefix } /** Abstract base class for Modules, which behave much like Verilog modules. @@ -140,6 +142,52 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw } } +/** Creates a new module prefix scope that prefixes all instantiated modules + * + * @example {{{ + * val m = Module(new Module { + * val child = withModulePrefix("Foo") { + * Module(new Module { + * override val desiredName = "Module" + * }) + * } + * }) + * + * // m.child.name will be equal to "FooModule" + * }}} + * + * Module prefixes can be nested within each other, like so: + * @example {{{ + * val m = ChiselStage.construct(new Module { + * val child = withModulePrefix("Foo") { + * Module(new chisel3.Module { + * val nestedChild = withModulePrefix("Bar") { + * Module(new chisel3.Module { + * override val desiredName = "Module" + * }) + * } + * }) + * } + * }) + * + * // m.child.nestedChild.name will be equal to "FooBarModule" + * }}} + */ +object withModulePrefix { + /** Creates a new Module prefix scope + * + * @param prefix the prefix to add to all modules in the scope + * @param block the block of code to run with the new prefix + * @return the result of the block + */ + def apply[T](prefix: String)(block: => T): T = { + Builder.pushModulePrefix(prefix) + val res = block // execute block + Builder.popModulePrefix() + res + } + +} package experimental { @@ -450,8 +498,9 @@ package experimental { // PseudoModules are not "true modules" and thus should share // their original modules names without uniquification this match { - case _: PseudoModule => desiredName - case _ => Builder.globalNamespace.name(desiredName) + case _: PseudoModule => Module.currentModulePrefix + desiredName + case _: BlackBox => Builder.globalNamespace.name(desiredName) + case _ => Module.currentModulePrefix + Builder.globalNamespace.name(desiredName) } } catch { case e: NullPointerException => throwException( diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 0a0a3f2ddc6..356ae6c7b26 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -349,6 +349,9 @@ private[chisel3] class ChiselContext() { // Records the different prefixes which have been scoped at this point in time var prefixStack: Prefix = Nil + // Records the current nested module prefix + var modulePrefixStack: Prefix = Nil + // Views belong to a separate namespace (for renaming) // The namespace outside of Builder context is useless, but it ensures that views can still be created // and the resulting .toTarget is very clearly useless (_$$View$$_...) @@ -498,6 +501,23 @@ private[chisel3] object Builder extends LazyLogging { // Returns the prefix stack at this moment def getPrefix: Prefix = chiselContext.get().prefixStack + // Puts a module prefix string onto the module prefix stack + def pushModulePrefix(prefix: String): Unit = { + val context = chiselContext.get() + context.modulePrefixStack = prefix :: context.modulePrefixStack + } + + // Remove the module prefix on top of the stack + def popModulePrefix(): List[String] = { + val context = chiselContext.get() + val tail = context.modulePrefixStack.tail + context.modulePrefixStack = tail + tail + } + + // Returns the nested module prefix at this moment + def getModulePrefix: String = chiselContext.get().modulePrefixStack.foldLeft("")((a, b) => b + a) + def currentModule: Option[BaseModule] = dynamicContextVar.value match { case Some(dyanmicContext) => dynamicContext.currentModule case _ => None diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala index 7703e8765ed..358ccae78a1 100644 --- a/src/test/scala/chiselTests/Module.scala +++ b/src/test/scala/chiselTests/Module.scala @@ -5,6 +5,7 @@ package chiselTests import chisel3._ import chisel3.experimental.DataMirror import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation} +import chisel3.util.HasBlackBoxInline import firrtl.annotations.NoTargetAnnotation import firrtl.options.Unserializable @@ -200,4 +201,84 @@ class ModuleSpec extends ChiselPropSpec with Utils { val s = Source.fromFile("generated/PlusOne.v").mkString("") assert(s.contains("assign io_out = io_in + 32'h1")) } + + property("withModulePrefix should prefix modules within it") { + val m = elaborateAndGetModule(new Module { + val child = withModulePrefix("Foo") { + Module(new chisel3.Module { + Module.currentModulePrefix should be ("Foo") + + override val desiredName = "Module" + }) + } + }) + + m.child.name should be ("FooModule") + } + + property("withModulePrefix should support nested module prefixing") { + val m = elaborateAndGetModule(new Module { + val child = withModulePrefix("Foo") { + Module(new chisel3.Module { + Module.currentModulePrefix should be ("Foo") + + val nestedChild = withModulePrefix("Bar") { + Module(new chisel3.Module { + Module.currentModulePrefix should be ("FooBar") + + override val desiredName = "Module" + }) + } + }) + } + }) + + m.child.nestedChild.name should be ("FooBarModule") + } + + property("withModulePrefix should not prefix if given an empty argument") { + val m = elaborateAndGetModule(new Module { + val child = withModulePrefix("") { + Module(new chisel3.Module { + Module.currentModulePrefix should be ("") + override val desiredName = "NoPrefixModule" + }) + } + }) + + m.child.name should be ("NoPrefixModule") + } + + property("withModulePrefix should not prefix blackboxes") { + val m = elaborateAndGetModule(new Module { + val bb = withModulePrefix("Foo") { + Module(new BlackBox { + val io = IO(new Bundle { }) + override val desiredName = "BlackBox" + }) + } + }) + + m.bb.name should be ("BlackBox") + } + + property("withModulePrefix should not prefix inlined blackboxes") { + val m = elaborateAndGetModule(new Module { + val bb = withModulePrefix("Foo") { + Module(new BlackBox with HasBlackBoxInline { + val io = IO(new Bundle { }) + override val desiredName = "InlineBlackBox" + + setInline("InlineBlackBox.v", + s""" + |module InlineBlackBox (); + | + |endmodule + """.stripMargin) + }) + } + }) + + m.bb.name should be ("InlineBlackBox") + } }