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

如果关键字otherwise与when条件的右括号}在同一行,则不需要加点。如下:

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

1.2、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(value1, value2)。

1.2.1、实例

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

1.2.2、附加选项

默认情况下,如果一个 switch 包含了一个 default 语句,并且该switch 的所有可能逻辑值已经被 is 语句覆盖,则 SpinalHDL 将生成“UNREACHABLE DEFAULT STATEMENT”错误。您可以通过指定 switch(myValue, coverUnreachable = true) { … } 来取消此错误报告。

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

此检查是基于逻辑值而非物理值进行的。例如,如果您有一个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) { ... }
}

1.3、本地声明

可以在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检查在作用域内定义的信号只能在该作用域内被赋值。

1.4、Mux

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

【SpinalHDL快速入门】6.2、SpinalHDL语法之When/Switch/Mux-LMLPHP

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

1.5、位选择

一个位选择看起来像VHDL的when语法。

1.5.1、实例

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(...)是备选的位选择器,它们将 tuples 或 mappings 序列作为输入。

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

如果未覆盖的值不重要,则可以使用muxtListDc,并通过使用muxListDc将其保留未分配状态。如果需要,则会添加一个默认情况。如果仿真过程中遇到X,则此默认情况将生成X'smuxListDc(...)通常是通用代码的良好替代方案。

以下是将128位比特数分成32位比特数的示例:

【SpinalHDL快速入门】6.2、SpinalHDL语法之When/Switch/Mux-LMLPHP

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)))
}
06-07 06:09