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)
}
警告
如果关键字 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)
}
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 ? whenTrue | whenFalse |
T |
当 |
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
将它们保留为未分配状态。如果需要,这将添加默认情况。如果遇到这种默认情况,将在仿真过程中生成 X。muxListDc(...)
通常是一个很好的通用代码的替代方法。
下面是将 128 位的 Bits
划分为 32 位的示例:
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)))
}