赋值
有多个赋值运算符:
符号 |
描述 |
---|---|
|
标准赋值,相当于 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.
}
如果我们查看生成的 Verilog,会发现``b`` 不存在。由于它是引用的 a
的副本,因此这些变量指代相同的 Verilog 信号。
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.