规则(Rules)

一、简介(Introduction)

SpinalHDL背后的语义学很重要, 学会语义你就能理解在这些情景背后到底发生了什么, 并且如何去控制它。

这些语义通过以下几条规则定义:

  • 信号和寄存器互相并行地执行(正如VHDL和Verilog, 硬件并发特点)

  • 对组合逻辑的赋值很像是写出它何时是true的表达式

  • 对寄存器的赋值很像是写出在时钟域下施加什么信号的规则

  • 对于每个信号, 以最后一个赋值的为准

  • 每个信号和寄存器都能以OOP方式在硬件描述细化期间作为对象控制

二、并行性(Concurrency)

你给每个组合逻辑或寄存器赋值的顺序对其硬件行为没有影响。

例如, 以下两段代码完全等价:

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

更加通俗来说, 当你用:=赋值操作符, 就好比给左侧的信号/寄存器一个新的数据产生规则。

三、以最后赋值为准(Last valid assignment wins)

如果组合逻辑信号或寄存器多次被赋值, 最后一次赋值有效。

例如:

val x, y = Bool()           //定义两个组合逻辑信号
val result = UInt(8 bits)   //定义一个组合逻辑信号

result := 1
when(x) {
    result := 2
    when(y) {
        result := 3
    }
}

这会产生如下真值表:

x y => 结果
False False 1
Fasle True 1
True False 2
True True 3

四、Scala下的信号和寄存器的内在联系(Signal and register interactions with Scala)(OOP引用+函数)

在SpinalHDL中, 每个硬件单元通过类例化被建模。这意味着你可以用他们的引用使用例化, 例如把他们作为变量传递给函数。

以下例子实现了当inc为真时自增, 当clear为真时清零的寄存器(clear的优先级高于inc):

val inc, clear = Bool()             //定义两个组合逻辑信号/线类型
val counter = Reg(UInt(8 bits))     //定义8 bit寄存器

when(inc) {
    counter := counter + 1
}
when(clear) {
    counter := 0    //如果inc和clear都为真, 该赋值有效
}

你可以通过用赋值给counter的函数混合之前的例子来实现完全一致的函数性:

val inc, clear = Bool()
val counter = Reg(UInt(8 bits))

def setCounter(value: UInt): Unit = {
    counter := value
}

when(inc) {
    setCounter(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语法本身。

换句话说, 从生成SpinalHDL/RTL生成器角度来说, 当你在Scala用函数生成电路, 这就好像函数是内联的。这对于Scala loop也同样适用, 因为他们将以展开的形式生成RTL。