When/Switch/Mux

When

As in VHDL and Verilog, signals can be conditionally assigned when a specified condition is met:

when(cond1) {
  // Execute when cond1 is true
}.elsewhen(cond2) {
  // Execute when (not cond1) and cond2
}.otherwise {
  // Execute when (not cond1) and (not cond2)
}

Warning

If the keyword otherwise is on the same line as the closing bracket } of the when condition, no dot is needed.

when(cond1) {
    // Execute when cond1 is true
} otherwise {
    // Execute when (not cond1) and (not cond2)
}

But if .otherwise is on another line, a dot is required:

when(cond1) {
    // Execute when cond1 is true
}
.otherwise {
    // Execute when (not cond1) and (not cond2)
}

Switch

As in VHDL and Verilog, signals can be conditionally assigned when a signal has a defined value:

switch(x) {
  is(value1) {
    // Execute when x === value1
  }
  is(value2) {
    // Execute when x === value2
  }
  default {
    // Execute if none of precedent conditions met
  }
}

is clauses can be factorized by separating them with a comma is(value1, value2).

Example

switch(aluop) {
  is(ALUOp.add) {
    immediate := instruction.immI.signExtend
  }
  is(ALUOp.slt) {
    immediate := instruction.immI.signExtend
  }
  is(ALUOp.sltu) {
    immediate := instruction.immI.signExtend
  }
  is(ALUOp.sll) {
    immediate := instruction.shamt
  }
  is(ALUOp.sra) {
    immediate := instruction.shamt
  }
}

is equivalent to

switch(aluop) {
  is(ALUOp.add, ALUOp.slt, ALUOp.sltu) {
      immediate := instruction.immI.signExtend
  }
  is(ALUOp.sll, ALUOp.sra) {
      immediate := instruction.shamt
  }
}

Additional options

By default, SpinalHDL will generate an “UNREACHABLE DEFAULT STATEMENT” error if a switch contains a default statement while all the possible logical values of the switch are already covered by the is statements. You can drop this error reporting by specifying `` switch(myValue, coverUnreachable = true) { … }``.

switch(my2Bits, coverUnreachable = false) {
    is(0) { ... }
    is(1) { ... }
    is(2) { ... }
    is(3) { ... }
    default { ... } // This will be okay
}

Note

This check is done on the logical values, not on the physical values. For instance, if you have a SpinalEnum(A,B,C) encoded in a on-hot manner, SpinalHDL will only care about the A,B,C values (“001” “010” “100”). Pyhsical values as “000” “011” “101” “110” “111” will not be taken in account.

By default, SpinalHDL will generate a “DUPLICATED ELEMENTS IN SWITCH IS(…) STATEMENT” error if a given is statement provides multiple times the same value. For instance is(42,42) { ... } You can drop this error reporting by specifying switch(myValue, strict = true){ ... }. SpinalHDL will then take care of removing duplicated values.

switch(value, strict = false) {
    is(0) { ... }
    is(1,1,1,1,1) { ... } // This will be okay
    is(2) { ... }
}

Local declaration

It is possible to define new signals inside a when/switch statement:

val x, y = UInt(4 bits)
val a, b = UInt(4 bits)

when(cond) {
  val tmp = a + b
  x := tmp
  y := tmp + 1
} otherwise {
  x := 0
  y := 0
}

Note

SpinalHDL checks that signals defined inside a scope are only assigned inside that scope.

Mux

If you just need a Mux with a Bool selection signal, there are two equivalent syntaxes:

Syntax

Return

Description

Mux(cond, whenTrue, whenFalse)

T

Return whenTrue when cond is True, whenFalse otherwise

cond ? whenTrue | whenFalse

T

Return whenTrue when cond is True, whenFalse otherwise

val cond = Bool()
val whenTrue, whenFalse = UInt(8 bits)
val muxOutput  = Mux(cond, whenTrue, whenFalse)
val muxOutput2 = cond ? whenTrue | whenFalse

Bitwise selection

A bitwise selection looks like the VHDL when syntax.

Example

val bitwiseSelect = UInt(2 bits)
val bitwiseResult = bitwiseSelect.mux(
  0 -> (io.src0 & io.src1),
  1 -> (io.src0 | io.src1),
  2 -> (io.src0 ^ io.src1),
  default -> (io.src0)
)

mux checks that all possible values are covered to prevent generation of latches. If all possible values are covered, the default statement must not be added:

val bitwiseSelect = UInt(2 bits)
val bitwiseResult = bitwiseSelect.mux(
  0 -> (io.src0 & io.src1),
  1 -> (io.src0 | io.src1),
  2 -> (io.src0 ^ io.src1),
  3 -> (io.src0)
)

muxList(...) and muxListDc(...) are alternatives bitwise selectors that take a sequence of tuples or mappings as input.

muxList can be used as a direct replacement for mux, providing a easier to use interface in code that generates the cases. It has the same checking behavior as mux does, requiring full coverage and prohibiting listing a default if it is not needed.

muxtListDc can be used if the uncovered values are not important, they can be left unassigned by using muxListDc. This will add a default case if needed. This default case will generate X’s during the simulation if ever encountered. muxListDc(...) is often a good alternative in generic code.

Below is an example of dividing a Bits of 128 bits into 32 bits:

../../_images/MuxList.png
val sel  = UInt(2 bits)
val data = Bits(128 bits)

// Dividing a wide Bits type into smaller chunks, using a mux:
val dataWord = sel.muxList(for (index <- 0 until 4) yield (index, data(index*32+32-1 downto index*32)))

// A shorter way to do the same thing:
val dataWord = data.subdivideIn(32 bits)(sel)

Example for muxListDc selecting bits from a configurable width vector:

case class Example(width: Int = 3) extends Component {
  // 2 bit wide for default width
  val sel = UInt(log2Up(count) bit)
  val data = Bits(width*8 bit)
  // no need to cover missing case 3 for default width
  val dataByte = sel.muxListDc(for(i <- 0 until count) yield (i, data(index*8, 8 bit)))
}