赋值
有多个赋值运算符:
| 符号 | 描述 | 
|---|---|
| 
 | 标准赋值,相当于 VHDL/Verilog 中的``<=``。 | 
| 
 | 相当于 VHDL 中的  | 
| 
 | 2 个信号或相同类型的两个信号线束之间的自动连接。通过使用信号定义(输入/输出)来推断方向。 (与  | 
When muxing (for instance using when, see When/Switch/Mux.), the last
valid standard assignment := wins. Else, assigning twice to the same assignee
from the same scope results in an assignment overlap.  SpinalHDL will assume
this is a unintentional design error by default and halt elaboration with error.
For special use-cases assignment overlap can be programmatically permitted on a case by case basis.
(see 赋值覆盖(Assignment overlap)).
val a, b, c = UInt(4 bits)
a := 0
b := a
// a := 1 // this would cause an `assignment overlap` error,
          // if manually overridden the assignment would take assignment priority
c := a
var x = UInt(4 bits)
val y, z = UInt(4 bits)
x := 0
y := x      // y read x with the value 0
x \= x + 1
z := x      // z read x with the value 1
// Automatic connection between two UART interfaces.
uartCtrl.io.uart <> io.uart
它还支持线束赋值(将所有位信号转换为适当位宽的 Bits 类型的单位总线,然后在赋值表达式中使用更宽的形式)。在赋值表达式的左侧和右侧使用 () (Scala 元组语法)将多个信号捆绑在一起。
val a, b, c = UInt(4 bits)
val d       = UInt(12 bits)
val e       = Bits(10 bits)
val f       = SInt(2  bits)
val g       = Bits()
(a, b, c) := B(0, 12 bits)
(a, b, c) := d.asBits
(a, b, c) := (e, f).asBits           // both sides
g         := (a, b, c, e, f).asBits  // and on the right hand side
重要的是要理解,在 SpinalHDL 中,信号的性质(组合/时序)是在其声明中定义的,而不是通过赋值的方式定义的。所有数据类型实例都将定义一个组合信号,而用 Reg(...) 包装的实例将定义为一个时序信号(寄存器)。
val a = UInt(4 bits)              // Define a combinational signal
val b = Reg(UInt(4 bits))         // Define a registered signal
val c = Reg(UInt(4 bits)) init(0) // Define a registered signal which is
                                  //  set to 0 when a reset occurs
位宽检查
SpinalHDL 检查赋值左侧和右侧的位数是否匹配。有多种方法可以改变给定 BitVector (Bits, UInt, SInt)的位宽:
| 调整位宽的技术 | 描述 | 
|---|---|
| x := y.resized | 将 y 改变位宽后的副本分配给 x,其位宽是从 x 推断出来的。 | 
| x := y.resize(newWidth) | 为 x 赋值一个 y 变为 newWidth 位宽后的副本。 | 
| x := y.resizeLeft(newWidth) | 对 x 赋值 y 变为 newWidth 位宽后的副本。如果需要,可在 LSB 处进行填充。 | 
所有改变位宽方法都可能导致生成的位宽比 y 的原始位宽更宽或更窄。当发生加宽时,额外的位将用零填充。
x.resized 根据赋值表达式左侧的目标位宽推断转换方法,并遵循与 y.resize(someWidth) 相同的语义。表达式 x := y.resized 相当于 x := y.resize(x.getBitsWidth bits)。
虽然示例代码片段显示了赋值语句的使用方法,但 resize 系列方法可以像任何普通 Scala 方法一样进行级联。
在一种情况下,Spinal 会自动调整位宽的大小:
// U(3) creates an UInt of 2 bits, which doesn't match the left side (8 bits)
myUIntOf_8bits := U(3)
因为 U(3) 是一个“弱”位计数推断信号,SpinalHDL 会自动加宽它。这可以被认为在功能上等同于 U(3, 2 bits).resized ,但是请放心,如果场景需要缩小范围,SpinalHDL 将做正确的事情并报告错误。当尝试赋值 myUIntOf_8bits``时,如果字面量需要 9 位(例如 ``U(0x100)),则会报告错误。
组合逻辑环(Combinatorial loops)
SpinalHDL 检查您的设计中是否存在组合逻辑环(锁存器)。如果检测到,会引发错误,并且 SpinalHDL 将打印造成循环的路径。
CombInit
CombInit 可用于复制信号及其当前的组合逻辑赋值。主要用例是能够稍后覆盖复制后信号,而不影响原始信号。
val a = UInt(8 bits)
a := 1
val b = a
when(sel) {
    b := 2
    // At this point, a and b are evaluated to 2: they reference the same signal
}
val c = UInt(8 bits)
c := 1
val d = CombInit(c)
// Here c and d are evaluated to 1
when(sel) {
    d := 2
    // At this point c === 1 and d === 2.
}
If we look at the resulting Verilog, b is not present. Since it is a copy of a by reference, these variables designate the same Verilog wire.
always @(*) begin
  a = 8'h01;
  if(sel) begin
    a = 8'h02;
  end
end
assign c = 8'h01;
always @(*) begin
  d = c;
  if(sel) begin
    d = 8'h02;
  end
end
CombInit 在辅助函数中特别有用,可确保返回值不引用输入。
// note that condition is an elaboration time constant
def invertedIf(b: Bits, condition: Boolean): Bits = if(condition) { ~b } else { CombInit(b) }
val a2 = invertedIf(a1, c)
when(sel) {
   a2 := 0
}
Without CombInit, if c == false (but not if c == true), a1 and a2 reference the same signal and the zero assignment is also applied to a1.
With CombInit we have a coherent behavior whatever the c value.