summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/chisel3/util/experimental/group.scala67
-rw-r--r--src/test/scala/chiselTests/experimental/GroupSpec.scala118
2 files changed, 185 insertions, 0 deletions
diff --git a/src/main/scala/chisel3/util/experimental/group.scala b/src/main/scala/chisel3/util/experimental/group.scala
new file mode 100644
index 00000000..87214ca9
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/group.scala
@@ -0,0 +1,67 @@
+// See LICENSE for license details.
+
+package chisel3.util.experimental
+
+import chisel3._
+import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform, annotate}
+import chisel3.internal.requireIsHardware
+import firrtl.Transform
+import firrtl.transforms.{GroupAnnotation, GroupComponents}
+
+/** Marks that a module to be ignored in Dedup Transform in Firrtl pass
+ *
+ * @example {{{
+ * class MyModule extends Module {
+ * val io = IO(new Bundle{
+ * val a = Input(Bool())
+ * val b = Output(Bool())
+ * })
+ * val reg1 = RegInit(0.U)
+ * reg1 := io.a
+ * val reg2 = RegNext(reg1)
+ * io.b := reg2
+ * group(Seq(reg1, reg2), "DosRegisters", "doubleReg")
+ * }
+ * }}}
+ *
+ * @note Intermediate wires will get pulled into the new instance, but intermediate registers will not
+ * because they are also connected to their module's clock port. This means that if you want
+ * a register to be included in a group, it must be explicitly referred to in the input list.
+ */
+object group {
+
+ /** Marks a set of components (and their interconnected components) to be included in a new
+ * instance hierarchy.
+ *
+ * @note Intermediate wires will get pulled into the new instance, but intermediate registers will not
+ * because they are also connected to their module's clock port. This means that if you want
+ * a register to be included in a group, it must be explicitly referred to in the input list.
+ *
+ * @param components components in this group
+ * @param newModule suggested name of the new module
+ * @param newInstance suggested name of the instance of the new module
+ * @param outputSuffix suggested suffix of any output ports of the new module
+ * @param inputSuffix suggested suffix of any input ports of the new module
+ * @param compileOptions necessary for backwards compatibility
+ * @tparam T Parent type of input components
+ */
+ def apply[T <: Data](
+ components: Seq[T],
+ newModule: String,
+ newInstance: String,
+ outputSuffix: Option[String] = None,
+ inputSuffix: Option[String] = None
+ )(implicit compileOptions: CompileOptions): Unit = {
+ if (compileOptions.checkSynthesizable) {
+ components.foreach { data =>
+ requireIsHardware(data, s"Component ${data.toString} is marked to group, but is not bound.")
+ }
+ }
+ annotate(new ChiselAnnotation with RunFirrtlTransform {
+ def toFirrtl = GroupAnnotation(components.map(_.toNamed), newModule, newInstance, outputSuffix, inputSuffix)
+
+ override def transformClass: Class[_ <: Transform] = classOf[GroupComponents]
+ })
+ }
+}
+
diff --git a/src/test/scala/chiselTests/experimental/GroupSpec.scala b/src/test/scala/chiselTests/experimental/GroupSpec.scala
new file mode 100644
index 00000000..593179f4
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/GroupSpec.scala
@@ -0,0 +1,118 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chiselTests.ChiselFlatSpec
+import chisel3._
+import chisel3.RawModule
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselMain}
+import chisel3.util.experimental.group
+import firrtl.analyses.InstanceGraph
+import firrtl.options.TargetDirAnnotation
+import firrtl.stage.CompilerAnnotation
+import firrtl.{LowFirrtlCompiler, ir => fir}
+
+import scala.collection.mutable
+
+class GroupSpec extends ChiselFlatSpec {
+
+ def collectInstances(c: fir.Circuit, top: Option[String] = None): Seq[String] = new InstanceGraph(c)
+ .fullHierarchy.values.flatten.toSeq
+ .map( v => (top.getOrElse(v.head.name) +: v.tail.map(_.name)).mkString(".") )
+
+ def collectDeclarations(m: fir.DefModule): Set[String] = {
+ val decs = mutable.HashSet[String]()
+ def onStmt(s: fir.Statement): fir.Statement = s.mapStmt(onStmt) match {
+ case d: fir.IsDeclaration => decs += d.name; d
+ case other => other
+ }
+ m.mapStmt(onStmt)
+ decs.toSet
+ }
+
+ def lower[T <: RawModule](gen: () => T): fir.Circuit = {
+ (ChiselMain.stage.run(
+ Seq(
+ CompilerAnnotation(new LowFirrtlCompiler()),
+ TargetDirAnnotation("test_run_dir"),
+ ChiselGeneratorAnnotation(gen)
+ )
+ ) collectFirst {
+ case firrtl.stage.FirrtlCircuitAnnotation(circuit) => circuit
+ }).get
+ }
+
+ "Module Grouping" should "compile to low FIRRTL" in {
+ class MyModule extends Module {
+ val io = IO(new Bundle{
+ val a = Input(Bool())
+ val b = Output(Bool())
+ })
+ val reg1 = RegInit(0.U)
+ reg1 := io.a
+ val reg2 = RegNext(reg1)
+ io.b := reg2
+ group(Seq(reg1, reg2), "DosRegisters", "doubleReg")
+ }
+
+ val firrtlCircuit = lower(() => new MyModule)
+ firrtlCircuit.modules.collect {
+ case m: fir.Module if m.name == "MyModule" =>
+ Set("doubleReg") should be (collectDeclarations(m))
+ case m: fir.Module if m.name == "DosRegisters" =>
+ Set("reg1", "reg2") should be (collectDeclarations(m))
+ }
+ val instances = collectInstances(firrtlCircuit, Some("MyModule")).toSet
+ Set("MyModule", "MyModule.doubleReg") should be (instances)
+ }
+
+ "Module Grouping" should "not include intermediate registers" in {
+ class MyModule extends Module {
+ val io = IO(new Bundle{
+ val a = Input(Bool())
+ val b = Output(Bool())
+ })
+ val reg1 = RegInit(0.U)
+ reg1 := io.a
+ val reg2 = RegNext(reg1)
+ val reg3 = RegNext(reg2)
+ io.b := reg3
+ group(Seq(reg1, reg3), "DosRegisters", "doubleReg")
+ }
+
+ val firrtlCircuit = lower(() => new MyModule)
+ firrtlCircuit.modules.collect {
+ case m: fir.Module if m.name == "MyModule" =>
+ Set("reg2", "doubleReg") should be (collectDeclarations(m))
+ case m: fir.Module if m.name == "DosRegisters" =>
+ Set("reg1", "reg3") should be (collectDeclarations(m))
+ }
+ val instances = collectInstances(firrtlCircuit, Some("MyModule")).toSet
+ Set("MyModule", "MyModule.doubleReg") should be (instances)
+ }
+
+ "Module Grouping" should "include intermediate wires" in {
+ class MyModule extends Module {
+ val io = IO(new Bundle{
+ val a = Input(Bool())
+ val b = Output(Bool())
+ })
+ val reg1 = RegInit(0.U)
+ reg1 := io.a
+ val wire = WireInit(reg1)
+ val reg3 = RegNext(wire)
+ io.b := reg3
+ group(Seq(reg1, reg3), "DosRegisters", "doubleReg")
+ }
+
+ val firrtlCircuit = lower(() => new MyModule)
+ firrtlCircuit.modules.collect {
+ case m: fir.Module if m.name == "MyModule" =>
+ Set("doubleReg") should be (collectDeclarations(m))
+ case m: fir.Module if m.name == "DosRegisters" =>
+ Set("reg1", "reg3", "wire") should be (collectDeclarations(m))
+ }
+ val instances = collectInstances(firrtlCircuit, Some("MyModule")).toSet
+ Set("MyModule", "MyModule.doubleReg") should be (instances)
+ }
+}