diff options
Diffstat (limited to 'chiselFrontend/src')
8 files changed, 109 insertions, 75 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 559a55bc..732bf8fc 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -335,50 +335,93 @@ trait VecLike[T <: Data] extends collection.IndexedSeq[T] with HasId { SeqUtils.oneHotMux(indexWhereHelper(p)) } -/** Base class for data types defined as a bundle of other data types. +/** Base class for Aggregates based on key values pairs of String and Data * - * Usage: extend this class (either as an anonymous or named class) and define - * members variables of [[Data]] subtypes to be elements in the Bundle. + * Record should only be extended by libraries and fairly sophisticated generators. + * RTL writers should use [[Bundle]]. */ -class Bundle extends Aggregate { - private val _namespace = Builder.globalNamespace.child +abstract class Record extends Aggregate { - // TODO: replace with better defined FIRRTL weak-connect operator - /** Connect elements in this Bundle to elements in `that` on a best-effort - * (weak) basis, matching by type, orientation, and name. - * - * @note unconnected elements will NOT generate errors or warnings + /** The collection of [[Data]] * - * @example + * This underlying datastructure is a ListMap because the elements must + * remain ordered for serialization/deserialization. Elements added later + * are higher order when serialized (this is similar to [[Vec]]). For example: * {{{ - * // Pass through wires in this module's io to those mySubModule's io, - * // matching by type, orientation, and name, and ignoring extra wires. - * mySubModule.io <> io + * // Assume we have some type MyRecord that creates a Record from the ListMap + * val record = MyRecord(ListMap("fizz" -> UInt(16.W), "buzz" -> UInt(16.W))) + * // "buzz" is higher order because it was added later than "fizz" + * record("fizz") := "hdead".U + * record("buzz") := "hbeef".U + * val uint = record.asUInt + * assert(uint === "hbeefdead".U) // This will pass * }}} */ + val elements: ListMap[String, Data] - lazy val elements: ListMap[String, Data] = ListMap(namedElts:_*) + /** Name for Pretty Printing */ + def className: String = this.getClass.getSimpleName - /** Returns a best guess at whether a field in this Bundle is a user-defined - * Bundle element without looking at type signatures. - */ - private def isBundleField(m: java.lang.reflect.Method) = - m.getParameterTypes.isEmpty && - !java.lang.reflect.Modifier.isStatic(m.getModifiers) && - !(Bundle.keywords contains m.getName) && !(m.getName contains '$') + private[chisel3] def toType = { + def eltPort(elt: Data): String = { + val flipStr: String = if(Data.isFirrtlFlipped(elt)) "flip " else "" + s"${flipStr}${elt.getRef.name} : ${elt.toType}" + } + elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}") + } - /** Returns a field's contained user-defined Bundle element if it appears to - * be one, otherwise returns None. - */ - private def getBundleField(m: java.lang.reflect.Method): Option[Data] = m.invoke(this) match { - case d: Data => Some(d) - case Some(d: Data) => Some(d) - case _ => None + private[chisel3] lazy val flatten = elements.toIndexedSeq.flatMap(_._2.flatten) + + // NOTE: This sets up dependent references, it can be done before closing the Module + private[chisel3] override def _onModuleClose: Unit = { // scalastyle:ignore method.name + val _namespace = Builder.globalNamespace.child + for ((name, elt) <- elements) { elt.setRef(this, _namespace.name(name)) } } - /** Returns a list of elements in this Bundle. + private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) + + // Helper because Bundle elements are reversed before printing + private[chisel3] def toPrintableHelper(elts: Seq[(String, Data)]): Printable = { + val xs = + if (elts.isEmpty) List.empty[Printable] // special case because of dropRight below + else elts flatMap { case (name, data) => + List(PString(s"$name -> "), data.toPrintable, PString(", ")) + } dropRight 1 // Remove trailing ", " + PString(s"$className(") + Printables(xs) + PString(")") + } + /** Default "pretty-print" implementation + * Analogous to printing a Map + * Results in "$className(elt0.name -> elt0.value, ...)" + */ + def toPrintable: Printable = toPrintableHelper(elements.toList) +} + +/** Base class for data types defined as a bundle of other data types. + * + * Usage: extend this class (either as an anonymous or named class) and define + * members variables of [[Data]] subtypes to be elements in the Bundle. + */ +class Bundle extends Record { + override def className = "Bundle" + + /** The collection of [[Data]] + * + * Elements defined earlier in the Bundle are higher order upon + * serialization. For example: + * {{{ + * class MyBundle extends Bundle { + * val foo = UInt(16.W) + * val bar = UInt(16.W) + * } + * // Note that foo is higher order because its defined earlier in the Bundle + * val bundle = Wire(new MyBundle) + * bundle.foo := 0x1234.U + * bundle.bar := 0x5678.U + * val uint = bundle.asUInt + * assert(uint === "h12345678".U) // This will pass + * }}} */ - private[core] lazy val namedElts = { + final lazy val elements: ListMap[String, Data] = { val nameMap = LinkedHashMap[String, Data]() val seen = HashSet[Data]() for (m <- getPublicFields(classOf[Bundle])) { @@ -391,20 +434,17 @@ class Bundle extends Aggregate { } } } - ArrayBuffer(nameMap.toSeq:_*) sortWith {case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn))} - } - private[chisel3] def toType = { - def eltPort(elt: Data): String = { - val flipStr: String = if(Data.isFirrtlFlipped(elt)) "flip " else "" - s"${flipStr}${elt.getRef.name} : ${elt.toType}" - } - s"{${namedElts.reverse.map(e => eltPort(e._2)).mkString(", ")}}" + ListMap(nameMap.toSeq sortWith { case ((an, a), (bn, b)) => (a._id > b._id) || ((a eq b) && (an > bn)) }: _*) } - private[chisel3] lazy val flatten = namedElts.flatMap(_._2.flatten) - private[chisel3] override def _onModuleClose: Unit = // scalastyle:ignore method.name - for ((name, elt) <- namedElts) { elt.setRef(this, _namespace.name(name)) } - private[chisel3] final def allElements: Seq[Element] = namedElts.flatMap(_._2.allElements) + /** Returns a field's contained user-defined Bundle element if it appears to + * be one, otherwise returns None. + */ + private def getBundleField(m: java.lang.reflect.Method): Option[Data] = m.invoke(this) match { + case d: Data => Some(d) + case Some(d: Data) => Some(d) + case _ => None + } override def cloneType : this.type = { // If the user did not provide a cloneType method, try invoking one of @@ -436,20 +476,14 @@ class Bundle extends Aggregate { /** Default "pretty-print" implementation * Analogous to printing a Map * Results in "Bundle(elt0.name -> elt0.value, ...)" + * @note The order is reversed from the order of elements in order to print + * the fields in the order they were defined */ - def toPrintable: Printable = { - val elts = - if (elements.isEmpty) List.empty[Printable] - else { - elements.toList.reverse flatMap { case (name, data) => - List(PString(s"$name -> "), data.toPrintable, PString(", ")) - } dropRight 1 // Remove trailing ", " - } - PString("Bundle(") + Printables(elts) + PString(")") - } + override def toPrintable: Printable = toPrintableHelper(elements.toList.reverse) } private[core] object Bundle { val keywords = List("flip", "asInput", "asOutput", "cloneType", "chiselCloneType", "toBits", "widthOption", "signalName", "signalPathName", "signalParent", "signalComponent") } + diff --git a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala index 2599a20a..b17239e7 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala @@ -37,9 +37,9 @@ object BiConnect { def MismatchedVecException = BiConnectException(": Left and Right are different length Vecs.") def MissingLeftFieldException(field: String) = - BiConnectException(s".$field: Left Bundle missing field ($field).") + BiConnectException(s".$field: Left Record missing field ($field).") def MissingRightFieldException(field: String) = - BiConnectException(s": Right Bundle missing field ($field).") + BiConnectException(s": Right Record missing field ($field).") def MismatchedException(left: String, right: String) = BiConnectException(s": Left ($left) and Right ($right) have different types.") @@ -67,20 +67,20 @@ object BiConnect { } } } - // Handle Bundle case - case (left_b: Bundle, right_b: Bundle) => { + // Handle Record case + case (left_r: Record, right_r: Record) => { // Verify right has no extra fields that left doesn't have - for((field, right_sub) <- right_b.elements) { - if(!left_b.elements.isDefinedAt(field)) { + for((field, right_sub) <- right_r.elements) { + if(!left_r.elements.isDefinedAt(field)) { if (connectCompileOptions.connectFieldsMustMatch) { throw MissingLeftFieldException(field) } } } // For each field in left, descend with right - for((field, left_sub) <- left_b.elements) { + for((field, left_sub) <- left_r.elements) { try { - right_b.elements.get(field) match { + right_r.elements.get(field) match { case Some(right_sub) => connect(sourceInfo, connectCompileOptions, left_sub, right_sub, context_mod) case None => { if (connectCompileOptions.connectFieldsMustMatch) { diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 3dfde7c2..71c441a7 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -33,7 +33,7 @@ import chisel3.internal.Builder.{forcedModule} * Aggregate is considered bound via its elements. May be appropriate to allow * Aggregates to be bound along with the Elements. However, certain literal and * port direction information doesn't quite make sense in aggregates. This would - * elegantly handle the empty Vec or Bundle problem though. + * elegantly handle the empty Vec or Record problem though. * * TODO(twigg): Binding is currently done via allElements. It may be more * elegant if this was instead done as a more explicit tree walk as that allows @@ -73,8 +73,8 @@ object Binding { } } } - case (bundle: Bundle) => { - for((field, subelem) <- bundle.elements) { + case (record: Record) => { + for((field, subelem) <- record.elements) { try walkToBinding(subelem, checker) catch { case BindingException(message) => throw BindingException(s".$field$message") diff --git a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala index 4dea39b5..4aa3ad33 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala @@ -5,7 +5,7 @@ package chisel3.core import scala.language.experimental.macros trait CompileOptions { - // Should Bundle connections require a strict match of fields. + // Should Record connections require a strict match of fields. // If true and the same fields aren't present in both source and sink, a MissingFieldException, // MissingLeftFieldException, or MissingRightFieldException will be thrown. val connectFieldsMustMatch: Boolean diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala index 6e80f045..4f3e2268 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala @@ -66,7 +66,7 @@ object Data { * Note that the current scheme only applies Flip to Elements or Vec chains of * Elements. * - * A Bundle is never marked flip, instead preferring its root fields to be marked + * A Record is never marked flip, instead preferring its root fields to be marked * * The Vec check is due to the fact that flip must be factored out of the vec, ie: * must have flip field: Vec(UInt) instead of field: Vec(flip UInt) @@ -74,7 +74,7 @@ object Data { private[chisel3] def isFlipped(target: Data): Boolean = target match { case (element: Element) => element.binding.direction == Some(Direction.Input) case (vec: Vec[Data @unchecked]) => isFlipped(vec.sample_element) - case (bundle: Bundle) => false + case (record: Record) => false } /** This function returns the "firrtl" flipped-ness for the specified object. diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala index 76a3b240..609f2ccf 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala @@ -141,7 +141,7 @@ extends HasId { /** IO for this Module. At the Scala level (pre-FIRRTL transformations), * connections in and out of a Module may only go through `io` elements. */ - def io: Bundle + def io: Record val clock = Port(Input(Clock())) val reset = Port(Input(Bool())) diff --git a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala index fcb14e6f..8b264801 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala @@ -15,7 +15,7 @@ import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfo * * The connect operation will recurse down the left Data (with the right Data). * An exception will be thrown if a movement through the left cannot be matched -* in the right. The right side is allowed to have extra Bundle fields. +* in the right. The right side is allowed to have extra Record fields. * Vecs must still be exactly the same size. * * See elemConnect for details on how the root connections are issued. @@ -45,7 +45,7 @@ object MonoConnect { def MismatchedVecException = MonoConnectException(": Sink and Source are different length Vecs.") def MissingFieldException(field: String) = - MonoConnectException(s": Source Bundle missing field ($field).") + MonoConnectException(s": Source Record missing field ($field).") def MismatchedException(sink: String, source: String) = MonoConnectException(s": Sink ($sink) and Source ($source) have different types.") @@ -73,12 +73,12 @@ object MonoConnect { } } } - // Handle Bundle case - case (sink_b: Bundle, source_b: Bundle) => { + // Handle Record case + case (sink_r: Record, source_r: Record) => { // For each field, descend with right - for((field, sink_sub) <- sink_b.elements) { + for((field, sink_sub) <- sink_r.elements) { try { - source_b.elements.get(field) match { + source_r.elements.get(field) match { case Some(source_sub) => connect(sourceInfo, connectCompileOptions, sink_sub, source_sub, context_mod) case None => { if (connectCompileOptions.connectFieldsMustMatch) { diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 6e463311..c93dbfc7 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -92,7 +92,7 @@ private[chisel3] trait HasId extends InstanceId { // 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 Bundle) + // (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) |
