diff options
6 files changed, 208 insertions, 18 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 5e410ebd..5d294ea9 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -395,6 +395,6 @@ class Bundle extends Aggregate { } private[core] object Bundle { - val keywords = List("flip", "asInput", "asOutput", "cloneType", "toBits", - "widthOption", "chiselCloneType") + val keywords = List("flip", "asInput", "asOutput", "cloneType", "chiselCloneType", "toBits", + "widthOption", "signalName", "signalPathName", "signalParent", "signalComponent") } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala index 2cfc6c6a..9f7048cd 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala @@ -31,13 +31,14 @@ object Module { m._commands.prepend(DefInvalid(childSourceInfo, m.io.ref)) // init module outputs Builder.currentModule = parent // Back to parent! val ports = m.computePorts - Builder.components += Component(m, m.name, ports, m._commands) + val component = Component(m, m.name, ports, m._commands) + m._component = Some(component) + Builder.components += component // Avoid referencing 'parent' in top module if(!Builder.currentModule.isEmpty) { pushCommand(DefInstance(sourceInfo, m, ports)) m.setupInParent(childSourceInfo) } - m } } @@ -100,6 +101,25 @@ extends HasId { /** Legalized name of this module. */ final val name = Builder.globalNamespace.name(desiredName) + /** FIRRTL Module name */ + private var _modName: Option[String] = None + private[chisel3] def setModName(name: String) = _modName = Some(name) + def modName = _modName match { + case Some(name) => name + case None => throwException("modName should be called after circuit elaboration") + } + + /** Keep component for signal names */ + private[chisel3] var _component: Option[Component] = None + + + /** Signal name (for simulation). */ + override def instanceName = + if (_parent == None) name else _component match { + case None => getRef.name + case Some(c) => getRef fullName c + } + /** IO for this Module. At the Scala level (pre-FIRRTL transformations), * connections in and out of a Module may only go through `io` elements. */ diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 3191e384..0376e067 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -55,7 +55,18 @@ private[chisel3] class IdGen { } } -private[chisel3] trait HasId { +/** Public API to access Node/Signal names. + * currently, the node's name, the full path name, and references to its parent Module and component. + * These are only valid once the design has been elaborated, and should not be used during its construction. + */ +trait InstanceId { + def instanceName: String + def pathName: String + def parentPathName: String + def parentModName: String +} + +private[chisel3] trait HasId extends InstanceId { private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name private[chisel3] val _parent: Option[Module] = Builder.currentModule _parent.foreach(_.addId(this)) @@ -95,6 +106,27 @@ private[chisel3] trait HasId { private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref)) private[chisel3] def getRef: Arg = _ref.get + + // Implementation of public methods. + def instanceName = _parent match { + case Some(p) => p._component match { + case Some(c) => getRef fullName c + case None => throwException("signalName/pathName should be called after circuit elaboration") + } + case None => throwException("this cannot happen") + } + def pathName = _parent match { + case None => instanceName + case Some(p) => s"${p.pathName}.$instanceName" + } + def parentPathName = _parent match { + case Some(p) => p.pathName + case None => throwException(s"$instanceName doesn't have a parent") + } + def parentModName = _parent match { + case Some(p) => p.modName + case None => throwException(s"$instanceName doesn't have a parent") + } } private[chisel3] class DynamicContext(moduleCompileOptions: Option[ExplicitCompileOptions] = None) { diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala index 8efb529d..f9f79f35 100644 --- a/src/main/scala/chisel3/Driver.scala +++ b/src/main/scala/chisel3/Driver.scala @@ -112,6 +112,8 @@ object Driver extends BackendCompilationUtilities { def emit[T <: Module](gen: () => T, moduleCompileOptions: Option[ExplicitCompileOptions] = None): String = Emitter.emit(elaborate(gen, moduleCompileOptions)) + def emit[T <: Module](ir: Circuit): String = Emitter.emit(ir) + def dumpFirrtl(ir: Circuit, optName: Option[File]): File = { val f = optName.getOrElse(new File(ir.name + ".fir")) val w = new FileWriter(f) diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 31856541..8b94c68f 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -27,11 +27,7 @@ private class Emitter(circuit: Circuit) { case e: Stop => s"stop(${e.clk.fullName(ctx)}, UInt<1>(1), ${e.ret})" case e: Printf => s"""printf(${e.clk.fullName(ctx)}, UInt<1>(1), "${e.format}"${e.ids.map(_.fullName(ctx)).fold(""){_ + ", " + _}})""" case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid" - case e: DefInstance => { - val modName = moduleMap.get(e.id.name).get - s"inst ${e.name} of $modName" - } - + case e: DefInstance => s"inst ${e.name} of ${e.id.modName}" case w: WhenBegin => indent() s"when ${w.pred.fullName(ctx)} :" @@ -47,8 +43,6 @@ private class Emitter(circuit: Circuit) { // Map of Module FIRRTL definition to FIRRTL name, if it has been emitted already. private val defnMap = collection.mutable.HashMap[(String, String), Component]() - // Map of Component name to FIRRTL id. - private val moduleMap = collection.mutable.HashMap[String, String]() /** Generates the FIRRTL module declaration. */ @@ -89,15 +83,11 @@ private class Emitter(circuit: Circuit) { defnMap get (m.id.desiredName, defn) match { case Some(duplicate) => - moduleMap(m.name) = duplicate.name + m.id setModName duplicate.name "" case None => - require(!(moduleMap contains m.name), - "emitting module with same name but different contents") - - moduleMap(m.name) = m.name defnMap((m.id.desiredName, defn)) = m - + m.id setModName m.name moduleDecl(m) + defn } } diff --git a/src/test/scala/chiselTests/AnnotatingExample.scala b/src/test/scala/chiselTests/AnnotatingExample.scala new file mode 100644 index 00000000..85b4b039 --- /dev/null +++ b/src/test/scala/chiselTests/AnnotatingExample.scala @@ -0,0 +1,146 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.core.Module +import chisel3.internal.InstanceId +import chisel3.testers.BasicTester +import chisel3.NotStrict.CompileOptions +import org.scalatest._ + +import scala.util.DynamicVariable + +//scalastyle:off magic.number + +/** + * This Spec file illustrates use of Donggyu's component name API, it currently only + * uses three methods .signalName, .parentModName and .pathName + * + * This is also an illustration of how to implement an annotation system in chisel3 + * A local (my) Driver and Builder are created to provide thread-local access to + * an annotation map, and then a post elaboration annotation processor can resolve + * the keys and could serialize the annotations to a file for use by firrtl passes + */ + +class SomeSubMod(param1: Int, param2: Int) extends Module { + val io = new Bundle { + val in = UInt(INPUT, 16) + val out = SInt(OUTPUT, 32) + } + val annotate = MyBuilder.myDynamicContext.annotationMap + + annotate(AnnotationKey(this, JustThisRef)) = s"SomeSubMod($param1, $param2)" + annotate(AnnotationKey(io.in, AllRefs)) = "sub mod io.in" + annotate(AnnotationKey(io.out, JustThisRef)) = "sub mod io.out" +} + +class AnnotatingExample extends Module { + val io = new Bundle { + val a = UInt(INPUT, 32) + val b = UInt(INPUT, 32) + val e = Bool(INPUT) + val z = UInt(OUTPUT, 32) + val v = Bool(OUTPUT) + val bun = new Bundle { + val nested_1 = UInt(INPUT, 12) + val nested_2 = Bool(OUTPUT) + } + } + val x = Reg(UInt(width = 32)) + val y = Reg(UInt(width = 32)) + + val subModule1 = Module(new SomeSubMod(1, 2)) + val subModule2 = Module(new SomeSubMod(3, 4)) + + + val annotate = MyBuilder.myDynamicContext.annotationMap + + annotate(AnnotationKey(subModule2, AllRefs)) = s"SomeSubMod was used" + + annotate(AnnotationKey(x, JustThisRef)) = "I am register X" + annotate(AnnotationKey(y, AllRefs)) = "I am register Y" + annotate(AnnotationKey(io.a, JustThisRef)) = "I am io.a" + annotate(AnnotationKey(io.bun.nested_1, AllRefs)) = "I am io.bun.nested_1" + annotate(AnnotationKey(io.bun.nested_2, JustThisRef)) = "I am io.bun.nested_2" +} + +class AnnotatingExampleTester extends BasicTester { + val dut = Module(new AnnotatingExample) + + stop() +} + +class AnnotatingExampleSpec extends FlatSpec with Matchers { + behavior of "Annotating components of a circuit" + + it should "contain the following relative keys" in { + val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester } + + annotationMap.contains("SomeSubMod.io.in") should be(true) + annotationMap.contains("AnnotatingExample.y") should be(true) + + annotationMap("SomeSubMod.io.in") should be("sub mod io.in") + } + it should "contain the following absolute keys" in { + val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester } + + annotationMap.contains("AnnotatingExampleTester.dut.subModule2.io.out") should be (true) + annotationMap.contains("AnnotatingExampleTester.dut.x") should be (true) + + annotationMap("AnnotatingExampleTester.dut.subModule2.io.out") should be ("sub mod io.out") + } +} + +trait AnnotationScope +case object AllRefs extends AnnotationScope +case object JustThisRef extends AnnotationScope + +object AnnotationKey { + def apply(component: InstanceId): AnnotationKey = { + AnnotationKey(component, AllRefs) + } +} +case class AnnotationKey(val component: InstanceId, scope: AnnotationScope) { + override def toString: String = { + scope match { + case JustThisRef => + s"${component.pathName}" + case AllRefs => + s"${component.parentModName}.${component.instanceName}" + case _ => + s"${component.toString}_unknown_scope" + } + } +} + +class AnnotationMap extends scala.collection.mutable.HashMap[AnnotationKey, String] + +class MyDynamicContext { + val annotationMap = new AnnotationMap +} + +object MyBuilder { + private val myDynamicContextVar = new DynamicVariable[Option[MyDynamicContext]](None) + + def myDynamicContext: MyDynamicContext = + myDynamicContextVar.value getOrElse new MyDynamicContext + + def processAnnotations(annotationMap: AnnotationMap): Map[String, String] = { + annotationMap.map { case (k,v) => k.toString -> v}.toMap + } + + def build[T <: Module](f: => T): Map[String, String] = { + myDynamicContextVar.withValue(Some(new MyDynamicContext)) { + Driver.emit(() => f) + processAnnotations(myDynamicContextVar.value.get.annotationMap) + } + } +} + +object MyDriver extends BackendCompilationUtilities { + /** + * illustrates a chisel3 style driver that, annotations can only processed within this structure + */ + def buildAnnotatedCircuit[T <: Module](gen: () => T): Map[String, String] = MyBuilder.build(Module(gen())) +}
\ No newline at end of file |
