summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3/Data.scala
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/Data.scala
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/Data.scala')
-rw-r--r--core/src/main/scala/chisel3/Data.scala29
1 files changed, 24 insertions, 5 deletions
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index 983307a6..b7859e91 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -376,16 +376,16 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
// TODO Is this okay for sample_element? It *shouldn't* be visible to users
protected def bindingToString: String = topBindingOpt match {
case None => ""
- case Some(OpBinding(enclosure)) => s"(OpResult in ${enclosure.desiredName})"
- case Some(MemoryPortBinding(enclosure)) => s"(MemPort in ${enclosure.desiredName})"
+ case Some(OpBinding(enclosure, _)) => s"(OpResult in ${enclosure.desiredName})"
+ case Some(MemoryPortBinding(enclosure, _)) => s"(MemPort in ${enclosure.desiredName})"
case Some(PortBinding(enclosure)) if !enclosure.isClosed => s"(IO in unelaborated ${enclosure.desiredName})"
case Some(PortBinding(enclosure)) if enclosure.isClosed =>
DataMirror.fullModulePorts(enclosure).find(_._2 eq this) match {
case Some((name, _)) => s"(IO $name in ${enclosure.desiredName})"
case None => s"(IO (unknown) in ${enclosure.desiredName})"
}
- case Some(RegBinding(enclosure)) => s"(Reg in ${enclosure.desiredName})"
- case Some(WireBinding(enclosure)) => s"(Wire in ${enclosure.desiredName})"
+ case Some(RegBinding(enclosure, _)) => s"(Reg in ${enclosure.desiredName})"
+ case Some(WireBinding(enclosure, _)) => s"(Wire in ${enclosure.desiredName})"
case Some(DontCareBinding()) => s"(DontCare)"
case Some(ElementLitBinding(litArg)) => s"(unhandled literal)"
case Some(BundleLitBinding(litMap)) => s"(unhandled bundle literal)"
@@ -446,9 +446,23 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
*/
private[chisel3] def typeEquivalent(that: Data): Boolean
+ private def requireVisible(): Unit = {
+ val mod = topBindingOpt.flatMap(_.location)
+ topBindingOpt match {
+ case Some(tb: TopBinding) if (mod == Builder.currentModule) =>
+ case Some(pb: PortBinding) if (mod.flatMap(_._parent) == Builder.currentModule) =>
+ case _ =>
+ throwException(s"operand is not visible from the current module")
+ }
+ if (!MonoConnect.checkWhenVisibility(this)) {
+ throwException(s"operand has escaped the scope of the when in which it was constructed")
+ }
+ }
+
// Internal API: returns a ref that can be assigned to, if consistent with the binding
private[chisel3] def lref: Node = {
requireIsHardware(this)
+ requireVisible()
topBindingOpt match {
case Some(binding: ReadOnlyBinding) => throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding")
case Some(binding: TopBinding) => Node(this)
@@ -460,6 +474,11 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
// Internal API: returns a ref, if bound. Literals should override this as needed.
private[chisel3] def ref: Arg = {
requireIsHardware(this)
+ if (Builder.currentModule.isDefined) {
+ // This is allowed (among other cases) for evaluating args of Printf / Assert / Printable, which are
+ // partially resolved *after* elaboration completes. If this is resolved, the check should be unconditional.
+ requireVisible()
+ }
topBindingOpt match {
case Some(binding: LitBinding) => throwException(s"internal error: can't handle literal binding $binding")
case Some(binding: TopBinding) => Node(this)
@@ -595,7 +614,7 @@ trait WireFactory {
val x = t.cloneTypeFull
// Bind each element of x to being a Wire
- x.bind(WireBinding(Builder.forcedUserModule))
+ x.bind(WireBinding(Builder.forcedUserModule, Builder.currentWhen()))
pushCommand(DefWire(sourceInfo, x))
if (!compileOptions.explicitInvalidate) {