Skip to content

Commit

Permalink
Add module prefixing to BaseModule definitions (#4509)
Browse files Browse the repository at this point in the history
* BaseModule.localModulePrefix can be used to set a prefix for the
  module and its children.
* BaseModule.localPrefixAppliesToSelf (defaults to true) allows the
  module to exclude itself from the prefix.
* localModulePrefix composes with prefixes added via withModulePrefix.
  • Loading branch information
jackkoenig authored Nov 14, 2024
1 parent 4fa08f8 commit 9c2498a
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 13 deletions.
30 changes: 27 additions & 3 deletions core/src/main/scala/chisel3/ModuleImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ private[chisel3] trait ObjectModuleImpl {
Builder.currentReset = saveReset
Builder.setPrefix(savePrefix)
Builder.enabledLayers = saveEnabledLayers
if (module.localModulePrefix.isDefined) {
Builder.popModulePrefix() // Pop localModulePrefix if it was defined
}

module.moduleBuilt()
module
Expand Down Expand Up @@ -696,7 +699,7 @@ package experimental {
this match {
case _: PseudoModule => Module.currentModulePrefix + desiredName
case _: BaseBlackBox => Builder.globalNamespace.name(desiredName)
case _ => Builder.globalNamespace.name(Module.currentModulePrefix + desiredName)
case _ => Builder.globalNamespace.name(this.modulePrefix + desiredName)
}
} catch {
case e: NullPointerException =>
Expand Down Expand Up @@ -933,8 +936,29 @@ package experimental {
case Some(c) => getRef.fullName(c)
}

/** Returns the current nested module prefix */
val modulePrefix: String = Builder.getModulePrefix
/** Additional module prefix, applies to this module if defined (unless localModulePrefix is false) and all children.
*/
def localModulePrefix: Option[String] = None

/** Should [[localModulePrefix]] apply to this module? Defaults to true.
*
* Users should override to false if [[localModulePrefix]] should apply only to children.
*/
def localPrefixAppliesToSelf: Boolean = true

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

// Apply localModulePrefix to children.
localModulePrefix.foreach { prefix =>
Builder.pushModulePrefix(prefix)
}
}
}

Expand Down
55 changes: 52 additions & 3 deletions docs/src/explanations/moduleprefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Module prefixing allows you to create namespaces in the Verilog output of your d
They are especially useful for when you want to name a particular subsystem of your design,
and you want to make it easy to identify which subsystem a file belongs to by its name.

## withModulePrefix

We can open a module prefix block using `withModulePrefix`:

```scala mdoc:silent
Expand Down Expand Up @@ -41,6 +43,46 @@ 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.

## localModulePrefix

We can also set a module prefix on a module by overriding the `localModulePrefix` method.
This is useful if you want to set a prefix for all instances of a module.

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

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

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

This results in two module definitions: `Foo_Top` and `Foo_Sub`.

You can also override `localPrefixAppliesToSelf` to `false` to only apply the prefix to the children.

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

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

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

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

## Multiple Prefixes

If a generator is run in multiple prefix blocks, the result is multiple identical copies of the module definition,
each with its own distinct prefix.
For example, consider if we create two instances of `Sub` above like this:
Expand All @@ -66,6 +108,8 @@ class Sub extends Module {
Then, the resulting Verilog will have three module definitions: `Top`, `Foo_Sub`, and `Bar_Sub`.
Both `Foo_Sub` and `Bar_Sub` will be identical to each other.

## Nested Prefixes

Module prefixes can also be nested.

```scala mdoc:silent:reset
Expand All @@ -78,9 +122,10 @@ class Top extends Module {
}

class Mid extends Module {
val sub = withModulePrefix("Bar") {
Module(new Sub)
}
// You can mix withModulePrefix and localModulePrefix.
override def localModulePrefix = Some("Bar")
override def localPrefixAppliesToSelf = false
val sub = Module(new Sub)
}

class Sub extends Module {
Expand All @@ -90,6 +135,8 @@ class Sub extends Module {

This results in three module definitions: `Top`, `Foo_Mid`, and `Foo_Bar_Sub`.

## Instantiate

The `withModulePrefix` blocks also work with the `Instantiate` API.

```scala mdoc:silent:reset
Expand Down Expand Up @@ -119,6 +166,8 @@ In this example, we end up with four modules: `Top`, `Foo_Sub`, `Bar_Sub`, and `
When using `Definition` and `Instance`, all `Definition` calls will be affected by `withModulePrefix`.
However, `Instance` will not be effected, since it always creates an instance of the captured definition.

## External Modules

`BlackBox` and `ExtModule` are unaffected by `withModulePrefix`.
If you wish to have one that is sensitive to the module prefix,
you can explicitly name the module like this:
Expand Down
8 changes: 4 additions & 4 deletions src/test/scala/chiselTests/ChiselSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ trait Utils {
}

/** Contains helpful function to assert both statements to match, and statements to omit */
trait MatchesAndOmits {
trait MatchesAndOmits extends Assertions {
private def matches(lines: List[String], matchh: String): Option[String] = lines.filter(_.contains(matchh)).lastOption
private def omits(line: String, omit: String): Option[(String, String)] =
if (line.contains(omit)) Some((omit, line)) else None
Expand All @@ -374,11 +374,11 @@ trait MatchesAndOmits {
val lines = output.split("\n").toList
val unmatched = matchList.flatMap { m =>
if (matches(lines, m).nonEmpty) None else Some(m)
}.map(x => s" > $x was unmatched")
}.map(x => s" > '$x' was unmatched")
val unomitted = omitList.flatMap { o => omits(lines, o) }.map {
case (o, l) => s" > $o was not omitted in ($l)"
case (o, l) => s" > '$o' was not omitted in ($l)"
}
val results = unmatched ++ unomitted
assert(results.isEmpty, results.mkString("\n"))
assert(results.isEmpty, results.mkString("\n") + s"\nFull Input:\n'$output'\n")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import circt.stage.ChiselStage.emitCHIRRTL
import circt.stage.ChiselStage
import chisel3.util.SRAM

object PrefixSpec {
object ModulePrefixSpec {
// This has to be defined at the top-level because @instantiable doesn't work when nested.
@instantiable
class AddOne(width: Int) extends Module {
Expand All @@ -19,8 +19,8 @@ object PrefixSpec {
}
}

class PrefixSpec extends ChiselFlatSpec with ChiselRunners with Utils with MatchesAndOmits {
import PrefixSpec._
class ModulePrefixSpec extends ChiselFlatSpec with ChiselRunners with Utils with MatchesAndOmits {
import ModulePrefixSpec._
behavior.of("withModulePrefix")

it should "prefix modules in a withModulePrefix block, but not outside" in {
Expand Down Expand Up @@ -275,4 +275,86 @@ class PrefixSpec extends ChiselFlatSpec with ChiselRunners with Utils with Match

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

behavior.of("BaseModule.localModulePrefix")

it should "set the prefix for a Module and its children" in {

class Foo extends RawModule
class Bar extends RawModule

class Top extends RawModule {
override def localModulePrefix = Some("Prefix")
val foo = Module(new Foo)
val bar = Module(new Bar)
}

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

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

it should "set the prefix for a Module's children but not the Module itself if localPrefixAppliesToSelf is false" in {

class Foo extends RawModule
class Bar extends RawModule

class Top extends RawModule {
override def localModulePrefix = Some("Prefix")
override def localPrefixAppliesToSelf = false
val foo = Module(new Foo)
val bar = Module(new Bar)
}

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

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

it should "compose with withModulePrefix" in {

class Foo extends RawModule {
override def localModulePrefix = Some("Inner")
}
class Bar extends RawModule

class Top extends RawModule {
override def localModulePrefix = Some("Outer")
val f1 = Module(new Foo)
withModulePrefix("Prefix") {
val f2 = Module(new Foo)
val bar = Module(new Bar)
}
}

val chirrtl = emitCHIRRTL(new Top)
val lines =
"""
module Outer_Inner_Foo :
module Outer_Prefix_Inner_Foo :
module Outer_Prefix_Bar :
module Outer_Top :
inst f1 of Outer_Inner_Foo
inst f2 of Outer_Prefix_Inner_Foo
inst bar of Outer_Prefix_Bar
""".linesIterator.map(_.trim).toSeq

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

0 comments on commit 9c2498a

Please sign in to comment.