diff options
| author | Jack Koenig | 2020-03-25 19:51:46 -0700 |
|---|---|---|
| committer | GitHub | 2020-03-25 19:51:46 -0700 |
| commit | dbb024a9adee6d82f37e357cf8b55456674ff65c (patch) | |
| tree | 578858ab6d219ca6daf44cf87b73f75054989097 /chiselFrontend/src/main/scala/chisel3/internal/Builder.scala | |
| parent | 6263fcc56b630b7181eb30680cadcdbb2bdf91dc (diff) | |
| parent | fbf5e6f1a0e8bf535d465b748ad554575fe62156 (diff) | |
Merge pull request #1384 from freechipsproject/no-more-compile-internal
No more compile internal
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/internal/Builder.scala')
| -rw-r--r-- | chiselFrontend/src/main/scala/chisel3/internal/Builder.scala | 452 |
1 files changed, 0 insertions, 452 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala deleted file mode 100644 index 773a9ad1..00000000 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ /dev/null @@ -1,452 +0,0 @@ -// See LICENSE for license details. - -package chisel3.internal - -import scala.util.DynamicVariable -import scala.collection.mutable.ArrayBuffer -import chisel3._ -import chisel3.experimental._ -import chisel3.internal.firrtl._ -import chisel3.internal.naming._ -import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget} - -import scala.collection.mutable - -private[chisel3] class Namespace(keywords: Set[String]) { - private val names = collection.mutable.HashMap[String, Long]() - for (keyword <- keywords) - names(keyword) = 1 - - private def rename(n: String): String = { - val index = names(n) - val tryName = s"${n}_${index}" - names(n) = index + 1 - if (this contains tryName) rename(n) else tryName - } - - private def sanitize(s: String, leadingDigitOk: Boolean = false): String = { - // TODO what character set does FIRRTL truly support? using ANSI C for now - def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' - def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9') - val res = s filter legal - val headOk = (!res.isEmpty) && (leadingDigitOk || legalStart(res.head)) - if (headOk) res else s"_$res" - } - - def contains(elem: String): Boolean = names.contains(elem) - - // leadingDigitOk is for use in fields of Records - def name(elem: String, leadingDigitOk: Boolean = false): String = { - val sanitized = sanitize(elem, leadingDigitOk) - if (this contains sanitized) { - name(rename(sanitized)) - } else { - names(sanitized) = 1 - sanitized - } - } -} - -private[chisel3] object Namespace { - /** Constructs an empty Namespace */ - def empty: Namespace = new Namespace(Set.empty[String]) -} - -private[chisel3] class IdGen { - private var counter = -1L - def next: Long = { - counter += 1 - counter - } -} - -/** 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 - /** Returns a FIRRTL Named that refers to this object in the elaborated hardware graph */ - def toNamed: Named - /** Returns a FIRRTL IsMember that refers to this object in the elaborated hardware graph */ - def toTarget: IsMember - /** Returns a FIRRTL IsMember that refers to the absolute path to this object in the elaborated hardware graph */ - def toAbsoluteTarget: IsMember -} - -private[chisel3] trait HasId extends InstanceId { - private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name - private[chisel3] val _parent: Option[BaseModule] = Builder.currentModule - _parent.foreach(_.addId(this)) - - private[chisel3] val _id: Long = Builder.idGen.next - - // TODO: remove this, but its removal seems to cause a nasty Scala compiler crash. - override def hashCode: Int = super.hashCode() - override def equals(that: Any): Boolean = super.equals(that) - - // Facilities for 'suggesting' a name to this. - // Post-name hooks called to carry the suggestion to other candidates as needed - private var suggested_name: Option[String] = None - private val postname_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit] - // Only takes the first suggestion! - def suggestName(name: =>String): this.type = { - if(suggested_name.isEmpty) suggested_name = Some(name) - for(hook <- postname_hooks) { hook(name) } - this - } - private[chisel3] def suggestedName: Option[String] = suggested_name - private[chisel3] def addPostnameHook(hook: String=>Unit): Unit = postname_hooks += hook - - // Uses a namespace to convert suggestion into a true name - // Will not do any naming if the reference already assigned. - // (e.g. tried to suggest a name to part of a Record) - private[chisel3] def forceName(default: =>String, namespace: Namespace): Unit = - if(_ref.isEmpty) { - val candidate_name = suggested_name.getOrElse(default) - val available_name = namespace.name(candidate_name) - setRef(Ref(available_name)) - } - - private var _ref: Option[Arg] = None - private[chisel3] def setRef(imm: Arg): Unit = _ref = Some(imm) - private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name)) - 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 - private[chisel3] def getOptionRef: Option[Arg] = _ref - - // Implementation of public methods. - def instanceName: String = _parent match { - case Some(p) => p._component match { - case Some(c) => _ref match { - case Some(arg) => arg fullName c - case None => suggested_name.getOrElse("??") - } - case None => throwException("signalName/pathName should be called after circuit elaboration") - } - case None => throwException("this cannot happen") - } - def pathName: String = _parent match { - case None => instanceName - case Some(p) => s"${p.pathName}.$instanceName" - } - def parentPathName: String = _parent match { - case Some(p) => p.pathName - case None => throwException(s"$instanceName doesn't have a parent") - } - def parentModName: String = _parent match { - case Some(p) => p.name - case None => throwException(s"$instanceName doesn't have a parent") - } - // TODO Should this be public? - protected def circuitName: String = _parent match { - case None => instanceName - case Some(p) => p.circuitName - } - - private[chisel3] def getPublicFields(rootClass: Class[_]): Seq[java.lang.reflect.Method] = { - // Suggest names to nodes using runtime reflection - def getValNames(c: Class[_]): Set[String] = { - if (c == rootClass) { - Set() - } else { - getValNames(c.getSuperclass) ++ c.getDeclaredFields.map(_.getName) - } - } - val valNames = getValNames(this.getClass) - def isPublicVal(m: java.lang.reflect.Method) = - m.getParameterTypes.isEmpty && valNames.contains(m.getName) && !m.getDeclaringClass.isAssignableFrom(rootClass) - this.getClass.getMethods.sortWith(_.getName < _.getName).filter(isPublicVal(_)) - } -} -/** Holds the implementation of toNamed for Data and MemBase */ -private[chisel3] trait NamedComponent extends HasId { - /** Returns a FIRRTL ComponentName that references this object - * @note Should not be called until circuit elaboration is complete - */ - final def toNamed: ComponentName = - ComponentName(this.instanceName, ModuleName(this.parentModName, CircuitName(this.circuitName))) - - /** Returns a FIRRTL ReferenceTarget that references this object - * @note Should not be called until circuit elaboration is complete - */ - final def toTarget: ReferenceTarget = { - val name = this.instanceName - import _root_.firrtl.annotations.{Target, TargetToken} - Target.toTargetTokens(name).toList match { - case TargetToken.Ref(r) :: components => ReferenceTarget(this.circuitName, this.parentModName, Nil, r, components) - case other => - throw _root_.firrtl.annotations.Target.NamedException(s"Cannot convert $name into [[ReferenceTarget]]: $other") - } - } - - final def toAbsoluteTarget: ReferenceTarget = { - val localTarget = toTarget - _parent match { - case Some(parent) => parent.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component) - case None => localTarget - } - } -} - -// Mutable global state for chisel that can appear outside a Builder context -private[chisel3] class ChiselContext() { - val idGen = new IdGen - - // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles - val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() -} - -private[chisel3] class DynamicContext() { - val globalNamespace = Namespace.empty - val components = ArrayBuffer[Component]() - val annotations = ArrayBuffer[ChiselAnnotation]() - var currentModule: Option[BaseModule] = None - - /** Contains a mapping from a elaborated module to their aspect - * Set by [[ModuleAspect]] - */ - val aspectModule: mutable.HashMap[BaseModule, BaseModule] = mutable.HashMap.empty[BaseModule, BaseModule] - - // Set by object Module.apply before calling class Module constructor - // Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping - var readyForModuleConstr: Boolean = false - var whenDepth: Int = 0 // Depth of when nesting - var currentClock: Option[Clock] = None - var currentReset: Option[Reset] = None - val errors = new ErrorLog - val namingStack = new NamingStack -} - -//scalastyle:off number.of.methods -private[chisel3] object Builder { - // All global mutable state must be referenced via dynamicContextVar!! - private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None) - private def dynamicContext: DynamicContext = { - require(dynamicContextVar.value.isDefined, "must be inside Builder context") - dynamicContextVar.value.get - } - - private val chiselContext = new DynamicVariable[ChiselContext](new ChiselContext) - - // Initialize any singleton objects before user code inadvertently inherits them. - private def initializeSingletons(): Unit = { - // This used to contain: - // val dummy = core.DontCare - // but this would occasionally produce hangs due to static initialization deadlock - // when Builder initialization collided with chisel3.package initialization of the DontCare value. - // See: - // http://ternarysearch.blogspot.com/2013/07/static-initialization-deadlock.html - // https://bugs.openjdk.java.net/browse/JDK-8037567 - // https://stackoverflow.com/questions/28631656/runnable-thread-state-but-in-object-wait - } - - def namingStackOption: Option[NamingStack] = dynamicContextVar.value.map(_.namingStack) - - def idGen: IdGen = chiselContext.value.idGen - - def globalNamespace: Namespace = dynamicContext.globalNamespace - def components: ArrayBuffer[Component] = dynamicContext.components - def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations - def namingStack: NamingStack = dynamicContext.namingStack - - def currentModule: Option[BaseModule] = dynamicContextVar.value match { - case Some(dyanmicContext) => dynamicContext.currentModule - case _ => None - } - def currentModule_=(target: Option[BaseModule]): Unit = { - dynamicContext.currentModule = target - } - def aspectModule(module: BaseModule): Option[BaseModule] = dynamicContextVar.value match { - case Some(dynamicContext) => dynamicContext.aspectModule.get(module) - case _ => None - } - def addAspect(module: BaseModule, aspect: BaseModule): Unit = { - dynamicContext.aspectModule += ((module, aspect)) - } - def forcedModule: BaseModule = currentModule match { - case Some(module) => module - case None => throwException( - "Error: Not in a Module. Likely cause: Missed Module() wrap or bare chisel API call." - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - def referenceUserModule: RawModule = { - currentModule match { - case Some(module: RawModule) => - aspectModule(module) match { - case Some(aspect: RawModule) => aspect - case other => module - } - case _ => throwException( - "Error: Not in a RawModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - } - def forcedUserModule: RawModule = currentModule match { - case Some(module: RawModule) => module - case _ => throwException( - "Error: Not in a UserModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox." // scalastyle:ignore line.size.limit - // A bare api call is, e.g. calling Wire() from the scala console). - ) - } - def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr - def readyForModuleConstr_=(target: Boolean): Unit = { - dynamicContext.readyForModuleConstr = target - } - def whenDepth: Int = dynamicContext.whenDepth - def whenDepth_=(target: Int): Unit = { - dynamicContext.whenDepth = target - } - def currentClock: Option[Clock] = dynamicContext.currentClock - def currentClock_=(newClock: Option[Clock]): Unit = { - dynamicContext.currentClock = newClock - } - - def currentReset: Option[Reset] = dynamicContext.currentReset - def currentReset_=(newReset: Option[Reset]): Unit = { - dynamicContext.currentReset = newReset - } - - def forcedClock: Clock = currentClock.getOrElse( - throwException("Error: No implicit clock.") - ) - def forcedReset: Reset = currentReset.getOrElse( - throwException("Error: No implicit reset.") - ) - - // TODO(twigg): Ideally, binding checks and new bindings would all occur here - // However, rest of frontend can't support this yet. - def pushCommand[T <: Command](c: T): T = { - forcedUserModule.addCommand(c) - c - } - def pushOp[T <: Data](cmd: DefPrim[T]): T = { - // Bind each element of the returned Data to being a Op - cmd.id.bind(OpBinding(forcedUserModule)) - pushCommand(cmd).id - } - - // Called when Bundle construction begins, used to record a stack of open Bundle constructors to - // record candidates for Bundle autoclonetype. This is a best-effort guess. - // Returns the current stack of open Bundles - // Note: elt will NOT have finished construction, its elements cannot be accessed - def updateBundleStack(elt: Bundle): Seq[Bundle] = { - val stackElts = Thread.currentThread().getStackTrace() - .reverse // so stack frame numbers are deterministic across calls - .dropRight(2) // discard Thread.getStackTrace and updateBundleStack - - // Determine where we are in the Bundle stack - val eltClassName = elt.getClass.getName - val eltStackPos = stackElts.map(_.getClassName).lastIndexOf(eltClassName) - - // Prune the existing Bundle stack of closed Bundles - // If we know where we are in the stack, discard frames above that - val stackEltsTop = if (eltStackPos >= 0) eltStackPos else stackElts.size - val pruneLength = chiselContext.value.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => - pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname - } - chiselContext.value.bundleStack.trimEnd(pruneLength) - - // Return the stack state before adding the most recent bundle - val lastStack = chiselContext.value.bundleStack.map(_._1).toSeq - - // Append the current Bundle to the stack, if it's on the stack trace - if (eltStackPos >= 0) { - val stackElt = stackElts(eltStackPos) - chiselContext.value.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) - } - // Otherwise discard the stack frame, this shouldn't fail noisily - - lastStack - } - - /** Recursively suggests names to supported "container" classes - * Arbitrary nestings of supported classes are allowed so long as the - * innermost element is of type HasId - * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded) - */ - def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match { - case (id: HasId) => namer(id, prefix) - case Some(elt) => nameRecursively(prefix, elt, namer) - case (iter: Iterable[_]) if iter.hasDefiniteSize => - for ((elt, i) <- iter.zipWithIndex) { - nameRecursively(s"${prefix}_${i}", elt, namer) - } - case _ => // Do nothing - } - - def errors: ErrorLog = dynamicContext.errors - def error(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.error(m) - def warning(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warning(m) - def deprecated(m: => String, location: Option[String] = None): Unit = - if (dynamicContextVar.value.isDefined) errors.deprecated(m, location) - - /** Record an exception as an error, and throw it. - * - * @param m exception message - */ - @throws(classOf[ChiselException]) - def exception(m: => String): Unit = { - error(m) - throwException(m) - } - - def build[T <: RawModule](f: => T): (Circuit, T) = { - chiselContext.withValue(new ChiselContext) { - dynamicContextVar.withValue(Some(new DynamicContext())) { - errors.info("Elaborating design...") - val mod = f - mod.forceName(mod.name, globalNamespace) - errors.checkpoint() - errors.info("Done elaborating.") - - (Circuit(components.last.name, components, annotations), mod) - } - } - } - initializeSingletons() -} - -/** Allows public access to the naming stack in Builder / DynamicContext, and handles invocations - * outside a Builder context. - * Necessary because naming macros expand in user code and don't have access into private[chisel3] - * objects. - */ -object DynamicNamingStack { - def pushContext(): NamingContextInterface = { - Builder.namingStackOption match { - case Some(namingStack) => namingStack.pushContext() - case None => DummyNamer - } - } - - def popReturnContext[T <: Any](prefixRef: T, until: NamingContextInterface): T = { - until match { - case DummyNamer => - require(Builder.namingStackOption.isEmpty, - "Builder context must remain stable throughout a chiselName-annotated function invocation") - case context: NamingContext => - require(Builder.namingStackOption.isDefined, - "Builder context must remain stable throughout a chiselName-annotated function invocation") - Builder.namingStackOption.get.popContext(prefixRef, context) - } - prefixRef - } - - def length() : Int = Builder.namingStackOption.get.length -} - -/** Casts BigInt to Int, issuing an error when the input isn't representable. */ -private[chisel3] object castToInt { - def apply(x: BigInt, msg: String): Int = { - val res = x.toInt - require(x == res, s"$msg $x is too large to be represented as Int") - res - } -} |
