summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/internal/Builder.scala
blob: f7306d5df796ad523e632073e578267e77de1b9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
// SPDX-License-Identifier: Apache-2.0

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 _root_.firrtl.annotations.AnnotationUtils.validComponentName
import _root_.firrtl.{AnnotationSeq, RenameMap}
import chisel3.experimental.dataview.{reify, reifySingleData}
import chisel3.internal.Builder.Prefix
import logger.LazyLogging

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
  }
  def value: Long = 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 = {}
  private[chisel3] var _parent: Option[BaseModule] = Builder.currentModule

  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)

  // Contains suggested seed (user-decided seed)
  private var suggested_seed: Option[String] = None

  // Contains the seed computed automatically by the compiler plugin
  private var auto_seed: Option[String] = None

  // Prefix at time when this class is constructed
  private val construction_prefix: Prefix = Builder.getPrefix()

  // Prefix when the latest [[suggestSeed]] or [[autoSeed]] is called
  private var prefix_seed: Prefix = Nil

  // Post-seed hooks called to carry the suggested seeds to other candidates as needed
  private val suggest_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]

  // Post-seed hooks called to carry the auto seeds to other candidates as needed
  private val auto_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]

  /** Takes the last seed suggested. Multiple calls to this function will take the last given seed, unless
    * this HasId is a module port (see overridden method in Data.scala).
    *
    * If the final computed name conflicts with the final name of another signal, the final name may get uniquified by
    * appending a digit at the end of the name.
    *
    * Is a lower priority than [[suggestName]], in that regardless of whether [[autoSeed]]
    * was called, [[suggestName]] will always take precedence if it was called.
 *
    * @param seed Seed for the name of this component
    * @return this object
    */
  private[chisel3] def autoSeed(seed: String): this.type = forceAutoSeed(seed)
  // Bypass the overridden behavior of autoSeed in [[Data]], apply autoSeed even to ports
  private[chisel3] def forceAutoSeed(seed: String): this.type = {
    auto_seed = Some(seed)
    for(hook <- auto_postseed_hooks) { hook(seed) }
    prefix_seed = Builder.getPrefix()
    this
  }

  /** Takes the first seed suggested. Multiple calls to this function will be ignored.
    * If the final computed name conflicts with another name, it may get uniquified by appending
    * a digit at the end.
    *
    * Is a higher priority than [[autoSeed]], in that regardless of whether [[autoSeed]]
    * was called, [[suggestName]] will always take precedence.
 *
    * @param seed The seed for the name of this component
    * @return this object
    */
  def suggestName(seed: =>String): this.type = {
    if(suggested_seed.isEmpty) suggested_seed = Some(seed)
    prefix_seed = Builder.getPrefix()
    for(hook <- suggest_postseed_hooks) { hook(seed) }
    this
  }

  /** Computes the name of this HasId, if one exists
    * @param defaultPrefix Optionally provide a default prefix for computing the name
    * @param defaultSeed Optionally provide default seed for computing the name
    * @return the name, if it can be computed
    */
  def computeName(defaultPrefix: Option[String], defaultSeed: Option[String]): Option[String] = {
    /** Computes a name of this signal, given the seed and prefix
      * @param seed
      * @param prefix
      * @return
      */
    def buildName(seed: String, prefix: Prefix): String = {
      val builder = new StringBuilder()
      prefix.foreach(builder ++= _ + "_")
      builder ++= seed
      builder.toString
    }

    if (hasSeed) {
      Some(buildName(seedOpt.get, prefix_seed.reverse))
    } else {
      defaultSeed.map { default =>
        defaultPrefix match {
          case Some(p) => buildName(default, p :: construction_prefix.reverse)
          case None => buildName(default, construction_prefix.reverse)
        }
      }
    }
  }

  /** This resolves the precedence of [[autoSeed]] and [[suggestName]]
 *
    * @return the current calculation of a name, if it exists
    */
  private[chisel3] def seedOpt: Option[String] = suggested_seed.orElse(auto_seed)

  /** @return Whether either autoName or suggestName has been called */
  def hasSeed: Boolean = seedOpt.isDefined

  private[chisel3] def hasAutoSeed: Boolean = auto_seed.isDefined

  private[chisel3] def addSuggestPostnameHook(hook: String=>Unit): Unit = suggest_postseed_hooks += hook
  private[chisel3] def addAutoPostnameHook(hook: String=>Unit): Unit = auto_postseed_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(prefix: Option[String], default: =>String, namespace: Namespace): Unit =
    if(_ref.isEmpty) {
      val candidate_name = computeName(prefix, Some(default)).get
      val available_name = namespace.name(candidate_name)
      setRef(Ref(available_name))
    }

  private var _ref: Option[Arg] = None
  private[chisel3] def setRef(imm: Arg): Unit = setRef(imm, false)
  private[chisel3] def setRef(imm: Arg, force: Boolean): Unit = {
    if (_ref.isEmpty || force) {
      _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

  private def localName(c: Component): String = _ref match {
    case Some(arg) => arg fullName c
    case None => computeName(None, None).get
  }

  // Helper for reifying views if they map to a single Target
  private[chisel3] def reifyTarget: Option[Data] = this match {
    case d: Data => reifySingleData(d) // Only Data can be views
    case bad => throwException(s"This shouldn't be possible - got $bad with ${_parent}")
  }

  // Helper for reifying the parent of a view if the view maps to a single Target
  private[chisel3] def reifyParent: BaseModule = reifyTarget.flatMap(_._parent).getOrElse(ViewParent)

  // Implementation of public methods.
  def instanceName: String = _parent match {
    case Some(ViewParent) => reifyTarget.map(_.instanceName).getOrElse(this.localName(ViewParent.fakeComponent))
    case Some(p) => p._component match {
      case Some(c) => localName(c)
      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(ViewParent) => s"${reifyParent.pathName}.$instanceName"
    case Some(p) => s"${p.pathName}.$instanceName"
  }
  def parentPathName: String = _parent match {
    case Some(ViewParent) => reifyParent.pathName
    case Some(p) => p.pathName
    case None => throwException(s"$instanceName doesn't have a parent")
  }
  def parentModName: String = _parent match {
    case Some(ViewParent) => reifyParent.name
    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(ViewParent) => reifyParent.circuitName
    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) = {
      val noParameters = m.getParameterTypes.isEmpty
      val aVal = valNames.contains(m.getName)
      val notAssignable = !m.getDeclaringClass.isAssignableFrom(rootClass)
      val notWeirdVal = !m.getName.contains('$')
      noParameters && aVal && notAssignable && notWeirdVal
    }
    this.getClass.getMethods.filter(isPublicVal).sortWith(_.getName < _.getName)
  }
}
/** 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
    if (!validComponentName(name)) throwException(s"Illegal component name: $name (note: literals are illegal)")
    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
    def makeTarget(p: BaseModule) = p.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component)
    _parent match {
      case Some(ViewParent) => makeTarget(reifyParent)
      case Some(parent) => makeTarget(parent)
      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()

  // Records the different prefixes which have been scoped at this point in time
  var prefixStack: Prefix = Nil

  // Views belong to a separate namespace (for renaming)
  // The namespace outside of Builder context is useless, but it ensures that views can still be created
  // and the resulting .toTarget is very clearly useless (_$$View$$_...)
  val viewNamespace = Namespace.empty
}

private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) {
  val globalNamespace = Namespace.empty
  val components = ArrayBuffer[Component]()
  val annotations = ArrayBuffer[ChiselAnnotation]()
  var currentModule: Option[BaseModule] = None
  // This is only used for testing, it can be removed if the plugin becomes mandatory
  var allowReflectiveAutoCloneType = true

  /** Contains a mapping from a elaborated module to their aspect
    * Set by [[ModuleAspect]]
    */
  val aspectModule: mutable.HashMap[BaseModule, BaseModule] = mutable.HashMap.empty[BaseModule, BaseModule]

  // Views that do not correspond to a single ReferenceTarget and thus require renaming
  val unnamedViews: ArrayBuffer[Data] = ArrayBuffer.empty

  // 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 whenStack: List[WhenContext] = Nil
  var currentClock: Option[Clock] = None
  var currentReset: Option[Reset] = None
  val errors = new ErrorLog
  val namingStack = new NamingStack
}

private[chisel3] object Builder extends LazyLogging {

  // Represents the current state of the prefixes given
  type Prefix = List[String]

  // 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
  }

  // Ensure we have a thread-specific ChiselContext
  private val chiselContext = new ThreadLocal[ChiselContext]{
    override def initialValue: 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.get.idGen

  def globalNamespace: Namespace = dynamicContext.globalNamespace
  def components: ArrayBuffer[Component] = dynamicContext.components
  def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
  def annotationSeq: AnnotationSeq = dynamicContext.annotationSeq
  def namingStack: NamingStack = dynamicContext.namingStack

  def unnamedViews: ArrayBuffer[Data] = dynamicContext.unnamedViews
  def viewNamespace: Namespace = chiselContext.get.viewNamespace

  // Puts a prefix string onto the prefix stack
  def pushPrefix(d: String): Unit = {
    val context = chiselContext.get()
    context.prefixStack = d :: context.prefixStack
  }

  /** Pushes the current name of a data onto the prefix stack
    *
    * @param d data to derive prefix from
    * @return whether anything was pushed to the stack
    */
  def pushPrefix(d: HasId): Boolean = {
    def buildAggName(id: HasId): Option[String] = {
      def getSubName(field: Data): Option[String] = field.getOptionRef.flatMap {
        case Slot(_, field) => Some(field) // Record
        case Index(_, ILit(n)) => Some(n.toString) // Vec static indexing
        case Index(_, ULit(n, _)) => Some(n.toString) // Vec lit indexing
        case Index(_, _: Node) => None // Vec dynamic indexing
        case ModuleIO(_, n) => Some(n) // BlackBox port
      }
      def map2[A, B](a: Option[A], b: Option[A])(f: (A, A) => B): Option[B] =
        a.flatMap(ax => b.map(f(ax, _)))
      // If the binding is None, this is an illegal connection and later logic will error
      def recData(data: Data): Option[String] = data.binding.flatMap {
        case (_: WireBinding | _: RegBinding | _: MemoryPortBinding | _: OpBinding) => data.seedOpt
        case ChildBinding(parent) => recData(parent).map { p =>
          // And name of the field if we have one, we don't for dynamic indexing of Vecs
          getSubName(data).map(p + "_" + _).getOrElse(p)
        }
        case SampleElementBinding(parent) => recData(parent)
        case PortBinding(mod) if Builder.currentModule.contains(mod) => data.seedOpt
        case PortBinding(mod) => map2(mod.seedOpt, data.seedOpt)(_ + "_" + _)
        case (_: LitBinding | _: DontCareBinding) => None
        case _ => Some("view_") // TODO implement
      }
      id match {
        case d: Data => recData(d)
        case _ => None
      }
    }
    buildAggName(d).map { name =>
      pushPrefix(name)
    }.isDefined
  }

  // Remove a prefix from top of the stack
  def popPrefix(): List[String] = {
    val context = chiselContext.get()
    val tail = context.prefixStack.tail
    context.prefixStack = tail
    tail
  }

  // Removes all prefixes from the prefix stack
  def clearPrefix(): Unit = {
    chiselContext.get().prefixStack = Nil
  }

  // Clears existing prefixes and sets to new prefix stack
  def setPrefix(prefix: Prefix): Unit = {
    chiselContext.get().prefixStack = prefix
  }

  // Returns the prefix stack at this moment
  def getPrefix(): Prefix = chiselContext.get().prefixStack

  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
  }
  /** Retrieves the parent of a module based on the elaboration context
   *
   * @param module the module to get the parent of
   * @param context the context the parent should be evaluated in
   * @return the parent of the module provided
   */
  def retrieveParent(module: BaseModule, context: BaseModule): Option[BaseModule] = {
    module._parent match {
      case Some(parentModule) => { //if a parent exists investigate, otherwise return None
        context match {
          case aspect: ModuleAspect => { //if aspect context, do the translation
              Builder.aspectModule(parentModule) match {
                case Some(parentAspect) => Some(parentAspect) //we've found a translation
                case _ => Some(parentModule) //no translation found
              }
          } //otherwise just return our parent
          case _ => Some(parentModule)
        }
      }
      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."
        // 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."
      // 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.whenStack.length

  def pushWhen(wc: WhenContext): Unit = {
    dynamicContext.whenStack = wc :: dynamicContext.whenStack
  }

  def popWhen(): WhenContext = {
    val lastWhen = dynamicContext.whenStack.head
    dynamicContext.whenStack = dynamicContext.whenStack.tail
    lastWhen
  }

  def whenStack: List[WhenContext] = dynamicContext.whenStack
  def whenStack_=(s: List[WhenContext]): Unit = {
    dynamicContext.whenStack = s
  }

  def currentWhen(): Option[WhenContext] = dynamicContext.whenStack.headOption

  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
  }

  // This should only be used for testing, must be true outside of Builder context
  def allowReflectiveAutoCloneType: Boolean = {
    dynamicContextVar.value
                     .map(_.allowReflectiveAutoCloneType)
                     .getOrElse(true)
  }
  def allowReflectiveAutoCloneType_=(value: Boolean): Unit = {
    dynamicContext.allowReflectiveAutoCloneType = value
  }

  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, currentWhen()))
    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.get.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) =>
      pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname
    }
    chiselContext.get.bundleStack.trimEnd(pruneLength)

    // Return the stack state before adding the most recent bundle
    val lastStack = chiselContext.get.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.get.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)
    } else {
      throwException(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): Nothing = {
    error(m)
    throwException(m)
  }

  def getScalaMajorVersion: Int = {
    val "2" :: major :: _ :: Nil = chisel3.BuildInfo.scalaVersion.split("\\.").toList
    major.toInt
  }

  def checkScalaVersion(): Unit = {
    if (getScalaMajorVersion == 11) {
      val url = _root_.firrtl.stage.transforms.CheckScalaVersion.migrationDocumentLink
      val msg = s"Chisel 3.4 is the last version that will support Scala 2.11. " +
                s"Please upgrade to Scala 2.12. See $url"
      deprecated(msg, Some(""))
    }
  }

  // Builds a RenameMap for all Views that do not correspond to a single Data
  // These Data give a fake ReferenceTarget for .toTarget and .toReferenceTarget that the returned
  // RenameMap can split into the constituent parts
  private[chisel3] def makeViewRenameMap: RenameMap = {
    val renames = RenameMap()
    for (view <- unnamedViews) {
      val localTarget = view.toTarget
      val absTarget = view.toAbsoluteTarget
      val elts = getRecursiveFields.lazily(view, "")
        .collect { case (elt: Element, _) => elt }
      for (elt <- elts) {
        val targetOfView = reify(elt)
        renames.record(localTarget, targetOfView.toTarget)
        renames.record(absTarget, targetOfView.toAbsoluteTarget)
      }
    }
    renames
  }

  private [chisel3] def build[T <: RawModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = {
    dynamicContextVar.withValue(Some(dynamicContext)) {
      ViewParent // Must initialize the singleton in a Builder context or weird things can happen
                 // in tiny designs/testcases that never access anything in chisel3.internal
      checkScalaVersion()
      logger.info("Elaborating design...")
      val mod = f
      mod.forceName(None, mod.name, globalNamespace)
      errors.checkpoint(logger)
      logger.info("Done elaborating.")

      (Circuit(components.last.name, components.toSeq, annotations.toSeq, makeViewRenameMap), 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
  }
}