summaryrefslogtreecommitdiff
path: root/chiselFrontend
diff options
context:
space:
mode:
authorJack Koenig2017-01-20 12:37:40 -0800
committerGitHub2017-01-20 12:37:40 -0800
commit2e6444cc55b54b59f781a14823e219d9a2413f72 (patch)
tree0affb83730db42e41df214e8095d1ff1dd02b162 /chiselFrontend
parent4512aeeacf594349cf9a816384e92bf3da63892f (diff)
Add Record as new superclass of Bundle (#366)
Record gives uses the power to create collections of heterogenous elements. Bundle is a special case of Record that uses reflection to populate the elements of the collection. Bundle also attempts to implement cloneType whereas users of Record are required to supply one.
Diffstat (limited to 'chiselFrontend')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala140
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala16
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Binding.scala6
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala4
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Module.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala12
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala2
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)