Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add when.cond for getting the current when condition #1694

Merged
merged 2 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 52 additions & 8 deletions core/src/main/scala/chisel3/When.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
package chisel3

import scala.language.experimental.macros

import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{SourceInfo}
import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo}

object when {
/** Create a `when` condition block, where whether a block of logic is
Expand All @@ -29,7 +28,30 @@ object when {
*/

def apply(cond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = {
new WhenContext(sourceInfo, Some(() => cond), block)
new WhenContext(sourceInfo, Some(() => cond), block, 0, Nil)
}

/** Returns the current `when` condition
*
* This is the conjunction of conditions for all `whens` going up the call stack
* {{{
* when (a) {
* when (b) {
* when (c) {
* }.otherwise {
* when.cond // this is equal to: a && b && !c
* }
* }
* }
* }}}
* */
def cond: Bool = {
implicit val compileOptions = ExplicitCompileOptions.Strict
implicit val sourceInfo = UnlocatableSourceInfo
val whens = Builder.whenStack
whens.foldRight(true.B) {
case (ctx, acc) => acc && ctx.localCond()
}
}
Comment on lines +48 to 55
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get a source locator from the when conditions themselves?

}

Expand All @@ -43,18 +65,40 @@ object when {
* succeeding elsewhen or otherwise; therefore, this information is
* added by preprocessing the command queue.
*/
final class WhenContext(sourceInfo: SourceInfo, cond: Option[() => Bool], block: => Any, firrtlDepth: Int = 0) {
final class WhenContext private[chisel3] (
sourceInfo: SourceInfo,
cond: Option[() => Bool],
block: => Any,
firrtlDepth: Int,
// For capturing conditions from prior whens or elsewhens
altConds: List[() => Bool]
) {

@deprecated("Use when(...) { ... }, this should never have been public", "Chisel 3.4.2")
def this(sourceInfo: SourceInfo, cond: Option[() => Bool], block: => Any, firrtlDepth: Int = 0) =
this(sourceInfo, cond, block, firrtlDepth, Nil)

private var scopeOpen = false

/** Returns the local condition, inverted for an otherwise */
private[chisel3] def localCond(): Bool = {
implicit val compileOptions = ExplicitCompileOptions.Strict
implicit val sourceInfo = UnlocatableSourceInfo
val alt = altConds.foldRight(true.B) {
case (c, acc) => acc & !c()
}
cond.map(alt && _())
.getOrElse(alt)
}

/** This block of logic gets executed if above conditions have been
* false and this condition is true. The lazy argument pattern
* makes it possible to delay evaluation of cond, emitting the
* declaration and assignment of the Bool node of the predicate in
* the correct place.
*/
def elsewhen (elseCond: => Bool)(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): WhenContext = {
new WhenContext(sourceInfo, Some(() => elseCond), block, firrtlDepth + 1)
new WhenContext(sourceInfo, Some(() => elseCond), block, firrtlDepth + 1, cond ++: altConds)
}

/** This block of logic gets executed only if the above conditions
Expand All @@ -65,7 +109,7 @@ final class WhenContext(sourceInfo: SourceInfo, cond: Option[() => Bool], block:
* place.
*/
def otherwise(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit =
new WhenContext(sourceInfo, None, block, firrtlDepth + 1)
new WhenContext(sourceInfo, None, block, firrtlDepth + 1, cond ++: altConds)

def active(): Boolean = scopeOpen

Expand All @@ -79,13 +123,13 @@ final class WhenContext(sourceInfo: SourceInfo, cond: Option[() => Bool], block:
scopeOpen = true
block
} catch {
case ret: scala.runtime.NonLocalReturnControl[_] =>
case _: scala.runtime.NonLocalReturnControl[_] =>
throwException("Cannot exit from a when() block with a \"return\"!" +
" Perhaps you meant to use Mux or a Wire as a return value?"
)
}
scopeOpen = false
Builder.popWhen()
cond.foreach( c => pushCommand(WhenEnd(sourceInfo,firrtlDepth)) )
cond.foreach(_ => pushCommand(WhenEnd(sourceInfo,firrtlDepth)))
if (cond.isEmpty) { pushCommand(OtherwiseEnd(sourceInfo,firrtlDepth)) }
}
39 changes: 39 additions & 0 deletions src/test/scala/chiselTests/When.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,42 @@ class SubmoduleWhenTester extends BasicTester {
}
}

class WhenCondTester extends BasicTester {
val pred = Wire(Vec(4, Bool()))
val (cycle, done) = Counter(true.B, 1 << pred.size)
// Cycle through every predicate
pred := cycle.asBools
val Seq(a, b, c, d) = pred // Just for nicer accessors
// When want the when predicates on connection to optimize away,
// it's not necessary but it makes the Verilog prettier
val w1, w2, w3, w4, w5, w6, w7 = WireInit(Bool(), DontCare)
when (a) {
w1 := when.cond
when (b) {
w2 := when.cond
}.elsewhen (c) {
w3 := when.cond
}.elsewhen (d) {
w4 := when.cond
}.otherwise {
w5 := when.cond
}
}.otherwise {
w6 := when.cond
}
w7 := when.cond

assert(w1 === a)
assert(w2 === (a && b))
assert(w3 === (a && !b && c))
assert(w4 === (a && !b && !c && d))
assert(w5 === (a && !b && !c && !d))
assert(w6 === !a)
assert(w7)

when (done) { stop() }
}

class WhenSpec extends ChiselFlatSpec with Utils {
"When, elsewhen, and otherwise with orthogonal conditions" should "work" in {
assertTesterPasses{ new WhenTester }
Expand All @@ -105,6 +141,9 @@ class WhenSpec extends ChiselFlatSpec with Utils {
"Conditional connections to submodule ports" should "be handled properly" in {
assertTesterPasses(new SubmoduleWhenTester)
}
"when.cond" should "give the current when condition" in {
assertTesterPasses(new WhenCondTester)
}

"Returning in a when scope" should "give a reasonable error message" in {
val e = the [ChiselException] thrownBy extractCause[ChiselException] {
Expand Down