|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | + |
| 3 | +package chisel3.experimental.hierarchy |
| 4 | + |
| 5 | +import scala.reflect.runtime.{universe => ru} |
| 6 | +import scala.collection.mutable |
| 7 | + |
| 8 | +import chisel3._ |
| 9 | +import chisel3.experimental.{BaseModule, SourceInfo, UnlocatableSourceInfo} |
| 10 | +import chisel3.reflect.DataMirror |
| 11 | +import chisel3.reflect.DataMirror.internal.isSynthesizable |
| 12 | +import chisel3.internal.Builder |
| 13 | + |
| 14 | +private[chisel3] trait InstantiateImpl { |
| 15 | + |
| 16 | + // Data uses referential equality by default, but for looking up Data in the cache, we need to use |
| 17 | + // structural equality for Data unbound types and literal values |
| 18 | + // Note that this is somewhat incomplete because we can't handle the general case of user-defined |
| 19 | + // types that contain Data. |
| 20 | + // A more complete and robust solution would require either refining hashCode and equality on Data |
| 21 | + // to have the desired behavior, or using a different Map implementation that uses typeclasses for |
| 22 | + // equality and hashcode (eg. cats-collections-core's HashMap) |
| 23 | + private class DataBox(private val d: Data) { |
| 24 | + |
| 25 | + private def convertDataForHashing(data: Data): Any = data match { |
| 26 | + // Using toString is a bit weird but it works |
| 27 | + case elt: Element => elt.toString |
| 28 | + case rec: Record => |
| 29 | + // Purely structural, actual class of Record isn't involved |
| 30 | + rec.elements.map { case (name, data) => name -> convertDataForHashing(data) } |
| 31 | + case vec: Vec[_] => |
| 32 | + // We could map on elements but that's a lot of duplicated work for a type |
| 33 | + // Note that empty vecs of the same type will give same hash value, probably should be equal |
| 34 | + // as well, but Vec.typeEquivalent checks sample_element so they will not be equal |
| 35 | + ("Vec", vec.size, vec.headOption.map(convertDataForHashing(_))) |
| 36 | + } |
| 37 | + |
| 38 | + override def hashCode: Int = convertDataForHashing(d).hashCode |
| 39 | + |
| 40 | + // If literals, check same types and equal |
| 41 | + // If types, chck same types |
| 42 | + // If bound, fall back to normal equality |
| 43 | + override def equals(that: Any): Boolean = { |
| 44 | + that match { |
| 45 | + case that: DataBox => |
| 46 | + // def because it's not needed by non-literal but is synthesizable check |
| 47 | + def sameTypes = DataMirror.checkTypeEquivalence(this.d, that.d) |
| 48 | + // Lits are synthesizable so must check them first |
| 49 | + if (this.d.isLit) { |
| 50 | + that.d.isLit && |
| 51 | + (this.d.litValue == that.d.litValue) && |
| 52 | + sameTypes |
| 53 | + } else if (isSynthesizable(this.d)) { |
| 54 | + this.d.equals(that.d) |
| 55 | + } else { |
| 56 | + // We have checked that this.d is not synthesizable but need to check that.d as well |
| 57 | + sameTypes && !isSynthesizable(that.d) |
| 58 | + } |
| 59 | + |
| 60 | + case _ => false |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + // Recursively box all Data (by traversing Products and Iterables) in DataBoxes |
| 66 | + private def boxAllData(a: Any): Any = a match { |
| 67 | + case d: Data => new DataBox(d) // Must check this before Iterable because Vec is Iterable |
| 68 | + // Must check before Product, because many Iterables are Products, but can still be equal, eg. List(1) == Vector(1) |
| 69 | + case it: Iterable[_] => it.map(boxAllData(_)) |
| 70 | + case p: Product => Vector(p.getClass) ++ p.productIterator.map(boxAllData(_)) |
| 71 | + case other => other |
| 72 | + } |
| 73 | + |
| 74 | + import chisel3.internal.BuilderContextCache |
| 75 | + // Include type of module in key since different modules could have the same arguments |
| 76 | + private case class CacheKey[A <: BaseModule](args: Any, tt: Any, modulePrefix: List[String]) |
| 77 | + extends BuilderContextCache.Key[Definition[A]] |
| 78 | + |
| 79 | + protected def _instanceImpl[K, A <: BaseModule]( |
| 80 | + args: K, |
| 81 | + f: K => A, |
| 82 | + tt: Any |
| 83 | + )( |
| 84 | + implicit sourceInfo: SourceInfo |
| 85 | + ): Instance[A] = Instance.do_apply(_definitionImpl(args, f, tt))(sourceInfo) |
| 86 | + |
| 87 | + /** This is not part of the public API, do not call directly! */ |
| 88 | + protected def _definitionImpl[K, A <: BaseModule]( |
| 89 | + args: K, |
| 90 | + f: K => A, |
| 91 | + tt: Any |
| 92 | + ): Definition[A] = { |
| 93 | + val modulePrefix = Builder.getModulePrefixList |
| 94 | + Builder.contextCache |
| 95 | + .getOrElseUpdate( |
| 96 | + CacheKey[A](boxAllData(args), tt, modulePrefix), { |
| 97 | + // The definition needs to have no source locator because otherwise it will be unstably |
| 98 | + // derived from the first invocation of Instantiate for the particular Module |
| 99 | + Definition.do_apply(f(args))(UnlocatableSourceInfo) |
| 100 | + } |
| 101 | + ) |
| 102 | + .asInstanceOf[Definition[A]] |
| 103 | + } |
| 104 | +} |
0 commit comments