Chisel Type vs Scala Type

The Scala compiler cannot distinguish between Chisel’s representation of hardware such as false.B, Reg(Bool()) and pure Chisel types (e.g. Bool()). You can get runtime errors passing a Chisel type when hardware is expected, and vice versa.

Scala Type vs Chisel Type vs Hardware

The Scala type of the Data is recognized by the Scala compiler, such as Bool, Decoupled[UInt] or MyBundle in

class MyBundle(w: Int) extends Bundle {
  val foo = UInt(w.W)
  val bar = UInt(w.W)
}

The Chisel type of a Data is a Scala object. It captures all the fields actually present, by names, and their types including widths. For example, MyBundle(3) creates a Chisel Type with fields foo: UInt(3.W), bar: UInt(3.W)).

Hardware is Data that is “bound” to synthesizable hardware. For example false.B or Reg(Bool()). The binding is what determines the actual directionality of each field, it is not a property of the Chisel type.

A literal is a Data that is respresentable as a literal value without being wrapped in Wire, Reg, or IO.

Chisel Type vs Hardware vs Literals

The below code demonstrates how objects with the same Scala type (MyBundle) can have different properties.

import chisel3.experimental.BundleLiterals._

class MyModule(gen: () => MyBundle) extends Module {
                                                            //   Hardware   Literal
    val xType:    MyBundle     = new MyBundle(3)            //      -          -
    val dirXType: MyBundle     = Input(new MyBundle(3))     //      -          -
    val xReg:     MyBundle     = Reg(new MyBundle(3))       //      x          -
    val xIO:      MyBundle     = IO(Input(new MyBundle(3))) //      x          -
    val xRegInit: MyBundle     = RegInit(xIO)               //      x          -
    val xLit:     MyBundle     = xType.Lit(                 //      x          x 
      _.foo -> 0.U(3.W), 
      _.bar -> 0.U(3.W)
    )
    val y:        MyBundle = gen()                          //      ?          ?
    
    // Need to initialize all hardware values
    xReg := DontCare
}

Chisel Type vs Hardware – Specific Functions and Errors

.asTypeOf works for both hardware and Chisel type:

ChiselStage.elaborate(new Module {
  val chiselType = new MyBundle(3)
  val hardware = Wire(new MyBundle(3))
  hardware := DontCare
  val a = 0.U.asTypeOf(chiselType)
  val b = 0.U.asTypeOf(hardware)
})

Can only := to hardware:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  hardware := DontCare
})
// Not this...
ChiselStage.elaborate(new Module {
  val chiselType = new MyBundle(3)
  chiselType := DontCare
})
// chisel3.package$ExpectedHardwareException: data to be connected 'MyBundle' must be hardware, not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$24$$anonfun$apply$15$$anon$3.<init>(chisel-type-vs-scala-type.md:80)
// 	at repl.MdocSession$App$$anonfun$24$$anonfun$apply$15.apply(chisel-type-vs-scala-type.md:78)
// 	at repl.MdocSession$App$$anonfun$24$$anonfun$apply$15.apply(chisel-type-vs-scala-type.md:78)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Can only := from hardware:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = IO(new MyBundle(3))
  val moarHardware = Wire(new MyBundle(3))
  moarHardware := DontCare
  hardware := moarHardware
})
// Not this...
ChiselStage.elaborate(new Module {
  val hardware = IO(new MyBundle(3))
  val chiselType = new MyBundle(3)
  hardware := chiselType
})
// chisel3.package$ExpectedHardwareException: data to be connected 'MyBundle' must be hardware, not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$34$$anonfun$apply$19$$anon$5.<init>(chisel-type-vs-scala-type.md:105)
// 	at repl.MdocSession$App$$anonfun$34$$anonfun$apply$19.apply(chisel-type-vs-scala-type.md:102)
// 	at repl.MdocSession$App$$anonfun$34$$anonfun$apply$19.apply(chisel-type-vs-scala-type.md:102)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Have to pass hardware to chiselTypeOf:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  hardware := DontCare
  val chiselType = chiselTypeOf(hardware)
})
// Not this...
ChiselStage.elaborate(new Module {
  val chiselType = new MyBundle(3)
  val crash = chiselTypeOf(chiselType)
})
// chisel3.package$ExpectedHardwareException: 'MyBundle' must be hardware, not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24$$anon$7$$anonfun$46$$anonfun$apply$26.apply(chisel-type-vs-scala-type.md:128)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24$$anon$7$$anonfun$46$$anonfun$apply$26.apply(chisel-type-vs-scala-type.md:128)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24$$anon$7$$anonfun$46.apply(chisel-type-vs-scala-type.md:128)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24$$anon$7$$anonfun$46.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24$$anon$7.<init>(chisel-type-vs-scala-type.md:128)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24.apply(chisel-type-vs-scala-type.md:126)
// 	at repl.MdocSession$App$$anonfun$44$$anonfun$apply$24.apply(chisel-type-vs-scala-type.md:126)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Have to pass hardware to *Init:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  hardware := DontCare
  val moarHardware = WireInit(hardware)
})
// Not this...
ChiselStage.elaborate(new Module {
  val crash = WireInit(new MyBundle(3))
})
// chisel3.package$ExpectedHardwareException: wire initializer 'MyBundle' must be hardware, not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29$$anon$9$$anonfun$54$$anonfun$apply$30.apply(chisel-type-vs-scala-type.md:150)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29$$anon$9$$anonfun$54$$anonfun$apply$30.apply(chisel-type-vs-scala-type.md:150)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29$$anon$9$$anonfun$54.apply(chisel-type-vs-scala-type.md:150)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29$$anon$9$$anonfun$54.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29$$anon$9.<init>(chisel-type-vs-scala-type.md:150)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29.apply(chisel-type-vs-scala-type.md:149)
// 	at repl.MdocSession$App$$anonfun$53$$anonfun$apply$29.apply(chisel-type-vs-scala-type.md:149)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Can’t pass hardware to a Wire, Reg, IO:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  hardware := DontCare
})
// Not this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  val crash = Wire(hardware)
})
// chisel3.package$ExpectedChiselTypeException: wire type '_32_Anon.hardware: Wire[MyBundle]' must be a Chisel type, not hardware
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32$$anon$11$$anonfun$62$$anonfun$apply$34.apply(chisel-type-vs-scala-type.md:172)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32$$anon$11$$anonfun$62$$anonfun$apply$34.apply(chisel-type-vs-scala-type.md:172)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32$$anon$11$$anonfun$62.apply(chisel-type-vs-scala-type.md:172)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32$$anon$11$$anonfun$62.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32$$anon$11.<init>(chisel-type-vs-scala-type.md:172)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32.apply(chisel-type-vs-scala-type.md:170)
// 	at repl.MdocSession$App$$anonfun$60$$anonfun$apply$32.apply(chisel-type-vs-scala-type.md:170)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

.Lit can only be called on Chisel type:

// Do this...
ChiselStage.elaborate(new Module {
  val hardwareLit = (new MyBundle(3)).Lit(
    _.foo -> 0.U, 
    _.bar -> 0.U
  )
})
//Not this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new MyBundle(3))
  val crash = hardware.Lit(
    _.foo -> 0.U,
    _.bar -> 0.U
  )
})
// chisel3.package$ExpectedChiselTypeException: bundle literal constructor model '_38_Anon.hardware: Wire[MyBundle]' must be a Chisel type, not hardware
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38$$anon$13$$anonfun$69$$anonfun$apply$40.apply(chisel-type-vs-scala-type.md:196)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38$$anon$13$$anonfun$69$$anonfun$apply$40.apply(chisel-type-vs-scala-type.md:196)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38$$anon$13$$anonfun$69.apply(chisel-type-vs-scala-type.md:196)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38$$anon$13$$anonfun$69.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38$$anon$13.<init>(chisel-type-vs-scala-type.md:196)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38.apply(chisel-type-vs-scala-type.md:194)
// 	at repl.MdocSession$App$$anonfun$67$$anonfun$apply$38.apply(chisel-type-vs-scala-type.md:194)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Can only use a Chisel type within a Bundle definition:

// Do this...
ChiselStage.elaborate(new Module {
  val hardware = Wire(new Bundle {
    val nested = new MyBundle(3)
  })
  hardware := DontCare
})
// Not this...
ChiselStage.elaborate(new Module {
  val crash = Wire(new Bundle {
    val nested = Wire(new MyBundle(3))
  })
})
// chisel3.package$ExpectedChiselTypeException: '_44_Anon.crash_nested: Wire[MyBundle]' must be a Chisel type, not hardware
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44$$anon$16$$anonfun$77$$anonfun$apply$45.apply(chisel-type-vs-scala-type.md:222)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44$$anon$16$$anonfun$77$$anonfun$apply$45.apply(chisel-type-vs-scala-type.md:222)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44$$anon$16$$anonfun$77.apply(chisel-type-vs-scala-type.md:222)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44$$anon$16$$anonfun$77.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44$$anon$16.<init>(chisel-type-vs-scala-type.md:222)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44.apply(chisel-type-vs-scala-type.md:221)
// 	at repl.MdocSession$App$$anonfun$76$$anonfun$apply$44.apply(chisel-type-vs-scala-type.md:221)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Can only call directionOf on Hardware:

import chisel3.experimental.DataMirror

class Child extends Module{
  val hardware = IO(new MyBundle(3))
  hardware := DontCare
  val chiselType = new MyBundle(3)
}
// Do this...
ChiselStage.elaborate(new Module {
  val child = Module(new Child())
  child.hardware := DontCare
  val direction = DataMirror.directionOf(child.hardware)
})
// Not this...
ChiselStage.elaborate(new Module {
val child = Module(new Child())
  child.hardware := DontCare
  val direction = DataMirror.directionOf(child.chiselType)
})
// chisel3.package$ExpectedHardwareException: node requested directionality on 'MyBundle' must be hardware, not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?
// 	at ... ()
// 	at repl.MdocSession$App$$anonfun$87$$anonfun$apply$49$$anon$19.<init>(chisel-type-vs-scala-type.md:261)
// 	at repl.MdocSession$App$$anonfun$87$$anonfun$apply$49.apply(chisel-type-vs-scala-type.md:258)
// 	at repl.MdocSession$App$$anonfun$87$$anonfun$apply$49.apply(chisel-type-vs-scala-type.md:258)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

Can call specifiedDirectionOf on hardware or Chisel type:

ChiselStage.elaborate(new Module {
  val child = Module(new Child())
  child.hardware := DontCare
  val direction0 = DataMirror.specifiedDirectionOf(child.hardware)
  val direction1 = DataMirror.specifiedDirectionOf(child.chiselType)
})

.asInstanceOf vs .asTypeOf vs chiselTypeOf

.asInstanceOf is a Scala runtime cast, usually used for telling the compiler that you have more information than it can infer to convert Scala types:

class ScalaCastingModule(gen: () => Bundle) extends Module {
  val io = IO(Output(gen().asInstanceOf[MyBundle]))
  io.foo := 0.U
}

This works if we do indeed have more information than the compiler:

ChiselStage.elaborate(new ScalaCastingModule( () => new MyBundle(3)))

But if we are wrong, we can get a Scala runtime exception:

class NotMyBundle extends Bundle {val baz = Bool()}
ChiselStage.elaborate(new ScalaCastingModule(() => new NotMyBundle()))
// java.lang.ClassCastException: repl.MdocSession$App$$anonfun$99$NotMyBundle$1 cannot be cast to repl.MdocSession$App$MyBundle
// 	at ... ()
// 	at repl.MdocSession$App$ScalaCastingModule$$anonfun$95$$anonfun$apply$52.apply(chisel-type-vs-scala-type.md:283)
// 	at repl.MdocSession$App$ScalaCastingModule$$anonfun$95$$anonfun$apply$52.apply(chisel-type-vs-scala-type.md:283)
// 	at chisel3.internal.prefix$.apply(prefix.scala:48)
// 	at repl.MdocSession$App$ScalaCastingModule$$anonfun$95.apply(chisel-type-vs-scala-type.md:283)
// 	at repl.MdocSession$App$ScalaCastingModule$$anonfun$95.apply(chisel-type-vs-scala-type.md)
// 	at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
// 	at repl.MdocSession$App$ScalaCastingModule.<init>(chisel-type-vs-scala-type.md:283)
// 	at repl.MdocSession$App$$anonfun$99$$anonfun$apply$54.apply(chisel-type-vs-scala-type.md:299)
// 	at repl.MdocSession$App$$anonfun$99$$anonfun$apply$54.apply(chisel-type-vs-scala-type.md:299)
// 	at ... ()
// 	at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)

.asTypeOf is a conversion from one Data subclass to another. It is commonly used to assign data to all-zeros, as described in this cookbook recipe, but it can also be used (though not really recommended, as there is no checking on width matches) to convert one Chisel type to another:

class SimilarToMyBundle(w: Int) extends Bundle{
  val foobar = UInt((2*w).W)
}

ChiselStage.emitVerilog(new Module {
  val in = IO(Input(new MyBundle(3)))
  val out = IO(Output(new SimilarToMyBundle(3)))

  out := in.asTypeOf(out)
})
// res12: String = """module _103_Anon(
//   input        clock,
//   input        reset,
//   input  [2:0] in_foo,
//   input  [2:0] in_bar,
//   output [5:0] out_foobar
// );
//   assign out_foobar = {in_foo,in_bar}; // @[chisel-type-vs-scala-type.md 317:21]
// endmodule
// """

In contrast to asInstanceOf and asTypeOf, chiselTypeOf is not a casting operation. It returns a Scala object which can be used as shown in the examples above to create more Chisel types and hardware with the same Chisel type as existing hardware.