From 59a99044a3d29bd2d74c7c30547a12123902aa65 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 14 Nov 2024 09:41:37 -0800 Subject: [PATCH 1/3] Fix scala-reflect dependency version --- build.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sc b/build.sc index 3a6f2118ea..e193afc676 100644 --- a/build.sc +++ b/build.sc @@ -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) From 4fa08f8fa0a4125193189337ad9e56fd4645f6fa Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 14 Nov 2024 15:25:42 -0500 Subject: [PATCH 2/3] Add new InlineAllInstances trait (#4508) The InlineInstance trait can be attached to a module, to cause firtool to inline instance of that module. The inline annotation is tricky to wield because, if an inline module dedups with another module, we will inline all instances of both modules. To make InlineInstance easier to use, whenever we mark a module as inline, we also mark it as "no dedup". This of course does not work well when we have a module which we want to inline AND dedup. So, this PR adds a new InlineAllInstances trait which allows a module to be marked inline without blocking dedup. --- .../chisel3/util/experimental/Inline.scala | 11 ++++ src/test/scala/chiselTests/InlineSpec.scala | 59 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/test/scala/chiselTests/InlineSpec.scala diff --git a/src/main/scala/chisel3/util/experimental/Inline.scala b/src/main/scala/chisel3/util/experimental/Inline.scala index f24278dc96..2a998368b9 100644 --- a/src/main/scala/chisel3/util/experimental/Inline.scala +++ b/src/main/scala/chisel3/util/experimental/Inline.scala @@ -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 {{{ diff --git a/src/test/scala/chiselTests/InlineSpec.scala b/src/test/scala/chiselTests/InlineSpec.scala new file mode 100644 index 0000000000..48ea394b49 --- /dev/null +++ b/src/test/scala/chiselTests/InlineSpec.scala @@ -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()" + ) + } +} From 9c2498adb6c83da3887ad6688595af8c5579e638 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 14 Nov 2024 14:52:31 -0800 Subject: [PATCH 3/3] Add module prefixing to BaseModule definitions (#4509) * 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. --- core/src/main/scala/chisel3/ModuleImpl.scala | 30 ++++++- docs/src/explanations/moduleprefix.md | 55 +++++++++++- src/test/scala/chiselTests/ChiselSpec.scala | 8 +- ...refixSpec.scala => ModulePrefixSpec.scala} | 88 ++++++++++++++++++- 4 files changed, 168 insertions(+), 13 deletions(-) rename src/test/scala/chiselTests/{PrefixSpec.scala => ModulePrefixSpec.scala} (74%) diff --git a/core/src/main/scala/chisel3/ModuleImpl.scala b/core/src/main/scala/chisel3/ModuleImpl.scala index 2eb81f5817..cb64f15255 100644 --- a/core/src/main/scala/chisel3/ModuleImpl.scala +++ b/core/src/main/scala/chisel3/ModuleImpl.scala @@ -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 @@ -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 => @@ -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) + } } } diff --git a/docs/src/explanations/moduleprefix.md b/docs/src/explanations/moduleprefix.md index f2634c028e..ec61b8c15b 100644 --- a/docs/src/explanations/moduleprefix.md +++ b/docs/src/explanations/moduleprefix.md @@ -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 @@ -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: @@ -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 @@ -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 { @@ -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 @@ -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: diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 6674bace02..9b3087659b 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -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 @@ -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") } } diff --git a/src/test/scala/chiselTests/PrefixSpec.scala b/src/test/scala/chiselTests/ModulePrefixSpec.scala similarity index 74% rename from src/test/scala/chiselTests/PrefixSpec.scala rename to src/test/scala/chiselTests/ModulePrefixSpec.scala index 79091c9c3f..d24b407be7 100644 --- a/src/test/scala/chiselTests/PrefixSpec.scala +++ b/src/test/scala/chiselTests/ModulePrefixSpec.scala @@ -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 { @@ -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 { @@ -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: _*)() + } }