【SpinalHDL】结构语句

简单介绍在SpinalHDL编程中使用到的结构语句,主要包括赋值语句、选择语句,由于SpinalHDL中没有循环语句,这里在不介绍。

1. 赋值语句

SpinalHDL中有多个赋值运算:

:=标准赋值, 等价于Verilog中的<=;对变量的最后一次赋值有效;直到下一个仿真周期数据才更新
\=等价于Verilog中的=, 数据会立即更新
<>自动地链接两个信号或相同类型的两个Bundle, 信号的方向通过in/out推断(行为上更像:=)
//因为硬件的并发性, `a`的值一直是1
val a, b, c = UInt(4 bits)
a := 0
b := a
a := 1  //a := 1 "wins"
c := a

var x UInt(4 bits)
val y, z = UInt(4 bits)
x := 0
y := x      //y读到0
x \= x + 1
z := x      //z读到1

//自动连接两个UART接口
uartCtrl.io.uart <> io.uart

第一部分代码会出现重赋值错误

  always @(*) begin
    x_1 = x;
    x_1 = (x + 4'b0001);
  end

  assign b = a;
  assign a = 8'h01;
  assign c = a;
  assign x = 4'b0000;
  assign y = x;
  assign z = x_1;

1.1 并行性(Concurrency)

你给每个组合逻辑或寄存器赋值的顺序对其硬件行为没有影响。例如以下两段代码完全等价:

//code0
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
//code1
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

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

//code0
assign c = (a + b);
assign b = 8'h02;
assign a = (b + 8'h03);
//code1
assign b = 8'h02;
assign a = (b + 8'h03);
assign c = (a + b);

1.2 以最后赋值为准

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

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

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

这会产生如下真值表:

xy=>结果
FalseFalse1
FasleTrue1
TrueFalse2
TrueTrue3

1.3 位宽检查

SpinalHDL会检查赋值的左右两侧bit宽度是否一致。有多种方式能够去适应给定的Bit向量(Bits, UInt, SInt):

改变尺寸的方法描述
x:=y.resized用y的改变大小后的复制赋值给x, 改变的值会根据匹配的x自适应
x:=y.resize(newWidth)用y的改变大小后的复制赋值给x, 尺寸是手工计算的

2. 选择语句when/switch/mux

2.1 when语句

when 语句是一种条件语句,可以根据条件执行不同的操作。基本语法如下:

when(cond1) {
    //当cond1真时执行操作
}.elsewhen(cond2) {
    //当cond1假但cond2真时执行操作
}.otherwise {
    //cond1和cond2同假时执行操作
}

 在 when 语句中,你可以根据条件执行相应的代码块,并且还可以使用 .otherwise 块指定当条件不满足时的默认操作。

注意:如果otherwise关键字和花括号的后半部分}在同一行, .可以省略;但如果.otherwise在另一行, 需要.

//省略.标识
when(cond1) {
  //当cond1真时执行操作
} otherwise {
  //cond1和cond2同假时执行操作
}
//不能省略.标识
when(cond1) {
  //当cond1真时执行操作
}
.otherwise {
  //cond1和cond2同假时执行操作
}

2.2 switch语句

switch 语句允许根据不同的选择值执行不同的操作。它的基本语法如下:

switch(x) {
    is(value1) {
        //当x===value1执行
    }
    is(value2) {
        //当x===value2执行
    }
    default {
        //当之前的条件都没有符合执行
    }
}

在 switch 语句中,你可以使用多个 is(value) 块来指定不同选择值时执行的代码块,并且可以使用 default 块指定当选择值不匹配任何已定义值时的默认操作。同时is语句块可以用is(value1, value2)这种逗号分割的方式书写,代表多种条件下执行相同语句块。

switch(aluop) {
    is(ALUOp.add) {
        immediate := instruction.immI.signExtend
    }
    is(ALUOp.slt) {
        immediate := instruction.immI.signExtend
    }
    is(ALUOp.sltu) {
        immediate := instruction.immI.signExtend
    }
    is(ALUOp.sll) {
        immediate := instruction.shamt
    }
    is(ALUOp.sra) {
        immediate := instruction.shamt
    }
}

等价于

switch(aluop) {
    is(ALU0p.add, ALU0p.slt, ALU0p.sltu) {
        immediate := instruction.immI.signExtend
    }
    is(ALU0p.sll, ALU0p.sra) {
        immediate := instruction.shamt
    }
}

2.3 Mux语句

在 SpinalHDL中,可以使用 Mux(多路复用)语句来实现多路选择逻辑。Mux 语句可以根据一个选择信号选择其中一个输入,并输出所选输入的值。Mux 语句的基本语法如下:

val result = Mux(select, inputTrue, inputFalse)

其中:

  • select 是一个布尔型选择信号,用于选择要输出的输入之一。
  • inputTrue 是当 select 为真时要输出的输入值。
  • inputFalse 是当 select 为假时要输出的输入值。

以下是一个示例,演示如何使用 Mux 语句来实现多路选择:

val select = Bool() // 假设有一个布尔型选择信号 select
val inputTrue = UInt(8 bits) // 假设有一个8位无符号整数输入 inputTrue
val inputFalse = UInt(8 bits) // 假设有一个8位无符号整数输入 inputFalse

val result = Mux(select, inputTrue, inputFalse)

在上面的示例中,根据 select 的值,Mux 语句将选择输出 inputTrue 或 inputFalse 中的一个值作为 result。

通过使用 Mux 语句,你可以在 SpinalHDL 中实现多路选择逻辑,根据选择信号选择不同的输入。这对于构建选择器、多路开关和其他需要选择输入的硬件电路非常有用。

3.循环语句when/for

在 SpinalHDL 中,虽然没有直接的循环语句(例如 Verilog 中的 for 循环),但你可以使用递归和生成器来实现类似的功能。

3.1 when递归

你可以使用递归来实现一系列相似的操作。通过定义一个递归函数,每次调用函数时传递不同的参数,从而模拟循环的效果。以下是一个简单的示例,演示如何使用递归在 SpinalHDL 中实现类似于 for 循环的功能:

def generateLogic(index: UInt): Unit = {
  when(index < N) {
    // 在每次迭代中执行的逻辑

    generateLogic(index + 1) // 递归调用
  }
}

val index = Reg(UInt(log2Up(N) bits)) // 假设有一个用于迭代的计数器 index
generateLogic(index)

在上面的示例中,generateLogic 是一个递归函数,它根据 index 的值执行一系列逻辑,并在每次迭代中递增 index 的值。通过递归调用 generateLogic,可以实现类似于 for 循环的迭代效果。

3.2 for生成器

SpinalHDL 还提供了一种强大的机制,即使用生成器来生成硬件电路。生成器允许你在编译时生成多个相似的硬件模块,从而实现类似于循环的效果。你可以使用 for 循环语句或迭代器来生成多个硬件实例。以下是一个简单的示例,演示如何使用生成器在 SpinalHDL 中生成多个硬件模块:

import spinal.core._

class MyModule extends Component {
  val inputs = Vec(5, UInt(8 bits)) // 假设有一个包含 5 个 8 位无符号整数的输入向量

  // 生成多个硬件模块
  for (i <- 0 until 5) {
    val submodule = new MySubmodule(inputs(i))
    // 连接其他信号和逻辑
    // ...
  }
}

在上面的示例中,通过使用 for 循环来迭代 5 次,并在每次迭代中生成一个名为 MySubmodule 的硬件子模块。每个子模块都接收不同的输入信号。

使用递归和生成器的这些技术,你可以在 SpinalHDL 中实现类似于 Verilog 中的 for 循环的功能。通过利用递归和生成器的强大特性,可以有效地描述复杂的硬件电路结构和行为。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

惜缘若水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值