summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Koenig2017-11-23 06:09:59 -0800
committerGitHub2017-11-23 06:09:59 -0800
commit30e8eb552a29b22bc23faa06f5661da6129188b2 (patch)
tree790f0d895eb47945295bc410599e7c84f281d813
parent6f58883198c083cdd79fee2e3d30f777b61dfe80 (diff)
Change switch to emit when, elsewhen, instead of when, when (#720)
Also enforce switch is conditions are mutually exclusive literals
-rw-r--r--src/main/scala/chisel3/util/Conditional.scala44
-rw-r--r--src/test/scala/chiselTests/SwitchSpec.scala35
2 files changed, 63 insertions, 16 deletions
diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala
index 5830e014..3ebda69b 100644
--- a/src/main/scala/chisel3/util/Conditional.scala
+++ b/src/main/scala/chisel3/util/Conditional.scala
@@ -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
*/
@@ -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 }"""
}
}
diff --git a/src/test/scala/chiselTests/SwitchSpec.scala b/src/test/scala/chiselTests/SwitchSpec.scala
new file mode 100644
index 00000000..2cfe16d2
--- /dev/null
+++ b/src/test/scala/chiselTests/SwitchSpec.scala
@@ -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 }
+ }
+ })
+ }
+ }
+}