规则
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 :=
运算符对组合信号或寄存器进行多次分配,则可能执行的最后一次赋值生效(因此可以将值设置为该状态的结果)。
It could be said that top to bottom evaluation occurs based on the state that exists at that time. If your upstream signal inputs are driven from registers and so have synchronous behavior, then it could be said that at each clock cycle the assignments are re-evaluated based on the new state at the time.
在硬件中,赋值语句可能无法在本时钟周期执行的一些原因,可能是由于它被包装在 when(cond)
子句中。
Another reason maybe that the SpinalHDL code never made it through elaboration
because the feature was parameterized and disabled during HDL code-generation,
see paramIsFalse
use below.
举个例子:
// 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 中。