Chisel 学习笔记(七)
集合
在Chisel中,我们既可以使用Scala中自带的集合,包括List,Seq,ArrayBuffer——List的使用方式在学习笔记(一)中有所体现;Seq与List类似,但是在Chisel中,我们更常使用Seq来表述某个模块的参数;ArrayBuffer的特性是不用描述出长度,且通过+就可以简单的添加元素(-可以删除元素),较为方便——也可以使用Chisel中所有的特殊集合Vec,Vec与其他Scala中的集合功能类似,但是可以更贴合Chisel。
List,Seq,ArrayBuffer
正如上文所说
- List是最原初的列表类,当然也有各种操作,不过较为繁琐
- Seq经常作为Chisel中构造函数的参数
- ArrayBuffer有着更简便的对集合的操作
List&Seq
class ScalaFirFilter(taps: Seq[Int]) {
var pseudoRegisters = List.fill(taps.length)(0) //构造一个taps.length长度的数组,将初始设为0
def poke(value: Int): Int = {
pseudoRegisters = value :: pseudoRegisters.take(taps.length - 1) //取出pseudoRegister中前taps.length-1个元素,并在列表头插入value的值
var accumulator = 0
for(i <- taps.indices) {
accumulator += taps(i) * pseudoRegisters(i) //对Seq和List中元素的访问方式一样
}
accumulator
}
}
Seq&ArrayBuffer
在Chisel中,更常用的是通过Seq传参
class MyManyElementFir(consts: Seq[Int], bitWidth: Int) extends Module { //参数为一个不定长的Seq集合
val io = IO(new Bundle {
val in = Input(UInt(bitWidth.W))
val out = Output(UInt(bitWidth.W))
})
val regs = mutable.ArrayBuffer[UInt]() //ArrayBuffer的使用,先初始化成空
for(i <- 0 until consts.length) {
if(i == 0) regs += io.in //通过加法可以很方便地添加元素到ArrayBuffer末尾
else regs += RegNext(regs(i - 1), 0.U)
}
val muls = mutable.ArrayBuffer[UInt]()
for(i <- 0 until consts.length) {
muls += regs(i) * consts(i).U
}
val scan = mutable.ArrayBuffer[UInt]()
for(i <- 0 until consts.length) {
if(i == 0) scan += muls(i)
else scan += muls(i) + scan(i - 1) //对元素的访问与Seq,List一样
}
io.out := scan.last
}
Chisel的特性——Vec
Vec也是集合,且是为Chisel专门打造的。在某些情况下,scala中的集合并不支持Chisel的操作,主要是以下两种情况:
- 在Bundle中,特别是IO Bundle中,scala中的集合不再起作用
- 在某些需要用到Chisel特性进行访问或更改的操作中,例如处理器的寄存器堆设计,scala中的集合不再起作用
Vec的使用如下
class MyManyDynamicElementVecFir(length: Int) extends Module {
val io = IO(new Bundle {
val in = Input(UInt(8.W))
val out = Output(UInt(8.W))
val consts = Input(Vec(length, UInt(8.W)))
})
val regs = RegInit(Vec.fill(length - 1)(0.U(8.W))) //对Vec的初始化和List操作类似,说明长度和初始值
//val regs = RegInit(VecInit(Seq.fill(length - 1)(0.U(8.W)))) 最新一版中的Vec初始化要这样写
for(i <- 0 until length - 1) {
if(i == 0) regs(i) := io.in //对Vec中元素的访问也和List一样
else regs(i) := regs(i - 1)
}
val muls = Wire(Vec(length, UInt(8.W))) //对一组导线的定义
for(i <- 0 until length) {
if(i == 0) muls(i) := io.in * io.consts(i)
else muls(i) := regs(i - 1) * io.consts(i)
}
val scan = Wire(Vec(length, UInt(8.W)))
for(i <- 0 until length) {
if(i == 0) scan(i) := muls(i)
else scan(i) := muls(i) + scan(i - 1)
}
io.out := scan(length - 1)
}
RISC-V中寄存器堆的实现
package Passthrough
import chisel3._
class RegisterFile (readPorts: Int) extends Module{
val io = IO(new Bundle{
val wen = Input(Bool())
val waddr = Input(UInt(5.W))
val wdata = Input(UInt(32.W))
val raddr = Input(Vec(readPorts, UInt(5.W)))
val rdata = Output(Vec(readPorts, UInt(32.W)))
})
// val regs = RegInit(Vec(32, UInt(32.W)).fromBits(0.U)) 另外一种初始化方法
val regs = RegInit(VecInit(Seq.fill(32)(0.U(32.W))))
when(io.wen & io.waddr=/=0.U){
regs(io.waddr) := io.wdata
}
for(i<-0 until readPorts){
io.rdata(i) := regs(io.raddr(i))
}
}