summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/internal
diff options
context:
space:
mode:
authorJack Koenig2021-08-12 17:04:11 -0700
committerGitHub2021-08-12 17:04:11 -0700
commit1ceb974c55c6785c21ab3934fa750ade0702e276 (patch)
treebc8559e8ef558e3216ecc5612593f5904f9a6b60 /core/src/main/scala/chisel3/internal
parent713de6b823d8707246935b9e31ed2fbafeaeca32 (diff)
Add DataView (#1955)
DataView is a mechanism for "viewing" Scala objects as a subtype of `Data`. Often, this is useful for viewing one subtype of `Data`, as another. One can think about a DataView as a cross between a customizable cast and an untagged union. A DataView has a Target type `T`, and a View type `V`. DataView requires that an implementation of `DataProduct` is available for Target types. DataProduct is a type class that provides a way to iterate on `Data` children of objects of implementing types. If a DataView is provided for a type T to a type V, then the function .viewAs[V] (of type T => V) is available. The object (of type T) returned by .viewAs is called a "View" and can be used as both an rvalue and an lvalue. Unlike when using an .asTypeOf cast, connecting to a "View" will connect to the associated field or fields of the underlying Target. DataView also enables .viewAsSupertype which is available for viewing Bundles as a parent Bundle type. It is similar to .viewAs but requires a prototype object of the Target type which will be cloned in order to create the returned View. .viewAsSupertype maps between the corresponding fields of the parent and child Bundle types.
Diffstat (limited to 'core/src/main/scala/chisel3/internal')
-rw-r--r--core/src/main/scala/chisel3/internal/BiConnect.scala6
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala9
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala66
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala6
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala7
5 files changed, 83 insertions, 11 deletions
diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala
index aa7d7ac3..4a9bb4f5 100644
--- a/core/src/main/scala/chisel3/internal/BiConnect.scala
+++ b/core/src/main/scala/chisel3/internal/BiConnect.scala
@@ -3,9 +3,11 @@
package chisel3.internal
import chisel3._
+import chisel3.experimental.dataview.reify
import chisel3.experimental.{Analog, BaseModule, attach}
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl.{Connect, DefInvalid}
+
import scala.language.experimental.macros
import chisel3.internal.sourceinfo._
@@ -225,8 +227,10 @@ private[chisel3] object BiConnect {
// This function checks if element-level connection operation allowed.
// Then it either issues it or throws the appropriate exception.
- def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: RawModule): Unit = {
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, _left: Element, _right: Element, context_mod: RawModule): Unit = {
import BindingDirection.{Internal, Input, Output} // Using extensively so import these
+ val left = reify(_left)
+ val right = reify(_right)
// If left or right have no location, assume in context module
// This can occur if one of them is a literal, unbound will error previously
val left_mod: BaseModule = left.topBinding.location.getOrElse(context_mod)
diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala
index 300803ce..6f4ab4b0 100644
--- a/core/src/main/scala/chisel3/internal/Binding.scala
+++ b/core/src/main/scala/chisel3/internal/Binding.scala
@@ -120,6 +120,15 @@ case class MemTypeBinding[T <: Data](parent: MemBase[T]) extends Binding {
// It is a source (RHS). It may only be connected/applied to sinks.
case class DontCareBinding() extends UnconstrainedBinding
+// Views currently only support 1:1 Element-level mappings
+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 target Optional Data this Aggregate views if the view is total and the target is a Data
+ */
+private[chisel3] case class AggregateViewBinding(childMap: Map[Data, Element], target: Option[Data]) extends UnconstrainedBinding
+
+
sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding
// Literal binding attached to a element that is not part of a Bundle.
case class ElementLitBinding(litArg: LitArg) extends LitBinding
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index fef12093..ddaedd2e 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -10,7 +10,8 @@ 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
+import _root_.firrtl.{AnnotationSeq, RenameMap}
+import chisel3.experimental.dataview.{reify, reifySingleData}
import chisel3.internal.Builder.Prefix
import logger.LazyLogging
@@ -215,32 +216,48 @@ private[chisel3] trait HasId extends InstanceId {
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) => _ref match {
- case Some(arg) => arg fullName c
- case None => computeName(None, None).get
- }
+ 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
}
@@ -288,8 +305,10 @@ private[chisel3] trait NamedComponent extends HasId {
final def toAbsoluteTarget: ReferenceTarget = {
val localTarget = toTarget
+ def makeTarget(p: BaseModule) = p.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component)
_parent match {
- case Some(parent) => parent.toAbsoluteTarget.ref(localTarget.ref).copy(component = localTarget.component)
+ case Some(ViewParent) => makeTarget(reifyParent)
+ case Some(parent) => makeTarget(parent)
case None => localTarget
}
}
@@ -304,6 +323,11 @@ private[chisel3] class ChiselContext() {
// 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) {
@@ -319,6 +343,9 @@ private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) {
*/
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
@@ -370,6 +397,9 @@ private[chisel3] object Builder extends LazyLogging {
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()
@@ -403,6 +433,7 @@ private[chisel3] object Builder extends LazyLogging {
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)
@@ -646,8 +677,29 @@ private[chisel3] object Builder extends LazyLogging {
}
}
+ // 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.warn("Elaborating design...")
val mod = f
@@ -655,7 +707,7 @@ private[chisel3] object Builder extends LazyLogging {
errors.checkpoint(logger)
logger.warn("Done elaborating.")
- (Circuit(components.last.name, components.toSeq, annotations.toSeq), mod)
+ (Circuit(components.last.name, components.toSeq, annotations.toSeq, makeViewRenameMap), mod)
}
}
initializeSingletons()
diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala
index b979ebae..5cbab329 100644
--- a/core/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -5,7 +5,9 @@ package chisel3.internal
import chisel3._
import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum}
import chisel3.internal.Builder.pushCommand
+import chisel3.experimental.dataview.reify
import chisel3.internal.firrtl.{Connect, DefInvalid}
+
import scala.language.experimental.macros
import chisel3.internal.sourceinfo.SourceInfo
@@ -188,8 +190,10 @@ private[chisel3] object MonoConnect {
// This function checks if element-level connection operation allowed.
// Then it either issues it or throws the appropriate exception.
- def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: RawModule): Unit = {
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, _sink: Element, _source: Element, context_mod: RawModule): Unit = {
import BindingDirection.{Internal, Input, Output} // Using extensively so import these
+ val sink = reify(_sink)
+ val source = reify(_source)
// If source has no location, assume in context module
// This can occur if is a literal, unbound will error previously
val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException)
diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
index 5796522c..f8a3cf7f 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -8,7 +8,8 @@ import chisel3.internal._
import chisel3.internal.sourceinfo.SourceInfo
import chisel3.experimental._
import _root_.firrtl.{ir => firrtlir}
-import _root_.firrtl.PrimOps
+import _root_.firrtl.{PrimOps, RenameMap}
+import _root_.firrtl.annotations.Annotation
import scala.collection.immutable.NumericRange
import scala.math.BigDecimal.RoundingMode
@@ -789,4 +790,6 @@ abstract class Component extends Arg {
case class DefModule(id: RawModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component
case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir: SpecifiedDirection, params: Map[String, Param]) extends Component
-case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation] = Seq.empty)
+case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation], renames: RenameMap) {
+ def firrtlAnnotations: Iterable[Annotation] = annotations.flatMap(_.toFirrtl.update(renames))
+}