summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
authormergify[bot]2022-04-12 00:09:55 +0000
committerGitHub2022-04-12 00:09:55 +0000
commit898142ba05b04fb1602b249fd1ae81baa3f47f89 (patch)
tree75304868c8e8a43abc79a5e125c51167fccce6b4 /core/src/main
parentd766e8f7270579406d54abc9015d494cd199c6ce (diff)
Enhance views to [sometimes] support dynamic indexing and implement FlatIO (backport #2476) (#2479)
* Capture 1:1 mappings of Aggregates inside of views This is implemented by including any corresponding Aggregates from the DataView.mapping in the AggregateViewBinding.childMap (which is now of type Map[Data, Data]). This enables dynamically indexing Vecs that are themselves elements of larger Aggregates in views when the corresponding element of the view is a Vec of the same type. It also increases the number of cases where a single Target can represent part of a view. (cherry picked from commit 1f6b1ca14ccf86918065073c3f6f3626dd83a68e) * Add FlatIO API for creating ports from Bundles without a prefix (cherry picked from commit 772a3a1fe3b9372b7c2d7cd2d424b2adcd633cdb) * [docs] Add FlatIO to the general cookbook (cherry picked from commit b4159641350f238f0f899b69954142ce8ee11544) Co-authored-by: Jack Koenig <koenig@sifive.com>
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/scala/chisel3/Data.scala2
-rw-r--r--core/src/main/scala/chisel3/Element.scala11
-rw-r--r--core/src/main/scala/chisel3/experimental/dataview/package.scala55
-rw-r--r--core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala16
-rw-r--r--core/src/main/scala/chisel3/experimental/package.scala28
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala11
6 files changed, 82 insertions, 41 deletions
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index ef43a3b0..f468335e 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -667,7 +667,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
topBindingOpt match {
// DataView
case Some(ViewBinding(target)) => reify(target).ref
- case Some(AggregateViewBinding(viewMap, _)) =>
+ case Some(AggregateViewBinding(viewMap)) =>
viewMap.get(this) match {
case None => materializeWire() // FIXME FIRRTL doesn't have Aggregate Init expressions
// This should not be possible because Element does the lookup in .topBindingOpt
diff --git a/core/src/main/scala/chisel3/Element.scala b/core/src/main/scala/chisel3/Element.scala
index 401f2bdf..39b7689c 100644
--- a/core/src/main/scala/chisel3/Element.scala
+++ b/core/src/main/scala/chisel3/Element.scala
@@ -36,10 +36,15 @@ abstract class Element extends Data {
case Some(litArg) => Some(ElementLitBinding(litArg))
case _ => Some(DontCareBinding())
}
- case Some(b @ AggregateViewBinding(viewMap, _)) =>
+ // TODO Do we even need this? Looking up things in the AggregateViewBinding is fine
+ case Some(b @ AggregateViewBinding(viewMap)) =>
viewMap.get(this) match {
- case Some(elt) => Some(ViewBinding(elt))
- case _ => throwException(s"Internal Error! $this missing from topBinding $b")
+ case Some(elt: Element) => Some(ViewBinding(elt))
+ // TODO We could generate a reduced AggregateViewBinding, but is there a point?
+ // Generating the new object would be somewhat slow, it's not clear if we should do this
+ // matching anyway
+ case Some(data: Aggregate) => Some(b)
+ case _ => throwException(s"Internal Error! $this missing from topBinding $b")
}
case topBindingOpt => topBindingOpt
}
diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala
index 891ecb81..c583c516 100644
--- a/core/src/main/scala/chisel3/experimental/dataview/package.scala
+++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala
@@ -97,11 +97,16 @@ package object dataview {
val targetContains: Data => Boolean = implicitly[DataProduct[T]].dataSet(target)
// Resulting bindings for each Element of the View
- val childBindings =
+ // Kept separate from Aggregates for totality checking
+ val elementBindings =
new mutable.HashMap[Data, mutable.ListBuffer[Element]] ++
viewFieldLookup.view.collect { case (elt: Element, _) => elt }
.map(_ -> new mutable.ListBuffer[Element])
+ // Record any Aggregates that correspond 1:1 for reification
+ // Using Data instead of Aggregate to avoid unnecessary checks
+ val aggregateMappings = mutable.ArrayBuffer.empty[(Data, Data)]
+
def viewFieldName(d: Data): String =
viewFieldLookup.get(d).map(_ + " ").getOrElse("") + d.toString
@@ -130,7 +135,7 @@ package object dataview {
s"View field $fieldName has width ${vwidth} that is incompatible with target value $tex's width ${twidth}"
)
}
- childBindings(vex) += tex
+ elementBindings(vex) += tex
}
mapping.foreach {
@@ -145,7 +150,7 @@ package object dataview {
}
getMatchedFields(aa, ba).foreach {
case (aelt: Element, belt: Element) => onElt(aelt, belt)
- case _ => // Ignore matching of Aggregates
+ case (t, v) => aggregateMappings += (v -> t)
}
}
@@ -155,7 +160,7 @@ package object dataview {
val targetSeen: Option[mutable.Set[Data]] = if (total) Some(mutable.Set.empty[Data]) else None
- val resultBindings = childBindings.map {
+ val elementResult = elementBindings.map {
case (data, targets) =>
val targetsx = targets match {
case collection.Seq(target: Element) => target
@@ -183,23 +188,25 @@ package object dataview {
}
view match {
- case elt: Element => view.bind(ViewBinding(resultBindings(elt)))
+ case elt: Element => view.bind(ViewBinding(elementResult(elt)))
case agg: Aggregate =>
- // We record total Data mappings to provide a better .toTarget
- val topt = target match {
- case d: Data if total => Some(d)
+ // Don't forget the potential mapping of the view to the target!
+ target match {
+ case d: Data if total =>
+ aggregateMappings += (agg -> d)
case _ =>
- // Record views that don't have the simpler .toTarget for later renaming
- Builder.unnamedViews += view
- None
}
- // TODO We must also record children as unnamed, some could be namable but this requires changes to the Binding
+
+ val fullResult = elementResult ++ aggregateMappings
+
+ // We need to record any Aggregates that don't have a 1-1 mapping (including the view
+ // itself)
getRecursiveFields.lazily(view, "_").foreach {
- case (agg: Aggregate, _) if agg != view =>
- Builder.unnamedViews += agg
+ case (unnamed: Aggregate, _) if !fullResult.contains(unnamed) =>
+ Builder.unnamedViews += unnamed
case _ => // Do nothing
}
- agg.bind(AggregateViewBinding(resultBindings, topt))
+ agg.bind(AggregateViewBinding(fullResult))
}
}
@@ -208,8 +215,8 @@ package object dataview {
private def unfoldView(elt: Element): LazyList[Element] = {
def rec(e: Element): LazyList[Element] = e.topBindingOpt match {
case Some(ViewBinding(target)) => target #:: rec(target)
- case Some(AggregateViewBinding(mapping, _)) =>
- val target = mapping(e)
+ case Some(avb: AggregateViewBinding) =>
+ val target = avb.lookup(e).get
target #:: rec(target)
case Some(_) | None => LazyList.empty
}
@@ -246,15 +253,11 @@ package object dataview {
*/
private[chisel3] def reifySingleData(data: Data): Option[Data] = {
val candidate: Option[Data] =
- data.binding.collect { // First check if this is a total mapping of an Aggregate
- case AggregateViewBinding(_, Some(t)) => t
- }.orElse { // Otherwise look via top binding
- data.topBindingOpt match {
- case None => None
- case Some(ViewBinding(target)) => Some(target)
- case Some(AggregateViewBinding(lookup, _)) => lookup.get(data)
- case Some(_) => None
- }
+ data.topBindingOpt match {
+ case None => None
+ case Some(ViewBinding(target)) => Some(target)
+ case Some(AggregateViewBinding(lookup)) => lookup.get(data)
+ case Some(_) => None
}
candidate.flatMap { d =>
// Candidate may itself be a view, keep tracing in those cases
diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
index 60290f83..46a38e7c 100644
--- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
+++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala
@@ -173,12 +173,12 @@ object Lookupable {
// We have to lookup the target(s) of the view since they may need to be underlying into the current context
val newBinding = data.topBinding match {
case ViewBinding(target) => ViewBinding(lookupData(reify(target)))
- case avb @ AggregateViewBinding(map, targetOpt) =>
+ case avb @ AggregateViewBinding(map) =>
data match {
- case _: Element => ViewBinding(lookupData(reify(map(data))))
+ case e: Element => ViewBinding(lookupData(reify(avb.lookup(e).get)))
case _: Aggregate =>
// Provide a 1:1 mapping if possible
- val singleTargetOpt = targetOpt.filter(_ => avb == data.binding.get).flatMap(reifySingleData)
+ val singleTargetOpt = map.get(data).filter(_ => avb == data.binding.get).flatMap(reifySingleData)
singleTargetOpt match {
case Some(singleTarget) => // It is 1:1!
// This is a little tricky because the values in newMap need to point to Elements of newTarget
@@ -187,15 +187,15 @@ object Lookupable {
case (res, from) =>
(res: Data) -> mapRootAndExtractSubField(map(from), _ => newTarget)
}.toMap
- AggregateViewBinding(newMap, Some(newTarget))
+ AggregateViewBinding(newMap + (result -> newTarget))
case None => // No 1:1 mapping so we have to do a flat binding
// Just remap each Element of this aggregate
val newMap = coiterate(result, data).map {
// Upcast res to Data since Maps are invariant in the Key type parameter
- case (res, from) => (res: Data) -> lookupData(reify(map(from)))
+ case (res, from) => (res: Data) -> lookupData(reify(avb.lookup(from).get))
}.toMap
- AggregateViewBinding(newMap, None)
+ AggregateViewBinding(newMap)
}
}
}
@@ -204,8 +204,8 @@ object Lookupable {
// We must also mark non-1:1 and child Aggregates in the view for renaming
newBinding match {
case _: ViewBinding => // Do nothing
- case AggregateViewBinding(_, target) =>
- if (target.isEmpty) {
+ case AggregateViewBinding(childMap) =>
+ if (!childMap.contains(result)) {
Builder.unnamedViews += result
}
// Binding does not capture 1:1 for child aggregates views
diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala
index ce258a25..9b9c83f4 100644
--- a/core/src/main/scala/chisel3/experimental/package.scala
+++ b/core/src/main/scala/chisel3/experimental/package.scala
@@ -57,6 +57,34 @@ package object experimental {
type Direction = ActualDirection
val Direction = ActualDirection
+ /** The same as [[IO]] except there is no prefix for the name of the val */
+ def FlatIO[T <: Record](gen: => T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = noPrefix {
+ import dataview._
+ def coerceDirection(d: Data) = {
+ import chisel3.{SpecifiedDirection => SD}
+ DataMirror.specifiedDirectionOf(gen) match {
+ case SD.Flip => Flipped(d)
+ case SD.Input => Input(d)
+ case SD.Output => Output(d)
+ case _ => d
+ }
+ }
+ val ports: Seq[Data] =
+ gen.elements.toSeq.reverse.map {
+ case (name, data) =>
+ val p = IO(coerceDirection(chiselTypeClone(data).asInstanceOf[Data]))
+ p.suggestName(name)
+ p
+
+ }
+
+ implicit val dv: DataView[Seq[Data], T] = DataView.mapping(
+ _ => chiselTypeClone(gen).asInstanceOf[T],
+ (seq, rec) => seq.zip(rec.elements.toSeq.reverse).map { case (port, (_, field)) => port -> field }
+ )
+ ports.viewAs[T]
+ }
+
implicit class ChiselRange(val sc: StringContext) extends AnyVal {
import scala.language.experimental.macros
diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala
index 6431dd23..bab79bc1 100644
--- a/core/src/main/scala/chisel3/internal/Binding.scala
+++ b/core/src/main/scala/chisel3/internal/Binding.scala
@@ -141,11 +141,16 @@ case class DontCareBinding() extends UnconstrainedBinding
private[chisel3] case class ViewBinding(target: Element) extends UnconstrainedBinding
/** Binding for Aggregate Views
- * @param childMap Mapping from children of this view to each child's target
+ * @param childMap Mapping from children of this view to their respective targets
* @param target Optional Data this Aggregate views if the view is total and the target is a Data
+ * @note For any Elements in the childMap, both key and value must be Elements
+ * @note The types of key and value need not match for the top Data in a total view of type
+ * Aggregate
*/
-private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Element], target: Option[Data])
- extends UnconstrainedBinding
+private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Data]) extends UnconstrainedBinding {
+ // Helper lookup function since types of Elements always match
+ def lookup(key: Element): Option[Element] = childMap.get(key).map(_.asInstanceOf[Element])
+}
/** Binding for Data's returned from accessing an Instance/Definition members, if not readable/writable port */
private[chisel3] case object CrossModuleBinding extends TopBinding {