diff options
| -rw-r--r-- | src/main/scala/chisel3/util/experimental/group.scala | 67 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/experimental/GroupSpec.scala | 118 |
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) + } +} |
