0%

3.3 Generator-Collections

3.3 Generators: Collections

Parameterized FIR Filter Generator

  • Build a software Golden Model of a tap configurable FIR.
  • Redesign our test to use this model, and confirm that it works.
  • Refactor our My4ElementFir to allow an configurable number of taps.
  • Test the new circuit using our new test harness.

Golden Model

1
2
3
4
5
6
7
8
9
10
11
12
class ScalaFirFilter(taps: Seq[Int]) {
var pseudoRegisters = List.fill(taps.length)(0)

def poke(value: Int): Int = {
pseudoRegisters = value :: pseudoRegisters.take(taps.length - 1)
var accumulator = 0
for(i <- taps.indices) {
accumulator += taps(i) * pseudoRegisters(i)
}
accumulator
}
}

mutable.ArrayBuffer[]

  • We are using a Scala collection type called ArrayBuffer.
    ArrayBuffer allows you to append elements using the **+= **operator (also insert and delete, but we don’t need this).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyManyElementFir(consts: Seq[Int], bitWidth: Int) extends Module {
val io = IO(new Bundle {
val in = Input(UInt(bitWidth.W))
val out = Output(UInt(bitWidth.W))
})

val regs = mutable.ArrayBuffer[UInt]()
for(i <- 0 until consts.length) {
if(i == 0) regs += io.in // not register
else regs += RegNext(regs.last, 0.U) // register
}

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.last
}

io.out := scan.last
}
println(getVerilog(new MyManyElementFir(Seq(1, 1, 1, 1), 8)))

Vec & VecInit

Vec supports many of the scala collection methods but it can only contain Chisel hardware elements.

Vec should only be used in situations where ordinary Scala collections won’t work, basically where:

  1. You need a collection of elements in a Bundle, typically a Bundle that will be used as IO.
  2. You need to access the collection via an index that is part of the hardware (think Register File).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)))
})

// Reference solution
val regs = RegInit(VecInit(Seq.fill(length - 1)(0.U(8.W))))
// val regs = Reg(Vec(length - 1, UInt(8.W))) // ?
for(i <- 0 until length - 1) {
if(i == 0) regs(i) := io.in
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)
}

Register File

  • Writes will only be performed when wen (write enable) is asserted.

  • The register at index 0 (the first register) is always zero when you read from it, regardless of what you write to it (it’s often useful to have 0 handy).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class RegisterFile(readPorts: Int) extends Module {
require(readPorts >= 0)
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)))
})

// A Register of a vector of UInts
val reg = RegInit(VecInit(Seq.fill(32)(0.U(32.W))))

when (io.wen) {
reg(io.waddr) := io.wdata
printf(p"waddr = ${io.waddr}, wdata = ${io.wdata}, reg(${io.waddr - 1.U}) = ${reg(io.waddr - 1.U)}\n")
}

// when (io.wen === true.B) {
// for (i <- 1 until 32) {
// when (io.waddr === i.U) {
// reg(i) := io.wdata
// printf(p"waddr = ${io.waddr}, wdata = ${io.wdata}, reg(${i - 1}) = ${reg(i - 1)}\n")
// }
// }
// }//.otherwise { reg := reg }

for (i <- 0 until readPorts) {
when (io.raddr(i) === 0.U) {
io.rdata(i) := 0.U
} .otherwise {
io.rdata(i) := reg(io.raddr(i))
}
}

// for (i <- 0 until readPorts) {
// for (j <- 0 until 32) {
// when (io.raddr(i) === j.U) {
// io.rdata(i) := reg(j)
// // printf(p"raddr = ${io.raddr}, rdata = ${io.rdata}, reg(${j}) = ${reg(j)}\n")
// }.otherwise { io.rdata(i) := 0.U }
// }
// }
}