summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/internal
diff options
context:
space:
mode:
authorAlbert Magyar2020-07-31 11:05:13 -0700
committerGitHub2020-07-31 18:05:13 +0000
commit5ecde24d390248722f8ab6ac790fbd1d453e898e (patch)
treee92d337431500ea06392acd0731f7c021662f6e6 /core/src/main/scala/chisel3/internal
parent8990ca3d8d8434a6c979b0c5fc06b05a39fd31d4 (diff)
Check whether signals escape their when scopes (#1518)
* Include and check when scoping as part of reg/mem/wire/node bindings * Allow outdated 'when' behavior of CHIRRTL memory ports with enables * Extend cross-module / when-visibility checks to all data refs * Fixes #1512 * Cannot be checked if outside a module context * E.g. delayed evaluation of printf / assert args * Add basic test cases for cross-module refs / signals escaping when scopes * Remove illegal cross-module references from existing tests
Diffstat (limited to 'core/src/main/scala/chisel3/internal')
-rw-r--r--core/src/main/scala/chisel3/internal/Binding.scala15
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala26
-rw-r--r--core/src/main/scala/chisel3/internal/MonoConnect.scala20
3 files changed, 52 insertions, 9 deletions
diff --git a/core/src/main/scala/chisel3/internal/Binding.scala b/core/src/main/scala/chisel3/internal/Binding.scala
index 07c44f9f..95bd40e5 100644
--- a/core/src/main/scala/chisel3/internal/Binding.scala
+++ b/core/src/main/scala/chisel3/internal/Binding.scala
@@ -88,13 +88,20 @@ sealed trait ConstrainedBinding extends TopBinding {
// A binding representing a data that cannot be (re)assigned to.
sealed trait ReadOnlyBinding extends TopBinding
+// A component that can potentially be declared inside a 'when'
+sealed trait ConditionalDeclarable extends TopBinding {
+ def visibility: Option[WhenContext]
+}
+
// TODO(twigg): Ops between unenclosed nodes can also be unenclosed
// However, Chisel currently binds all op results to a module
-case class OpBinding(enclosure: RawModule) extends ConstrainedBinding with ReadOnlyBinding
-case class MemoryPortBinding(enclosure: RawModule) extends ConstrainedBinding
+
case class PortBinding(enclosure: BaseModule) extends ConstrainedBinding
-case class RegBinding(enclosure: RawModule) extends ConstrainedBinding
-case class WireBinding(enclosure: RawModule) extends ConstrainedBinding
+
+case class OpBinding(enclosure: RawModule, visibility: Option[WhenContext]) extends ConstrainedBinding with ReadOnlyBinding with ConditionalDeclarable
+case class MemoryPortBinding(enclosure: RawModule, visibility: Option[WhenContext]) extends ConstrainedBinding with ConditionalDeclarable
+case class RegBinding(enclosure: RawModule, visibility: Option[WhenContext]) extends ConstrainedBinding with ConditionalDeclarable
+case class WireBinding(enclosure: RawModule, visibility: Option[WhenContext]) extends ConstrainedBinding with ConditionalDeclarable
case class ChildBinding(parent: Data) extends Binding {
def location: Option[BaseModule] = parent.topBinding.location
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index ceb14900..3c6d7290 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -332,7 +332,7 @@ private[chisel3] class DynamicContext() {
// 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
- var whenDepth: Int = 0 // Depth of when nesting
+ var whenStack: List[WhenContext] = Nil
var currentClock: Option[Clock] = None
var currentReset: Option[Reset] = None
val errors = new ErrorLog
@@ -461,10 +461,26 @@ private[chisel3] object Builder {
def readyForModuleConstr_=(target: Boolean): Unit = {
dynamicContext.readyForModuleConstr = target
}
- def whenDepth: Int = dynamicContext.whenDepth
- def whenDepth_=(target: Int): Unit = {
- dynamicContext.whenDepth = target
+
+ def whenDepth: Int = dynamicContext.whenStack.length
+
+ def pushWhen(wc: WhenContext): Unit = {
+ dynamicContext.whenStack = wc :: dynamicContext.whenStack
+ }
+
+ def popWhen(): WhenContext = {
+ val lastWhen = dynamicContext.whenStack.head
+ dynamicContext.whenStack = dynamicContext.whenStack.tail
+ lastWhen
}
+
+ def whenStack: List[WhenContext] = dynamicContext.whenStack
+ def whenStack_=(s: List[WhenContext]): Unit = {
+ dynamicContext.whenStack = s
+ }
+
+ def currentWhen(): Option[WhenContext] = dynamicContext.whenStack.headOption
+
def currentClock: Option[Clock] = dynamicContext.currentClock
def currentClock_=(newClock: Option[Clock]): Unit = {
dynamicContext.currentClock = newClock
@@ -490,7 +506,7 @@ private[chisel3] object Builder {
}
def pushOp[T <: Data](cmd: DefPrim[T]): T = {
// Bind each element of the returned Data to being a Op
- cmd.id.bind(OpBinding(forcedUserModule))
+ cmd.id.bind(OpBinding(forcedUserModule, currentWhen()))
pushCommand(cmd).id
}
diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala
index 24c0e229..dbc4f7f2 100644
--- a/core/src/main/scala/chisel3/internal/MonoConnect.scala
+++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala
@@ -40,6 +40,10 @@ private[chisel3] object MonoConnect {
MonoConnectException(": Source is unreadable from current module.")
def UnwritableSinkException =
MonoConnectException(": Sink is unwriteable by current module.")
+ def SourceEscapedWhenScopeException =
+ MonoConnectException(": Source has escaped the scope of the when in which it was constructed.")
+ def SinkEscapedWhenScopeException =
+ MonoConnectException(": Sink has escaped the scope of the when in which it was constructed.")
def UnknownRelationException =
MonoConnectException(": Sink or source unavailable to current module.")
// These are when recursing down aggregate types
@@ -58,6 +62,14 @@ private[chisel3] object MonoConnect {
def AnalogMonoConnectionException =
MonoConnectException(": Analog cannot participate in a mono connection (source and sink)")
+ def checkWhenVisibility(x: Data): Boolean = {
+ x.topBinding match {
+ case mp: MemoryPortBinding => true // TODO (albert-magyar): remove this "bridge" for odd enable logic of current CHIRRTL memories
+ case cd: ConditionalDeclarable => cd.visibility.map(_.active()).getOrElse(true)
+ case _ => true
+ }
+ }
+
/** This function is what recursively tries to connect a sink and source together
*
* There is some cleverness in the use of internal try-catch to catch exceptions
@@ -184,6 +196,14 @@ private[chisel3] object MonoConnect {
val sink_direction = BindingDirection.from(sink.topBinding, sink.direction)
val source_direction = BindingDirection.from(source.topBinding, source.direction)
+ if (!checkWhenVisibility(sink)) {
+ throw SinkEscapedWhenScopeException
+ }
+
+ if (!checkWhenVisibility(source)) {
+ throw SourceEscapedWhenScopeException
+ }
+
// CASE: Context is same module that both left node and right node are in
if( (context_mod == sink_mod) && (context_mod == source_mod) ) {
((sink_direction, source_direction): @unchecked) match {