规则
SpinalHDL 背后的语义很重要,这样您就可以了解幕后真正发生的事情以及如何控制它。
这些语义由多个规则定义:
信号和寄存器彼此同时运行(并行行为,如 VHDL 和 Verilog)
对组合信号的赋值就像表达一条始终为真的规则
对寄存器的赋值就像表达一条应用于其时钟域的每个周期的规则
对于每个信号,最后一个赋值生效
每个信号和寄存器都可以作为对象在硬件实例细化期间进行操作,用 OOP 的思想
并发
您对每个组合或时序信号的赋值顺序不会对行为产生影响。
例如,以下两段代码是等效的:
val a, b, c = UInt(8 bits) // Define 3 combinational signals
c := a + b // c will be set to 7
b := 2 // b will be set to 2
a := b + 3 // a will be set to 5
这相当于:
val a, b, c = UInt(8 bits) // Define 3 combinational signals
b := 2 // b will be set to 2
a := b + 3 // a will be set to 5
c := a + b // c will be set to 7
更一般地说,当您使用 :=
赋值运算符时,就像为左侧信号/寄存器指定一个附加的新规则。
最后有效赋值生效
如果通过使用 SpinalHDL :=
运算符对组合信号或寄存器进行多次分配,则可能执行的最后一次赋值生效(因此可以将值设置为该状态的结果)。
可以说,自上而下的评估计算是根据当时的状态进行的。如果您的上游信号输入是从寄存器驱动的,因此具有同步行为,那么可以说,在每个时钟周期,都会根据当时的新状态重新计算、赋值。
在硬件中,赋值语句可能无法在本时钟周期执行的一些原因,可能是由于它被包装在 when(cond)
子句中。
另一个原因,可能是 SpinalHDL 代码从未通过实力细化,因为该功能在 HDL 代码生成期间被参数化并禁用,请参阅下面 paramIsFalse
的案例。
举个例子:
// Every clock cycle evaluation starts here
val paramIsFalse = false
val x, y = Bool() // Define two combinational signals
val result = UInt(8 bits) // Define a combinational signal
result := 1
when(x) {
result := 2
when(y) {
result := 3
}
}
if(paramIsFalse) { // This assignment should win as it is last, but it was never elaborated
result := 4 // into hardware due to the use of if() and it evaluating to false at the time
} // of elaboration. The three := assignments above are elaborated into hardware.
这将产生以下真值表:
x |
y |
=> |
结果 |
---|---|---|---|
False |
False |
1 |
|
False |
True |
1 |
|
True |
False |
2 |
|
True |
True |
3 |
信号和寄存器与 Scala 语言的协作(OOP 引用 + 函数)
在 SpinalHDL 中,每个硬件元素都由一个类实例建模。这意味着您可以通过使用实例的引用来操作实例,例如将它们作为参数传递给函数。
作为示例,以下代码实现了一个寄存器,当 inc
为 True 时递增,当 clear
为 True 时清零(clear
优先于 inc
):
val inc, clear = Bool() // Define two combinational signals/wires
val counter = Reg(UInt(8 bits)) // Define an 8 bit register
when(inc) {
counter := counter + 1
}
when(clear) {
counter := 0 // If inc and clear are True, then this assignment wins
} // (last value assignment wins rule)
您可以通过将前面的示例与赋值给 ``counter``的函数混合来实现完全相同的功能:
val inc, clear = Bool()
val counter = Reg(UInt(8 bits))
def setCounter(value : UInt): Unit = {
counter := value
}
when(inc) {
setCounter(counter + 1) // Set counter with counter + 1
}
when(clear) {
counter := 0
}
您还可以将条件检查集成到函数内:
val inc, clear = Bool()
val counter = Reg(UInt(8 bits))
def setCounterWhen(cond : Bool,value : UInt): Unit = {
when(cond) {
counter := value
}
}
setCounterWhen(cond = inc, value = counter + 1)
setCounterWhen(cond = clear, value = 0)
并指定函数应实现的赋值:
val inc, clear = Bool()
val counter = Reg(UInt(8 bits))
def setSomethingWhen(something : UInt, cond : Bool, value : UInt): Unit = {
when(cond) {
something := value
}
}
setSomethingWhen(something = counter, cond = inc, value = counter + 1)
setSomethingWhen(something = counter, cond = clear, value = 0)
前面的所有示例在生成的 RTL 中,从 SpinalHDL 编译器的角度来看都是严格等效的。这是因为 SpinalHDL 只关心 Scala 运行时实例化的对象,它不关心 Scala 语法本身。
换句话说,从生成的 RTL 生成/SpinalHDL 的角度来看,当您调用 Scala 中生成硬件的函数时,就像该函数被内联了一样。 Scala 循环也是如此,因为它们将以展开的形式出现在生成的 RTL 中。