summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util/Conditional.scala
blob: 5830e014a0982b2b7af11c67fed6a20ed7316715 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// See LICENSE for license details.

/** Conditional blocks.
  */

package chisel3.util

import scala.language.reflectiveCalls
import scala.language.experimental.macros
import scala.reflect.runtime.universe._
import scala.reflect.macros.blackbox._

import chisel3._

object unless {  // scalastyle:ignore object.name
  /** Does the same thing as [[when$ when]], but with the condition inverted.
    */
  def apply(c: Bool)(block: => Unit) {
    when (!c) { block }
  }
}

/** 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) {
    if (!v.isEmpty) {
      when (v.map(_.asUInt === cond.asUInt).reduce(_||_)) {
        block
      }
    }
  }
  def is(v: T)(block: => Unit) { is(Seq(v))(block) }
  def is(v: T, vr: T*)(block: => Unit) { 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 dummy implementation, a macro inside [[switch]] transforms this into the actual
  * implementation
  */
object is {   // scalastyle:ignore object.name
  // TODO: Begin deprecation of non-type-parameterized is statements.
  /** Executes `block` if the switch condition is equal to any of the values in `v`.
    */
  def apply(v: Iterable[Bits])(block: => Unit) {
    require(false, "The 'is' keyword may not be used outside of a switch.")
  }

  /** Executes `block` if the switch condition is equal to `v`.
    */
  def apply(v: Bits)(block: => Unit) {
    require(false, "The 'is' keyword may not be used outside of a switch.")
  }

  /** Executes `block` if the switch condition is equal to any of the values in the argument list.
    */
  def apply(v: Bits, vr: Bits*)(block: => Unit) {
    require(false, "The 'is' keyword may not be used outside of a switch.")
  }
}

/** Conditional logic to form a switch block. See [[is$ is]] for the case API.
  *
  * @example {{{
  * switch (myState) {
  *   is (state1) {
  *     // some logic here that runs when myState === state1
  *   }
  *   is (state2) {
  *     // some logic here that runs when myState === state2
  *   }
  * }
  * }}}
  */
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 }"""
  }
}