When/Switch/Mux

When

与 VHDL 和 Verilog 中一样,当满足指定条件时可以有条件地赋值信号:

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

警告

如果关键字 otherwisewhen 条件的右括号 } 在同一行,则不需要点。

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

但如果 .otherwise 在另一行,则**需要**一个点:

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

WhenBuilder

有时需要为when条件生成一些参数,而when else结构并不太合适。因此,我们提供了一个’whenBuilder’方法来实现这个目标

import spinal.lib._

val conds = Bits(8 bits)
val result = UInt(8 bits)

val ctx = WhenBuilder()
ctx.when(conds(0)) {
  result := 0
}
ctx.when(conds(1)) {
  result := 1
}
if(true) {
  ctx.when(conds(2)) {
    result := 2
  }
}
ctx.when(conds(3)) {
  result := 3
}

与when/elsewhen/otherwise方法相比,它可能更方便于参数化。我们也可以这样使用

for(i <- 5 to 7) ctx.when(conds(i)) {
  result := i
}

ctx.otherwise {
  result := 255
}

switch(addr) {
  for (i <- addressElements ) {
    is(i) {
      rdata :=  buffer(i)
    }
  }
}

这样,我们可以像在 switch() 中使用 foreach 一样,对优先级电路进行参数化,并以更直观的if-else格式生成代码。

Switch

与 VHDL 和 Verilog 中一样,当信号具有定义的值时,可以有条件地对信号赋值:

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

is 子句可以通过用逗号 is(value1, value2) 分隔来进行分解(逻辑 OR)。

示例

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
  }
}

相当于

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

其他选项

默认情况下,如果 switch 包含 default 语句,而 switch 的所有可能的逻辑值都已被是 is 语句“覆盖”,SpinalHDL 将生成“UNREACHABLE DEFAULT STATEMENT”错误。您可以通过指定 `` switch(myValue, coverUnreachable = true) { … }`` 来删除此错误报告。

switch(my2Bits, coverUnreachable = true) {
    is(0) { ... }
    is(1) { ... }
    is(2) { ... }
    is(3) { ... }
    default { ... } // This will parse and validate without error now
}

备注

此检查是针对逻辑值而不是物理值进行的。例如,如果您有一个以独热编码的 SpinalEnum(A,B,C),SpinalHDL 将只关心 A,B,C 值 (“001” “010” “100”)。物理值“000”“011”“101”“110”“111”将不被考虑。

默认情况下,如果给定的 is 语句多次提供相同的值,SpinalHDL 将生成 “DUPLICATED ELEMENTS IN SWITCH IS(…) STATEMENT” 错误。例如 is(42,42) { ... } 您可以通过指定 switch(myValue, strict = true){ ... } 来避免报告此错误。 SpinalHDL 然后将负责删除重复的值。

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

本地声明

可以在 when/switch 语句中定义新信号:

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
}

备注

SpinalHDL 会检查范围内定义的信号是否仅在该范围内使用/赋值。

Mux

如果您只需要一个带有 Bool 选择信号的 Mux ,则有两种等效的语法:

语法

返回类型

描述

Mux(cond, whenTrue, whenFalse)

T

cond 为 True 时返回 whenTrue ,否则返回 whenFalse

cond ? whenTrue | whenFalse

T

cond 为 True 时返回 whenTrue ,否则返回 whenFalse

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

按位选择

按位选择看起来像 VHDL when 语法。

示例

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 检查所有可能的值是否都被覆盖以防止锁存器的生成。如果覆盖了所有可能的值,则不允许添加default语句:

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(...)muxListDc(...) 是另一种按位选择器,它们采用元组或映射作为输入。

muxList 可以用作 mux 的直接替代品,在生成案例的代码中提供更易于使用的接口。它具有与 mux 相同的检查行为,它要求完全覆盖并禁止在不需要时列出默认值。

如果未覆盖的值不重要,则可以使用 muxtListDc ,可以使用 muxtListDc 将它们保留为未分配状态。如果需要,这将添加默认情况。如果遇到这种默认情况,将在仿真过程中生成 XmuxListDc(...) 通常是一个很好的通用代码的替代方法。

下面是将 128 位的 Bits 划分为 32 位的示例:

../../_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)

下面是 muxListDc 的案例,从可配置位宽的向量中选择多个位:

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)))
}