规则

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 中。