Skip to content

Commit

Permalink
Merge branch 'master' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
ucbjrl committed Nov 24, 2017
2 parents 34a5ed1 + 30e8eb5 commit 78bfa07
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 16 deletions.
44 changes: 28 additions & 16 deletions src/main/scala/chisel3/util/Conditional.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,35 @@ object unless { // scalastyle:ignore object.name
/** Implementation details for [[switch]]. See [[switch]] and [[chisel3.util.is is]] for the
* user-facing API.
*/
class SwitchContext[T <: Bits](cond: T) {
def is(v: Iterable[T])(block: => Unit) {
class SwitchContext[T <: Bits](cond: T, whenContext: Option[WhenContext], lits: Set[BigInt]) {
def is(v: Iterable[T])(block: => Unit): SwitchContext[T] = {
if (!v.isEmpty) {
when (v.map(_.asUInt === cond.asUInt).reduce(_||_)) {
block
val newLits = v.map { w =>
require(w.isLit, "is conditions must be literals!")
val value = w.litValue
require(!lits.contains(value), "all is conditions must be mutually exclusive!")
value
}
// def instead of val so that logic ends up in legal place
def p = v.map(_.asUInt === cond.asUInt).reduce(_||_)
whenContext match {
case Some(w) => new SwitchContext(cond, Some(w.elsewhen(p)(block)), lits ++ newLits)
case None => new SwitchContext(cond, Some(when(p)(block)), lits ++ newLits)
}
} else {
this
}
}
def is(v: T)(block: => Unit) { is(Seq(v))(block) }
def is(v: T, vr: T*)(block: => Unit) { is(v :: vr.toList)(block) }
def is(v: T)(block: => Unit): SwitchContext[T] = is(Seq(v))(block)
def is(v: T, vr: T*)(block: => Unit): SwitchContext[T] = is(v :: vr.toList)(block)
}

/** Use to specify cases in a [[switch]] block, equivalent to a [[when$ when]] block comparing to
* the condition variable.
*
* @note illegal outside a [[switch]] block
* @note multiple conditions may fire simultaneously
* @note must be a literal
* @note each is must be mutually exclusive
* @note dummy implementation, a macro inside [[switch]] transforms this into the actual
* implementation
*/
Expand Down Expand Up @@ -80,15 +92,15 @@ object is { // scalastyle:ignore object.name
object switch { // scalastyle:ignore object.name
def apply[T <: Bits](cond: T)(x: => Unit): Unit = macro impl
def impl(c: Context)(cond: c.Tree)(x: c.Tree): c.Tree = { import c.universe._
val sc = c.universe.internal.reificationSupport.freshTermName("sc")
def extractIsStatement(tree: Tree): List[c.universe.Tree] = tree match {
// TODO: remove when Chisel compatibility package is removed
case q"Chisel.`package`.is.apply( ..$params )( ..$body )" => List(q"$sc.is( ..$params )( ..$body )")
case q"chisel3.util.is.apply( ..$params )( ..$body )" => List(q"$sc.is( ..$params )( ..$body )")
case b => throw new Exception(s"Cannot include blocks that do not begin with is() in switch.")
}
val q"..$body" = x
val ises = body.flatMap(extractIsStatement(_))
q"""{ val $sc = new SwitchContext($cond); ..$ises }"""
val res = body.foldLeft(q"""new SwitchContext($cond, None, Set.empty)""") {
case (acc, tree) => tree match {
// TODO: remove when Chisel compatibility package is removed
case q"Chisel.`package`.is.apply( ..$params )( ..$body )" => q"$acc.is( ..$params )( ..$body )"
case q"chisel3.util.is.apply( ..$params )( ..$body )" => q"$acc.is( ..$params )( ..$body )"
case b => throw new Exception(s"Cannot include blocks that do not begin with is() in switch.")
}
}
q"""{ $res }"""
}
}
35 changes: 35 additions & 0 deletions src/test/scala/chiselTests/SwitchSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// See LICENSE for license details.

package chiselTests

import chisel3._
import chisel3.util._
import chisel3.testers.BasicTester

class SwitchSpec extends ChiselFlatSpec {
"switch" should "require literal conditions" in {
a [java.lang.IllegalArgumentException] should be thrownBy {
elaborate(new Module {
val io = IO(new Bundle {})
val state = RegInit(0.U)
val wire = WireInit(0.U)
switch (state) {
is (wire) { state := 1.U }
}
})
}
}
it should "require mutually exclusive conditions" in {
a [java.lang.IllegalArgumentException] should be thrownBy {
elaborate(new Module {
val io = IO(new Bundle {})
val state = RegInit(0.U)
switch (state) {
is (0.U) { state := 1.U }
is (1.U) { state := 2.U }
is (0.U) { state := 3.U }
}
})
}
}
}

0 comments on commit 78bfa07

Please sign in to comment.