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/internal | |
| 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/internal')
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Binding.scala | 15 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Builder.scala | 26 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/MonoConnect.scala | 20 |
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 { |
