Skip to content

Commit

Permalink
Add APIs to omit module prefix separator (#4532)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkoenig authored Nov 27, 2024
1 parent 118502e commit c60ef2f
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 23 deletions.
36 changes: 26 additions & 10 deletions core/src/main/scala/chisel3/ModuleImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,6 @@ private[chisel3] trait ObjectModuleImpl {
/** Explicitly Asynchronous Reset */
case object Asynchronous extends Type
}

def getModulePrefixList: List[String] = {
Builder.getModulePrefixList
}
}

private[chisel3] trait ModuleImpl extends RawModule with ImplicitClock with ImplicitReset {
Expand Down Expand Up @@ -946,18 +942,27 @@ package experimental {
*/
def localModulePrefixAppliesToSelf: Boolean = true

/** Should the localModulePrefix include a separator between prefix and the Module name
*
* Defaults to true, users can override to false if they don't want a separator.
*/
def localModulePrefixUseSeparator: Boolean = true

/** The resolved module prefix used for this Module.
*
* Includes [[localModulePrefix]] if defined and if [[localModulePrefixAppliesToSelf]] is true.
*/
final val modulePrefix: String =
withModulePrefix(localModulePrefix.filter(_ => localModulePrefixAppliesToSelf).getOrElse("")) {
withModulePrefix(
localModulePrefix.filter(_ => localModulePrefixAppliesToSelf).getOrElse(""),
localModulePrefixUseSeparator
) {
Builder.getModulePrefix
}

// Apply localModulePrefix to children.
localModulePrefix.foreach { prefix =>
Builder.pushModulePrefix(prefix)
Builder.pushModulePrefix(prefix, localModulePrefixUseSeparator)
}
}
}
Expand All @@ -967,12 +972,23 @@ package experimental {
*/
object withModulePrefix {

/**
* @param arg prefix Prefix is the module prefix, blank means ignore.
/** Prefixes modules with the given prefix
*
* Uses default separator.
*
* @param prefix The module prefix, blank means ignore.
*/
def apply[T](prefix: String)(block: => T): T =
apply(prefix, true)(block)

/** Prefixes modules with the given prefix
*
* @param prefix The module prefix, blank means ignore.
* @param includeSeparator Include the separator after the prefix
*/
def apply[T](prefix: String)(block: => T): T = {
def apply[T](prefix: String, includeSeparator: Boolean)(block: => T): T = {
if (prefix != "") {
Builder.pushModulePrefix(prefix)
Builder.pushModulePrefix(prefix, includeSeparator)
}
val res = block // execute block
if (prefix != "") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private[chisel3] trait InstantiateImpl {

import chisel3.internal.BuilderContextCache
// Include type of module in key since different modules could have the same arguments
private case class CacheKey[A <: BaseModule](args: Any, tt: Any, modulePrefix: List[String])
private case class CacheKey[A <: BaseModule](args: Any, tt: Any, modulePrefix: String)
extends BuilderContextCache.Key[Definition[A]]

protected def _instanceImpl[K, A <: BaseModule](
Expand All @@ -90,7 +90,7 @@ private[chisel3] trait InstantiateImpl {
f: K => A,
tt: Any
): Definition[A] = {
val modulePrefix = Builder.getModulePrefixList
val modulePrefix = Module.currentModulePrefix
Builder.contextCache
.getOrElseUpdate(
CacheKey[A](boxAllData(args), tt, modulePrefix), {
Expand Down
22 changes: 11 additions & 11 deletions core/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,8 @@ private[chisel3] class ChiselContext() {
// and the resulting .toTarget is very clearly useless (_$$View$$_...)
val viewNamespace = Namespace.empty

var modulePrefixStack: Prefix = Nil
// Pairs of Prefix and whether to include the separator
var modulePrefixStack: List[(String, Boolean)] = Nil
}

private[chisel3] class DynamicContext(
Expand Down Expand Up @@ -1168,33 +1169,32 @@ private[chisel3] object Builder extends LazyLogging {
}

// Puts a module prefix string onto the module prefix stack
def pushModulePrefix(prefix: String): Unit = {
def pushModulePrefix(prefix: String, includeSeparator: Boolean): Unit = {
val context = chiselContext.get()
context.modulePrefixStack = prefix :: context.modulePrefixStack
context.modulePrefixStack = (prefix, includeSeparator) :: context.modulePrefixStack
}

// Remove the module prefix on top of the stack
def popModulePrefix(): List[String] = {
def popModulePrefix(): Unit = {
val context = chiselContext.get()
val tail = context.modulePrefixStack.tail
context.modulePrefixStack = tail
tail
}

// Returns the nested module prefix at this moment
def getModulePrefix: String = {
val ctx = chiselContext.get()
val modulePrefixStack = ctx.modulePrefixStack
val sep = ctx.modulePrefixSeperator
if (modulePrefixStack.isEmpty) {
""
} else {
modulePrefixStack.foldLeft("")((a, b) => b + sep + a)
modulePrefixStack.foldLeft("") {
case (acc, (elt, useSep)) =>
val s = if (useSep) sep else ""
elt + s + acc
}
}

def getModulePrefixList: List[String] = {
chiselContext.get().modulePrefixStack
def getModulePrefixSeperator: String = {
chiselContext.get().modulePrefixSeperator
}

initializeSingletons()
Expand Down
36 changes: 36 additions & 0 deletions docs/src/explanations/moduleprefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ The result will be a design with two module definitions: `Top` and `Foo_Sub`.
Note that the `val sub =` part must be pulled outside of the `withModulePrefix` block,
or else the module will not be accessible to the rest of the `Top` module.

You can omit the prefix separator (`_`) by passing `false` as the second argument:

```scala mdoc:silent:reset
import chisel3._

class Top extends Module {
val sub = withModulePrefix("Foo", false) {
Module(new Sub)
}
}

class Sub extends Module {
// ..
}
```

This results in two module definitions: `Top` and `FooSub`.

## localModulePrefix

We can also set a module prefix on a module by overriding the `localModulePrefix` method.
Expand Down Expand Up @@ -81,6 +99,24 @@ class Sub extends Module {

This results in the two module definitions `Top` and `Foo_Sub`.

You can also override `localModulePrefixUseSeparator` to `false` to omit the separator.

```scala mdoc:silent:reset
import chisel3._

class Top extends Module {
override def localModulePrefix = Some("Foo")
override def localModulePrefixUseSeparator = false
val sub = Module(new Sub)
}

class Sub extends Module {
// ..
}
```

This results in the two module definitions `FooTop` and `FooSub`.

## Multiple Prefixes

If a generator is run in multiple prefix blocks, the result is multiple identical copies of the module definition,
Expand Down
68 changes: 68 additions & 0 deletions src/test/scala/chiselTests/ModulePrefixSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,24 @@ class ModulePrefixSpec extends ChiselFlatSpec with ChiselRunners with Utils with
matchesAndOmits(chirrtl)(lines: _*)()
}

it should "support omitting the separator" in {
class Foo extends Module
class Top extends Module {
val foo = withModulePrefix("Prefix", false) {
Module(new Foo)
}
}

val chirrtl = emitCHIRRTL(new Top)

val lines = """
module PrefixFoo :
module Top :
inst foo of PrefixFoo
""".linesIterator.map(_.trim).toSeq
matchesAndOmits(chirrtl)(lines: _*)()
}

behavior.of("BaseModule.localModulePrefix")

it should "set the prefix for a Module and its children" in {
Expand Down Expand Up @@ -357,4 +375,54 @@ class ModulePrefixSpec extends ChiselFlatSpec with ChiselRunners with Utils with

matchesAndOmits(chirrtl)(lines: _*)()
}

it should "omit the prefix if localModulePrefixUseSeparator is false" in {
class Foo extends RawModule {
override def localModulePrefix = Some("Inner")
override def localModulePrefixUseSeparator = false
}
class Top extends RawModule {
override def localModulePrefix = Some("Outer")
override def localModulePrefixUseSeparator = false
override def localModulePrefixAppliesToSelf = false
val foo = Module(new Foo)
}

val chirrtl = emitCHIRRTL(new Top)
val lines =
"""
module OuterInnerFoo :
module Top :
inst foo of OuterInnerFoo
""".linesIterator.map(_.trim).toSeq

matchesAndOmits(chirrtl)(lines: _*)()
}

it should "support mixing and matching of separator omission" in {
class Foo extends RawModule {
override def localModulePrefix = Some("Inner")
}
class Bar extends RawModule {
override def localModulePrefix = Some("Middle")
val foo = Module(new Foo)
}
class Top extends RawModule {
override def localModulePrefix = Some("Outer")
override def localModulePrefixUseSeparator = false
val bar = Module(new Bar)
}

val chirrtl = emitCHIRRTL(new Top)
val lines =
"""
module OuterMiddle_Inner_Foo :
module OuterMiddle_Bar :
inst foo of OuterMiddle_Inner_Foo
module OuterTop :
inst bar of OuterMiddle_Bar
""".linesIterator.map(_.trim).toSeq

matchesAndOmits(chirrtl)(lines: _*)()
}
}

0 comments on commit c60ef2f

Please sign in to comment.