Skip to content

Commit 0e19171

Browse files
mmaloney-sfjackkoenigmwachs5
authored
Add withModulePrefix (#4487)
withModulePrefix can be used to create "prefixing domains" which will prefix all modules instantiated within them. These prefixes apply recursively to children of prefixed modules. Applying multiple prefixes will nest them. Prefixes are separated from the name of the module (and each other) by `_`. Co-authored-by: Jack Koenig <[email protected]> Co-authored-by: Megan Wachs <[email protected]>
1 parent 57c95ce commit 0e19171

File tree

9 files changed

+516
-9
lines changed

9 files changed

+516
-9
lines changed

core/src/main/scala-2/chisel3/experimental/hierarchy/Instantiate.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ object Instantiate extends InstantiateImpl {
5757
): Instance[A] = _instanceImpl(args, f)
5858

5959
/** This is not part of the public API, do not call directly! */
60-
def _defination[K, A <: BaseModule: ru.WeakTypeTag](
60+
def _definition[K, A <: BaseModule: ru.WeakTypeTag](
6161
args: K,
6262
f: K => A
6363
): Definition[A] = _definitionImpl(args, f)
@@ -160,11 +160,11 @@ object Instantiate extends InstantiateImpl {
160160
val funcArgTypes = args.map(_.asInstanceOf[Tree].tpe.widen)
161161
val constructor = q"(($funcArg: (..$funcArgTypes)) => new $tpname[..$tparams](...$conArgs))"
162162
val tup = q"(..$args)"
163-
q"chisel3.experimental.hierarchy.Instantiate._defination[(..$funcArgTypes), $tpname]($tup, $constructor)"
163+
q"chisel3.experimental.hierarchy.Instantiate._definition[(..$funcArgTypes), $tpname]($tup, $constructor)"
164164

165165
case _ =>
166166
val msg =
167-
s"Argument to Instantiate.defination(...) must be of form 'new <T <: chisel3.Module>(<arguments...>)'.\n" +
167+
s"Argument to Instantiate.definition(...) must be of form 'new <T <: chisel3.Module>(<arguments...>)'.\n" +
168168
"Note that named arguments are currently not supported.\n" +
169169
s"Got: '$con'"
170170
c.error(con.pos, msg)

core/src/main/scala/chisel3/MemImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ private[chisel3] trait ObjectMemImpl {
2323
val mem = new Mem(mt, size, sourceInfo)
2424
mt.bind(MemTypeBinding(mem))
2525
pushCommand(DefMemory(sourceInfo, mem, mt, size))
26+
ModulePrefixAnnotation.annotate(mem)
2627
mem
2728
}
2829

@@ -207,6 +208,7 @@ private[chisel3] trait ObjectSyncReadMemImpl {
207208
val mem = new SyncReadMem(mt, size, ruw, sourceInfo)
208209
mt.bind(MemTypeBinding(mem))
209210
pushCommand(DefSeqMemory(sourceInfo, mem, mt, size, ruw))
211+
ModulePrefixAnnotation.annotate(mem)
210212
mem
211213
}
212214

core/src/main/scala/chisel3/ModuleImpl.scala

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,27 @@ import chisel3.internal.firrtl.ir._
1212
import chisel3.experimental.{requireIsChiselType, BaseModule, SourceInfo, UnlocatableSourceInfo}
1313
import chisel3.properties.{Class, Property}
1414
import chisel3.reflect.DataMirror
15-
import _root_.firrtl.annotations.{InstanceTarget, IsModule, ModuleName, ModuleTarget}
15+
import _root_.firrtl.annotations.{
16+
Annotation,
17+
InstanceTarget,
18+
IsMember,
19+
IsModule,
20+
ModuleName,
21+
ModuleTarget,
22+
SingleTargetAnnotation,
23+
Target
24+
}
1625
import _root_.firrtl.AnnotationSeq
1726
import chisel3.internal.plugin.autoNameRecursively
1827
import chisel3.util.simpleClassName
28+
import chisel3.experimental.{annotate, ChiselAnnotation}
1929
import chisel3.experimental.hierarchy.Hierarchy
2030

2131
private[chisel3] trait ObjectModuleImpl {
2232

2333
protected def _applyImpl[T <: BaseModule](bc: => T)(implicit sourceInfo: SourceInfo): T = {
2434
// Instantiate the module definition.
25-
val module = evaluate[T](bc)
35+
val module: T = evaluate[T](bc)
2636

2737
// Handle connections at enclosing scope
2838
// We use _component because Modules that don't generate them may still have one
@@ -165,6 +175,9 @@ private[chisel3] trait ObjectModuleImpl {
165175
/** Returns the current Module */
166176
def currentModule: Option[BaseModule] = Builder.currentModule
167177

178+
/** Returns the current nested module prefix */
179+
def currentModulePrefix: String = Builder.getModulePrefix
180+
168181
private[chisel3] def do_pseudo_apply[T <: BaseModule](
169182
bc: => T
170183
)(
@@ -225,6 +238,10 @@ private[chisel3] trait ObjectModuleImpl {
225238
/** Explicitly Asynchronous Reset */
226239
case object Asynchronous extends Type
227240
}
241+
242+
def getModulePrefixList: List[String] = {
243+
Builder.getModulePrefixList
244+
}
228245
}
229246

230247
private[chisel3] trait ModuleImpl extends RawModule with ImplicitClock with ImplicitReset {
@@ -677,8 +694,9 @@ package experimental {
677694
// PseudoModules are not "true modules" and thus should share
678695
// their original modules names without uniquification
679696
this match {
680-
case _: PseudoModule => desiredName
681-
case _ => Builder.globalNamespace.name(desiredName)
697+
case _: PseudoModule => Module.currentModulePrefix + desiredName
698+
case _: BaseBlackBox => Builder.globalNamespace.name(desiredName)
699+
case _ => Builder.globalNamespace.name(Module.currentModulePrefix + desiredName)
682700
}
683701
} catch {
684702
case e: NullPointerException =>
@@ -915,5 +933,43 @@ package experimental {
915933
case Some(c) => getRef.fullName(c)
916934
}
917935

936+
/** Returns the current nested module prefix */
937+
val modulePrefix: String = Builder.getModulePrefix
938+
}
939+
}
940+
941+
/**
942+
* Creates a block under which any generator that gets run results in a module whose name is prepended with the given prefix.
943+
*/
944+
object withModulePrefix {
945+
946+
/**
947+
* @param arg prefix Prefix is the module prefix, blank means ignore.
948+
*/
949+
def apply[T](prefix: String)(block: => T): T = {
950+
if (prefix != "") {
951+
Builder.pushModulePrefix(prefix)
952+
}
953+
val res = block // execute block
954+
if (prefix != "") {
955+
Builder.popModulePrefix()
956+
}
957+
res
958+
}
959+
}
960+
961+
private case class ModulePrefixAnnotation(target: IsMember, prefix: String) extends SingleTargetAnnotation[IsMember] {
962+
def duplicate(n: IsMember): ModulePrefixAnnotation = this.copy(target = n)
963+
}
964+
965+
private object ModulePrefixAnnotation {
966+
def annotate[T <: HasId](target: T): Unit = {
967+
val prefix = Builder.getModulePrefix
968+
if (prefix != "") {
969+
val annotation: ChiselAnnotation = new ChiselAnnotation {
970+
def toFirrtl: Annotation = ModulePrefixAnnotation(target.toTarget, prefix)
971+
}
972+
chisel3.experimental.annotate(annotation)
973+
}
918974
}
919975
}

core/src/main/scala/chisel3/experimental/hierarchy/InstantiateImpl.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private[chisel3] trait InstantiateImpl {
7373

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

7979
protected def _instanceImpl[K, A <: BaseModule: ru.WeakTypeTag](
@@ -88,9 +88,10 @@ private[chisel3] trait InstantiateImpl {
8888
args: K,
8989
f: K => A
9090
): Definition[A] = {
91+
val modulePrefix = Builder.getModulePrefixList
9192
Builder.contextCache
9293
.getOrElseUpdate(
93-
CacheKey(boxAllData(args), implicitly[ru.WeakTypeTag[A]]), {
94+
CacheKey(boxAllData(args), implicitly[ru.WeakTypeTag[A]], modulePrefix), {
9495
// The definition needs to have no source locator because otherwise it will be unstably
9596
// derived from the first invocation of Instantiate for the particular Module
9697
Definition.do_apply(f(args))(UnlocatableSourceInfo)

core/src/main/scala/chisel3/internal/Builder.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ private[chisel3] trait NamedComponent extends HasId {
461461

462462
// Mutable global state for chisel that can appear outside a Builder context
463463
private[chisel3] class ChiselContext() {
464+
val modulePrefixSeperator: String = "_"
464465
val idGen = new IdGen
465466

466467
// Records the different prefixes which have been scoped at this point in time
@@ -470,6 +471,8 @@ private[chisel3] class ChiselContext() {
470471
// The namespace outside of Builder context is useless, but it ensures that views can still be created
471472
// and the resulting .toTarget is very clearly useless (_$$View$$_...)
472473
val viewNamespace = Namespace.empty
474+
475+
var modulePrefixStack: Prefix = Nil
473476
}
474477

475478
private[chisel3] class DynamicContext(
@@ -1163,6 +1166,37 @@ private[chisel3] object Builder extends LazyLogging {
11631166
)
11641167
}
11651168
}
1169+
1170+
// Puts a module prefix string onto the module prefix stack
1171+
def pushModulePrefix(prefix: String): Unit = {
1172+
val context = chiselContext.get()
1173+
context.modulePrefixStack = prefix :: context.modulePrefixStack
1174+
}
1175+
1176+
// Remove the module prefix on top of the stack
1177+
def popModulePrefix(): List[String] = {
1178+
val context = chiselContext.get()
1179+
val tail = context.modulePrefixStack.tail
1180+
context.modulePrefixStack = tail
1181+
tail
1182+
}
1183+
1184+
// Returns the nested module prefix at this moment
1185+
def getModulePrefix: String = {
1186+
val ctx = chiselContext.get()
1187+
val modulePrefixStack = ctx.modulePrefixStack
1188+
val sep = ctx.modulePrefixSeperator
1189+
if (modulePrefixStack.isEmpty) {
1190+
""
1191+
} else {
1192+
modulePrefixStack.foldLeft("")((a, b) => b + sep + a)
1193+
}
1194+
}
1195+
1196+
def getModulePrefixList: List[String] = {
1197+
chiselContext.get().modulePrefixStack
1198+
}
1199+
11661200
initializeSingletons()
11671201
}
11681202

docs/src/explanations.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ read these documents in the following order:
4141
* [Deep Dive into Legacy Connection Operators](explanations/connection-operators)
4242
* [Properties](explanations/properties)
4343
* [Layers](explanations/layers)
44+
* [Module Prefixing](explanations/moduleprefix)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Module Prefixing
2+
3+
Chisel supports a feature called module prefixing.
4+
Module prefixing allows you to create namespaces in the Verilog output of your design.
5+
They are especially useful for when you want to name a particular subsystem of your design,
6+
and you want to make it easy to identify which subsystem a file belongs to by its name.
7+
8+
We can open a module prefix block using `withModulePrefix`:
9+
10+
```scala mdoc:silent
11+
import chisel3._
12+
13+
class Top extends Module {
14+
withModulePrefix("Foo") {
15+
// ...
16+
}
17+
}
18+
```
19+
20+
All modules defined inside of this block, whether an immediate submodule or a descendent, will be given a prefix `Foo`.
21+
(The prefix is separated by an underscore `_`).
22+
23+
For example, suppose we write the following:
24+
25+
```scala mdoc:silent:reset
26+
import chisel3._
27+
28+
class Top extends Module {
29+
val sub = withModulePrefix("Foo") {
30+
Module(new Sub)
31+
}
32+
}
33+
34+
class Sub extends Module {
35+
// ..
36+
}
37+
```
38+
39+
The result will be a design with two module definitions: `Top` and `Foo_Sub`.
40+
41+
Note that the `val sub =` part must be pulled outside of the `withModulePrefix` block,
42+
or else the module will not be accessible to the rest of the `Top` module.
43+
44+
If a generator is run in multiple prefix blocks, the result is multiple identical copies of the module definition,
45+
each with its own distinct prefix.
46+
For example, consider if we create two instances of `Sub` above like this:
47+
48+
```scala mdoc:silent:reset
49+
import chisel3._
50+
51+
class Top extends Module {
52+
val foo_sub = withModulePrefix("Foo") {
53+
Module(new Sub)
54+
}
55+
56+
val bar_sub = withModulePrefix("Bar") {
57+
Module(new Sub)
58+
}
59+
}
60+
61+
class Sub extends Module {
62+
// ..
63+
}
64+
```
65+
66+
Then, the resulting Verilog will have three module definitions: `Top`, `Foo_Sub`, and `Bar_Sub`.
67+
Both `Foo_Sub` and `Bar_Sub` will be identical to each other.
68+
69+
Module prefixes can also be nested.
70+
71+
```scala mdoc:silent:reset
72+
import chisel3._
73+
74+
class Top extends Module {
75+
val mid = withModulePrefix("Foo") {
76+
Module(new Mid)
77+
}
78+
}
79+
80+
class Mid extends Module {
81+
val sub = withModulePrefix("Bar") {
82+
Module(new Sub)
83+
}
84+
}
85+
86+
class Sub extends Module {
87+
// ..
88+
}
89+
```
90+
91+
This results in three module definitions: `Top`, `Foo_Mid`, and `Foo_Bar_Sub`.
92+
93+
The `withModulePrefix` blocks also work with the `Instantiate` API.
94+
95+
```scala mdoc:silent:reset
96+
import chisel3._
97+
import chisel3.experimental.hierarchy.{instantiable, Instantiate}
98+
99+
@instantiable
100+
class Sub extends Module {
101+
// ...
102+
}
103+
104+
class Top extends Module {
105+
val foo_sub = withModulePrefix("Foo") {
106+
Instantiate(new Sub)
107+
}
108+
109+
val bar_sub = withModulePrefix("Bar") {
110+
Instantiate(new Sub)
111+
}
112+
113+
val noprefix_sub = Instantiate(new Sub)
114+
}
115+
```
116+
117+
In this example, we end up with four modules: `Top`, `Foo_Sub`, `Bar_Sub`, and `Sub`.
118+
119+
When using `Definition` and `Instance`, all `Definition` calls will be affected by `withModulePrefix`.
120+
However, `Instance` will not be effected, since it always creates an instance of the captured definition.
121+
122+
`BlackBox` and `ExtModule` are unaffected by `withModulePrefix`.
123+
If you wish to have one that is sensitive to the module prefix,
124+
you can explicitly name the module like this:
125+
126+
```scala mdoc:silent:reset
127+
import chisel3._
128+
import chisel3.experimental.hierarchy.{instantiable, Instantiate}
129+
import chisel3.experimental.ExtModule
130+
131+
class Sub extends ExtModule {
132+
override def desiredName = modulePrefix + "Sub"
133+
}
134+
```

src/main/scala/chisel3/util/SRAM.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ object SRAM {
639639
descriptionInstance.hierarchyIn := Property(Path(mem))
640640
description := descriptionInstance.getPropertyReference
641641
}
642+
ModulePrefixAnnotation.annotate(mem)
642643
_out
643644
}
644645

0 commit comments

Comments
 (0)