Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix - module name collision for injecting aspect (bp #1635) #1639

Merged
merged 2 commits into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/src/main/scala/chisel3/Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ package experimental {

/** Legalized name of this module. */
final lazy val name = try {
Builder.globalNamespace.name(desiredName)
// If this is a module aspect, it should share the same name as the original module
// Thus, the desired name should be returned without uniquification
if(this.isInstanceOf[ModuleAspect]) desiredName else Builder.globalNamespace.name(desiredName)
} catch {
case e: NullPointerException => throwException(
s"Error: desiredName of ${this.getClass.getName} is null. Did you evaluate 'name' before all values needed by desiredName were available?", e)
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,11 @@ private[chisel3] object Builder {


def build[T <: RawModule](f: => T): (Circuit, T) = {
dynamicContextVar.withValue(Some(new DynamicContext())) {
build(f, new DynamicContext())
}

private [chisel3] def build[T <: RawModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = {
dynamicContextVar.withValue(Some(dynamicContext)) {
checkScalaVersion()
errors.info("Elaborating design...")
val mod = f
Expand Down
37 changes: 31 additions & 6 deletions src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

package chisel3.aop.injecting

import chisel3.{Module, ModuleAspect, experimental, withClockAndReset, RawModule, MultiIOModule}
import chisel3.{Module, ModuleAspect, MultiIOModule, RawModule, experimental, withClockAndReset}
import chisel3.aop._
import chisel3.internal.Builder
import chisel3.internal.{Builder, DynamicContext}
import chisel3.internal.firrtl.DefModule
import chisel3.stage.DesignAnnotation
import firrtl.annotations.ModuleTarget
Expand Down Expand Up @@ -42,17 +42,42 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
injection: M => Unit
) extends Aspect[T] {
final def toAnnotation(top: T): AnnotationSeq = {
toAnnotation(selectRoots(top), top.name)
val moduleNames = Select.collectDeep(top) { case i => i.name }.toSeq
toAnnotation(selectRoots(top), top.name, moduleNames)
}

final def toAnnotation(modules: Iterable[M], circuit: String): AnnotationSeq = {

/** Returns annotations which contain all injection logic
*
* @param modules The modules to inject into
* @param circuit Top level circuit
* @return
*/
@deprecated("Use other toAnnotation method to pass in names of all existing modules", "3.4.1")
final def toAnnotation(modules: Iterable[M], circuit: String): AnnotationSeq = toAnnotation(modules, circuit, Nil)

/** Returns annotations which contain all injection logic
*
* @param modules The modules to inject into
* @param circuit Top level circuit
* @param moduleNames The names of all existing modules in the original circuit, to avoid name collisions
* @return
*/
final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = {
val dynamicContext = new DynamicContext()
// Add existing module names into the namespace. If injection logic instantiates new modules
// which would share the same name, they will get uniquified accordingly
moduleNames.foreach { n =>
dynamicContext.globalNamespace.name(n)
}
RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module =>
val (chiselIR, _) = Builder.build(Module(new ModuleAspect(module) {
module match {
case x: MultiIOModule => withClockAndReset(x.clock, x.reset) { injection(module) }
case x: RawModule => injection(module)
}
}))
}), dynamicContext)

val comps = chiselIR.components.map {
case x: DefModule if x.name == module.name => x.copy(id = module)
case other => other
Expand All @@ -65,7 +90,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
case m: firrtl.ir.Module if m.name == module.name =>
stmts += m.body
Nil
case other =>
case other: firrtl.ir.Module =>
Seq(other)
}

Expand Down
74 changes: 48 additions & 26 deletions src/test/scala/chiselTests/aop/InjectionSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,46 @@
package chiselTests.aop

import chisel3.testers.{BasicTester, TesterDriver}
import chiselTests.ChiselFlatSpec
import chiselTests.{ChiselFlatSpec, Utils}
import chisel3._
import chisel3.aop.injecting.InjectingAspect
import logger.{LogLevel, LogLevelAnnotation}

class SubmoduleManipulationTester extends BasicTester {
val moduleSubmoduleA = Module(new SubmoduleA)
}
object InjectionHierarchy {

class SubmoduleA extends Module {
val io = IO(new Bundle {
val out = Output(Bool())
})
io.out := false.B
}
class SubmoduleManipulationTester extends BasicTester {
val moduleSubmoduleA = Module(new SubmoduleA)
}

class SubmoduleB extends Module {
val io = IO(new Bundle {
val in = Input(Bool())
})
}
class SubmoduleA extends Module {
val io = IO(new Bundle {
val out = Output(Bool())
})
io.out := false.B
}

class AspectTester(results: Seq[Int]) extends BasicTester {
val values = VecInit(results.map(_.U))
val counter = RegInit(0.U(results.length.W))
counter := counter + 1.U
when(counter >= values.length.U) {
stop()
}.otherwise {
when(reset.asBool() === false.B) {
printf("values(%d) = %d\n", counter, values(counter))
assert(counter === values(counter))
class SubmoduleB extends Module {
val io = IO(new Bundle {
val in = Input(Bool())
})
}

class AspectTester(results: Seq[Int]) extends BasicTester {
val values = VecInit(results.map(_.U))
val counter = RegInit(0.U(results.length.W))
counter := counter + 1.U
when(counter >= values.length.U) {
stop()
}.otherwise {
when(reset.asBool() === false.B) {
assert(counter === values(counter))
}
}
}
}

class InjectionSpec extends ChiselFlatSpec {
class InjectionSpec extends ChiselFlatSpec with Utils {
import InjectionHierarchy._
val correctValueAspect = InjectingAspect(
{dut: AspectTester => Seq(dut)},
{dut: AspectTester =>
Expand Down Expand Up @@ -67,6 +71,16 @@ class InjectionSpec extends ChiselFlatSpec {
}
)

val duplicateSubmoduleAspect = InjectingAspect(
{dut: SubmoduleManipulationTester => Seq(dut)},
{_: SubmoduleManipulationTester =>
// By creating a second SubmoduleA, the module names would conflict unless they were uniquified
val moduleSubmoduleA2 = Module(new SubmoduleA)
//if we're here then we've elaborated correctly
stop()
}
)

"Test" should "pass if inserted the correct values" in {
assertTesterPasses{ new AspectTester(Seq(0, 1, 2)) }
}
Expand All @@ -88,4 +102,12 @@ class InjectionSpec extends ChiselFlatSpec {
"Test" should "pass if the submodules in SubmoduleManipulationTester can be manipulated by manipulateSubmoduleAspect" in {
assertTesterPasses({ new SubmoduleManipulationTester} , Nil, Seq(manipulateSubmoduleAspect) ++ TesterDriver.verilatorOnly)
}

"Module name collisions when adding a new module" should "be resolved" in {
assertTesterPasses(
{ new SubmoduleManipulationTester},
Nil,
Seq(duplicateSubmoduleAspect) ++ TesterDriver.verilatorOnly
)
}
}