diff options
| author | Albert Magyar | 2020-07-31 11:05:13 -0700 |
|---|---|---|
| committer | GitHub | 2020-07-31 18:05:13 +0000 |
| commit | 5ecde24d390248722f8ab6ac790fbd1d453e898e (patch) | |
| tree | e92d337431500ea06392acd0731f7c021662f6e6 /core/src/main/scala/chisel3/Data.scala | |
| parent | 8990ca3d8d8434a6c979b0c5fc06b05a39fd31d4 (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.scala | 29 |
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) { |
