From 30e8eb552a29b22bc23faa06f5661da6129188b2 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 23 Nov 2017 06:09:59 -0800 Subject: [PATCH] Change switch to emit when, elsewhen, instead of when, when (#720) Also enforce switch is conditions are mutually exclusive literals --- src/main/scala/chisel3/util/Conditional.scala | 44 ++++++++++++------- src/test/scala/chiselTests/SwitchSpec.scala | 35 +++++++++++++++ 2 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/test/scala/chiselTests/SwitchSpec.scala diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala index 5830e014a09..3ebda69bbe5 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 00000000000..2cfe16d285e --- /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 } + } + }) + } + } +}