The Bundle is a composite type that defines a group of named signals (of any SpinalHDL basic type) under a single name.

A Bundle can be used to model data structures, buses, and interfaces.


The syntax to declare a bundle is as follows:

case class myBundle extends Bundle {
  val bundleItem0 = AnyType
  val bundleItem1 = AnyType
  val bundleItemN = AnyType

For example, a bundle holding a color could be defined as:

case class Color(channelWidth: Int) extends Bundle {
  val r, g, b = UInt(channelWidth bits)

You can find an APB3 definition among the Spinal HDL examples.

Conditional signals

The signals in the Bundle can be defined conditionally. Unless dataWidth is greater than 0, there will be no data signal in elaborated myBundle, as demonstrated in the example below.

case class myBundle(dataWidth: Int) extends Bundle {
  val data = (dataWidth > 0) generate (UInt(dataWidth bits))


The following operators are available for the Bundle type:




Return type

x === y



x =/= y



val color1 = Color(8)
color1.r := 0
color1.g := 0
color1.b := 0

val color2 = Color(8)
color2.r := 0
color2.g := 0
color2.b := 0

myBool := color1 === color2

Type cast





Binary cast to Bits

Bits(w(x) bits)

val color1 = Color(8)
val myBits := color1.asBits

The elements of the bundle will be mapped into place in the order in which they are defined. Thus, r in color1 will occupy bits 0 to 8 of myBits (LSB), followed by g and b in that order.

Convert Bits back to Bundle

The .assignFromBits operator can be viewed as the reverse of .asBits.





Convert Bits (y) to Bundle(x)


x.assignFromBits(y, hi, lo)

Convert Bits (y) to Bundle(x) with high/low boundary


The following example saves a Bundle called CommonDataBus into a circular buffer (3rd party memory), reads the Bits out later and converts them back to CommonDataBus format.

case class TestBundle () extends Component {
  val io = new Bundle {
    val we      = in     Bool()
    val addrWr  = in     UInt (7 bits)
    val dataIn  = slave  (CommonDataBus())

    val addrRd  = in     UInt (7 bits)
    val dataOut = master (CommonDataBus())

  val mm = Ram3rdParty_1w_1rs (G_DATA_WIDTH = io.dataIn.getBitsWidth,
                               G_ADDR_WIDTH = io.addrWr.getBitsWidth,
                               G_VENDOR     = "Intel_Arria10_M20K")

  mm.io.clk_in    := clockDomain.readClockWire
  mm.io.clk_out   := clockDomain.readClockWire

  mm.io.we        := io.we
  mm.io.addr_wr   := io.addrWr.asBits
  mm.io.d         := io.dataIn.asBits

  mm.io.addr_rd   := io.addrRd.asBits

IO Element direction

When you define a Bundle inside the IO definition of your component, you need to specify its direction.


If all elements of your bundle go in the same direction you can use in(MyBundle()) or out(MyBundle()).

For example:

val io = new Bundle {
  val input  = in (Color(8))
  val output = out(Color(8))


If your interface obeys to a master/slave topology, you can use the IMasterSlave trait. Then you have to implement the function def asMaster(): Unit to set the direction of each element from the master’s perspective. Then you can use the master(MyBundle()) and slave(MyBundle()) syntax in the IO definition.

There are functions defined as toXXX, such as the toStream method of the Flow class. These functions can usually be called by the master side. In addition, the fromXXX functions are designed for the slave side. It is common that there are more functions available for the master side than for the slave side.

For example:

case class HandShake(payloadWidth: Int) extends Bundle with IMasterSlave {
  val valid   = Bool()
  val ready   = Bool()
  val payload = Bits(payloadWidth bits)

  // You have to implement this asMaster function.
  // This function should set the direction of each signals from an master point of view
  override def asMaster(): Unit = {
    out(valid, payload)

val io = new Bundle {
  val input  = slave(HandShake(8))
  val output = master(HandShake(8))