Chisel 语言(Scala)学习 2 实例化模型和向量操作

本文介绍使用Chisel和Scala实现带有进位的4位加法器的过程,并演示了如何通过实例化全加器模块来构建加法器。此外,还介绍了向量操作和寄存器的定义方法。

Chisel 语言学习 2 实例化模型和向量操作

系列文章主要用来记录学习Chisel和scala过程中遇到的难点或知识点

实例化模型:

带有进位的加法器
这里写图片描述

Chisel代码如下:

// A 4-bit adder with carry in and carry out
class Adder4 extends Module {
  val io = IO(new Bundle {
    val A    = Input(UInt(4.W))
    val B    = Input(UInt(4.W))
    val Cin  = Input(UInt(1.W))
    val Sum  = Output(UInt(4.W))
    val Cout = Output(UInt(1.W))
  })
  // Adder for bit 0
  val Adder0 = Module(new FullAdder())
  Adder0.io.a   := io.A(0)
  Adder0.io.b   := io.B(0)
  Adder0.io.cin := io.Cin
  val s0 = Adder0.io.sum
  // Adder for bit 1
  val Adder1 = Module(new FullAdder())
  Adder1.io.a   := io.A(1)
  Adder1.io.b   := io.B(1)
  Adder1.io.cin := Adder0.io.cout
  val s1 = Cat(Adder1.io.sum, s0)
  // Adder for bit 2
  val Adder2 = Module(new FullAdder())
  Adder2.io.a   := io.A(2)
  Adder2.io.b   := io.B(2)
  Adder2.io.cin := Adder1.io.cout
  val s2 = Cat(Adder2.io.sum, s1)
  // Adder for bit 3
  val Adder3 = Module(new FullAdder())
  Adder3.io.a   := io.A(3)
  Adder3.io.b   := io.B(3)
  Adder3.io.cin := Adder2.io.cout
  io.Sum  := Cat(Adder3.io.sum, s2).asUInt
  io.Cout := Adder3.io.cout
}

向量操作

val myVec = Vec(Seq.fill( <number of elements> ) { <data type> })

eg:

val ufix5_vec10 := Vec(Seq.fill(10) { UInt(5.W) })

定义一个向量寄存器:

val reg_vec32 = Reg(Vec(Seq.fill(32){ UInt() }))

定义一个16位加法器(向量的值包含实例时,我们用io来定义):

val FullAdders = 
  Vec(Seq.fill(16){ Module(new FullAdder()).io })

为了使代码简洁,我们可以用下面这种写法:

val initValues = Seq.fill(4) { 0.U(8.W) }
  val delays = RegInit(Vec(initValues))
<think> 我们正在讨论Chisel硬件构建语言。用户询问关于`val``vec`的使用说明及相关特性。 根据Chisel的语法: 1. `val`是Scala的关键字(Chisel基于Scala),用于声明一个不可变的变量(即赋值后不可更改)。 在Chisel中,我们使用`val`来声明硬件组件,例如寄存器、线网等,因为硬件连接一旦建立就不应改变。 例如:`val myReg = Reg(UInt(8.W))` 声明了一个8位的寄存器2. `Vec`是Chisel中用于创建硬件向量(数组)的类。它允许我们创建一组相同类型的硬件元素,并可以通过索引访问。 `Vec`有两种主要创建方式: a. 使用`VecInit`创建并初始化一个向量,例如:`val myVec = VecInit(1.U, 2.U, 3.U)`。 b. 使用`Vec`创建未初始化的向量,通常在需要后续赋值时使用,例如:`val myVec = Vec(3, UInt(8.W))`,然后可以通过`myVec(0) := 1.U`等方式赋值。 注意:`Vec`是一个硬件向量,与Scala的集合(如Seq、List)不同,它代表的是硬件中的一组信号。 另外,`Vec`支持多种操作,包括索引(使用`apply`方法)、映射、缩减等,但要注意这些操作在硬件生成时的语义。 我们还需要注意,`Vec`通常用于需要动态索引的情况(在生成硬件时索引是静态可确定的,除非在`when`等条件语句中使用动态索引,此时需要用到`Vec`的特性)。在Chisel中,如果索引是静态的(即编译时固定),通常使用Scala的集合配合`for`循环生成硬件会更简单;而如果需要运行时动态索引,则必须使用`Vec`。 下面给出一些代码示例: 示例1:使用`Vec`创建一个寄存器文件(一组寄存器) ```scala val regFile = Vec(32, Reg(UInt(32.W))) // 使用时,例如读取索引为i的寄存器:regFile(i) ``` 示例2:使用`VecInit`创建一个常数向量 ```scala val constVec = VecInit(Seq.tabulate(4)(i => (i*2).U)) // 生成的硬件向量为:[0, 2, 4, 6](每个元素是UInt类型) ``` 示例3:使用`Vec`实现多路选择器(MUX tree) ```scala val inputs = VecInit(Seq(1.U, 2.U, 3.U, 4.U)) val sel = Wire(UInt(2.W)) val output = inputs(sel) // 根据sel选择4个输入中的一个 ``` 另外,`Vec`还可以用于创建二维数组(向量向量),但要注意访问时的硬件成本。 关于`val`,还需要注意在Chisel中,我们几乎总是使用`val`来声明硬件,因为硬件一旦构建就不应改变其引用(但硬件内部的值可以改变,如寄存器)。避免使用`var`,因为它可能会引起重新赋值,导致硬件结构改变,而这是不允许的。 总结: - `val`:声明不可变的变量(引用不可变),用于绑定硬件组件。 - `Vec`:硬件向量,用于创建一组硬件信号,支持索引访问。 根据要求,我们还需要生成相关问题。由于用户的问题是关于Chisel中`val``vec`的,我们可以围绕Chisel的其他基本概念、与`Vec`相关的操作、以及设计中的常见用法来提问。 生成相关问题: 1. Chisel中除了`Vec`,还有哪些集合类型可以使用? 2. 如何在Chisel中实现动态索引访问? 3. `Vec`Scala的`Array`或`List`有什么区别? 4. 在Chisel中,`val``var`有什么区别?为什么推荐使用`val`? </think> ### Chisel中的`val`与`Vec`详解 #### 一、`val`关键字 1. **基本定义** `val`是Scala的关键字(Chisel基于Scala),用于声明**不可变引用**的硬件组件或中间值: ```scala val myReg = Reg(UInt(8.W)) // 声明一个8位寄存器 ``` - 特点:绑定后无法重新赋值(如`myReg = 0.U`会报错) - 用途:声明寄存器、线网、模块实例等硬件组件[^1] 2. **硬件生成规则** - 非硬件类型(如`Int`)的`val`不会生成硬件 - 硬件类型(如`Reg`/`Wire`)的`val`会生成对应电路元件 ```scala val softwareVal = 42 // 仅软件值,无硬件 val hardwareVal = Wire(Bool()) // 生成硬件线网 ``` #### 二、`Vec`集合类型 1. **核心特性** `Vec`是Chisel的**硬件向量容器**,用于创建索引化的硬件组件集合: ```scala val vecReg = VecInit(Seq.fill(4)(RegInit(0.U(8.W)))) // 4个8位寄存器 ``` - 索引访问:`vecReg(0) := 1.U`(支持动态索引) - 类型安全:所有元素必须为相同硬件类型 2. **创建方式** | 方法 | 示例 | 用途 | |--------------------|--------------------------------------|--------------------------| | `VecInit` | `VecInit(1.U, 2.U, 3.U)` | 初始化固定值 | | `Vec` + 生成器 | `Vec(5, {Reg(Bool())})` | 未初始化向量 | | `Vec.tabulate` | `Vec.tabulate(4)(i => i.U)` | 索引依赖初始化 | 3. **动态索引示例** ```scala val index = Wire(UInt(2.W)) index := 3.U val selected = vecReg(index) // 硬件多路选择器 ``` #### 三、联合使用场景 1. **寄存器堆实现** ```scala val regFile = Vec(32, Reg(UInt(32.W))) // 32个32寄存器 when(writeEnable) { regFile(writeAddr) := writeData // 写端口 } ``` 2. **多路复用结构** ```scala val inputs = VecInit(Seq(a, b, c, d)) val sel = Wire(UInt(2.W)) val output = inputs(sel) // 4选1 MUX ``` #### 四、注意事项 1. **`Vec` vs Scala集合** - `Vec`生成硬件电路,Scala的`Array`/`List`仅用于生成期配置 - 错误示例:`val v = List(Reg(UInt()))`(不生成预期硬件) 2. **参数化设计** 通过参数化定义向量维度: ```scala class ParamVector(n: Int) extends Module { val io = IO(new Bundle { val out = Output(Vec(n, UInt(8.W))) }) val vec = VecInit(Seq.tabulate(n)(i => i.U)) io.out := vec } ``` 3. **混合向量类型** 需封装为`Bundle`: ```scala class MixedBundle extends Bundle { val data = UInt(8.W) val valid = Bool() } val mixedVec = Vec(4, new MixedBundle) ``` ### 常见用例总结 | 场景 | `val`用法 | `Vec`用法 | |--------------------|----------------------------|-------------------------------| | 单寄存器 | `val reg = Reg(UInt())` | - | | 寄存器数组 | `val vec = Vec(...)` | `Vec(n, Reg(...))` | | 常量查找表 | - | `VecInit(Seq(0.U, 1.U, ...))` | | 动态多路选择 | `val sel = Wire(UInt())` | `inputs(sel)` |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值