0%

3.7 Type

3.7 Type

Scala vs. Chisel Type

  • Int vs. UInt
1
2
3
val a = Wire(UInt(4.W))
a := 0.U // legal: chisel UInt
a := 0 // illegal: scala Int
  • Boolean vs. Bool
1
2
3
4
5
6
7
8
val bool = Wire(Bool())
val boolean: Boolean = false
// legal
when (bool) { ... }
if (boolean) { ... }
// illegal
if (bool) { ... }
when (boolean) { ... }

Type Coercion

Scala

x.asInstanceOf[T] casts the object x to the type T. It throws an exception if the given object cannot be cast to type T.

1
2
3
4
5
6
7
8
9
val x: UInt = 3.U
// We can't cast UInt to Int
try {
println(x.asInstanceOf[Int])
} catch {
case e: java.lang.ClassCastException => println("As expected, we can't cast UInt to Int")
}
// But we can cast UInt to Data since UInt inherits from Data.
println(x.asInstanceOf[Data])

Chisel

Chisel has a set of type casting functions. The most general is asTypeOf(). Some chisel objects also define asUInt() and asSInt() as well as some others.

1
2
3
4
5
6
7
class TypeConvertDemo extends Module {
val io = IO(new Bundle {
val in = Input(UInt(4.W))
val out = Output(SInt(4.W))
})
io.out := io.in.asTypeOf(io.out)
}

Match

Type Match

1
2
3
4
5
6
7
8
9
10
class ConstantSum(in1: Data, in2: Data) extends Module {
val io = IO(new Bundle {
val out = Output(chiselTypeOf(in1)) // in case in1 is literal then just get its type
})
(in1, in2) match {
case (x: UInt, y: UInt) => io.out := x + y
case (x: SInt, y: SInt) => io.out := x + y
case _ => throw new Exception("I give up!")
}
}

Value Match

It is good to remember that Chisel types generally should not be value matched. Scala’s match executes during circuit elaboration, but what you probably want is a post-elaboration comparison. The following gives a syntax error:

1
2
3
4
5
6
7
8
9
10
11
class InputIsZero extends Module {
val io = IO(new Bundle {
val in = Input(UInt(16.W))
val out = Output(Bool())
})
io.out := (io.in match {
// note that case 0.U is an error
case (0.U) => true.B
case _ => false.B
})
}

Unapply

value matching with case classes

1
2
3
4
5
6
case class Something(a: String, b: Int)
val a = Something("A", 3)
a match {
case Something("A", value) => value
case Something(str, 3) => 0
}

Scala unapply methods are another form of syntactic sugar that give match statements the ability to both match on types and extract values from those types during the matching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case class SomeGeneratorParameters(
someWidth: Int,
someOtherWidth: Int = 10,
pipelineMe: Boolean = false
) {
require(someWidth >= 0)
require(someOtherWidth >= 0)
val totalWidth = someWidth + someOtherWidth
}

def delay(p: SomeGeneratorParameters): Int = p match {
case sg @ SomeGeneratorParameters(_, _, true) => sg.totalWidth * 3
case SomeGeneratorParameters(_, sw, false) => sw * 2
case sg @ SomeGeneratorParameters(_, _, true) if sg.pipelineMe => sg.totalWidth * 3
}

All these syntaxes are enabled by a Scala unapply method contained in a class’s companion object. If you want to unapply a class but do not want to make it a case class, you can manually implement the unapply method. The following example demonstrates how one can manually implement a class’s apply and unapply methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Boat(val name: String, val length: Int)
object Boat {
def unapply(b: Boat): Option[(String, Int)] = Some((b.name, b.length))
def apply(name: String, length: Int): Boat = new Boat(name, length)
}

def getSmallBoats(seq: Seq[Boat]): Seq[Boat] = seq.filter { b =>
b match {
case Boat(_, length) if length < 60 => true
case Boat(_, _) => false
}
}

val boats = Seq(Boat("Santa Maria", 62), Boat("Pinta", 56), Boat("Nina", 50))
println(getSmallBoats(boats).map(_.name).mkString(" and ") + " are small boats!")

Partial Functions