Skip to content

Commit

Permalink
Merge branch 'main' into fix-mill-sources
Browse files Browse the repository at this point in the history
  • Loading branch information
unlsycn authored Nov 16, 2024
2 parents f1b7380 + 9c2498a commit 99a6144
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 14 deletions.
2 changes: 1 addition & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ trait Macros extends CrossSbtModule with HasScala2MacroAnno with ScalafmtModule
}
}

override def ivyDeps = super.ivyDeps() ++ Seq(ivy"org.scala-lang:scala-reflect:$scalaVersion")
override def ivyDeps = super.ivyDeps() ++ Seq(v.scalaReflect(crossScalaVersion))
}

object core extends Cross[Core](v.scalaCrossVersions)
Expand Down
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
11 changes: 11 additions & 0 deletions src/main/scala/chisel3/util/experimental/Inline.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ trait InlineInstance { self: BaseModule =>
.map(chisel3.experimental.annotate(_))
}

/** Inlines all instances of a module. If this module dedups with any other
* module, instances of that other module will also be inlined.
*/
trait InlineInstanceAllowDedup { self: BaseModule =>
chisel3.experimental.annotate(
new ChiselAnnotation {
def toFirrtl: Annotation = InlineAnnotation(self.toNamed)
}
)
}

/** Flattens an instance of a module
*
* @example {{{
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")
}
}
59 changes: 59 additions & 0 deletions src/test/scala/chiselTests/InlineSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests

import chisel3._
import circt.stage.ChiselStage
import org.scalatest.matchers.should.Matchers
import chiselTests.{ChiselFlatSpec, MatchesAndOmits}
import chisel3.util.experimental.{InlineInstance, InlineInstanceAllowDedup}

class InlineInstanceSpec extends ChiselFlatSpec with MatchesAndOmits {
class ModuleA extends RawModule {
val w = dontTouch(WireInit(false.B))
}

class ModuleB extends RawModule with InlineInstance {
val w = dontTouch(WireInit(false.B))
}

class TopModule extends RawModule {
val a = Module(new ModuleA)
val b = Module(new ModuleB)
}

"InlineInstanceAllowDedup" should "Inline any module that dedups with a module marked inline" in {
val verilog = ChiselStage.emitSystemVerilog(new TopModule)
matchesAndOmits(verilog)(
"module TopModule()",
"module ModuleA();"
)(
"module ModuleB()"
)
}
}

class InlineInstanceAllowDedupSpec extends ChiselFlatSpec with MatchesAndOmits {
class ModuleA extends RawModule {
val w = dontTouch(WireInit(false.B))
}

class ModuleB extends RawModule with InlineInstanceAllowDedup {
val w = dontTouch(WireInit(false.B))
}

class TopModule extends RawModule {
val a = Module(new ModuleA)
val b = Module(new ModuleB)
}

"InlineInstanceAllowDedup" should "Inline any module that dedups with a module marked inline" in {
val verilog = ChiselStage.emitSystemVerilog(new TopModule)
matchesAndOmits(verilog)(
"module TopModule()"
)(
"module ModuleA()",
"module ModuleB()"
)
}
}
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 99a6144

Please sign in to comment.