Verified Commit 3fcce836 authored by Minyong Li's avatar Minyong Li 💬
Browse files

core.{ProgramMemory{,Test},ControlWord}: refinements

- add a companion object for less verbose cw decl
- add a read port for pm
- test improvements
- naming improvements
parent 33283b63
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.core
import chisel3._
......@@ -6,13 +9,18 @@ class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle {
val immediate = UInt(immWidth.W)
val absoluteBranch = Bool()
val relativeBranch = Bool()
val ramAddressRead = Vec(2, UInt(addrWidth.W))
val ramAddressWrite = UInt(addrWidth.W)
val ramReadAddress = Vec(2, UInt(addrWidth.W))
val ramWriteAddress = UInt(addrWidth.W)
val fillConstant = Bool()
val incrementBlockCount = Bool()
val roundLoop = Bool()
val addFrom = Bool()
val xorFrom = Bool()
val writeBackInit = Bool()
val writeBackRound = Bool()
val writeBackFromInit = Bool()
val writeBackFromRound = Bool()
}
object ControlWord {
def apply(addrWidth: Int, immWidth: Int = 8) =
new ControlWord(addrWidth, immWidth)
}
......@@ -8,7 +8,7 @@ import chisel3._
class ProgramMemory(
addrWidth: Int,
cwWidth: Int,
size: Int,
nWords: Int,
syncMem: Boolean = true
) extends MultiIOModule {
val br = IO(new Bundle {
......@@ -17,20 +17,21 @@ class ProgramMemory(
val addr = Input(UInt(addrWidth.W))
})
val cw = IO(Output(UInt(cwWidth.W)))
val read = IO(new Bundle {
val addr = Input(UInt(addrWidth.W))
val data = Output(UInt(cwWidth.W))
})
val write = IO(new Bundle {
val en = Input(Bool())
val addr = Input(UInt(addrWidth.W))
val data = Input(UInt(cwWidth.W))
})
val mem =
if (syncMem) SyncReadMem(size, UInt(cwWidth.W))
else Mem(size, UInt(cwWidth.W))
val pc = RegInit(0.U(addrWidth.W))
when(write.en) {
mem(write.addr) := write.data
}
private val mem =
if (syncMem) SyncReadMem(nWords, UInt(cwWidth.W))
else Mem(nWords, UInt(cwWidth.W))
private val pc = RegInit(0.U(addrWidth.W))
when(br.abs) {
pc := br.addr.asUInt()
......@@ -41,4 +42,10 @@ class ProgramMemory(
}
cw := mem(pc)
read.data := mem(read.addr)
when(write.en) {
mem(write.addr) := write.data
}
}
......@@ -7,28 +7,27 @@ import org.scalatest._
import chiseltest._
import chisel3._
import scala.util.Random
import scala.math.pow
class ProgramMemoryTest extends FlatSpec with ChiselScalatestTester {
private val addrWidth = 8
private val cwWidth = 16
private val memMap = Seq(
"h00".U(addrWidth.W) -> "h0000".U(cwWidth.W),
"h01".U(addrWidth.W) -> "h1234".U(cwWidth.W),
"h02".U(addrWidth.W) -> "h2468".U(cwWidth.W),
"h03".U(addrWidth.W) -> "h369a".U(cwWidth.W),
"h04".U(addrWidth.W) -> "h4c2d".U(cwWidth.W),
"h05".U(addrWidth.W) -> "h59f7".U(cwWidth.W),
"h06".U(addrWidth.W) -> "h6efc".U(cwWidth.W),
"h07".U(addrWidth.W) -> "h7fff".U(cwWidth.W)
private val immWidth = 8
private val cwWidth = ControlWord(addrWidth, immWidth).getWidth
private val memMap = Seq.fill(8)(
Random
.nextInt(pow(2, cwWidth).toInt - 1)
.asUInt(cwWidth.W)
)
private val size = memMap.length
private val nWords = memMap.length
private def initMemory(pm: ProgramMemory): Unit = {
private def initMemory(pm: ProgramMemory) {
pm.reset.poke(true.B)
pm.write.en.poke(true.B)
memMap.foreach { m =>
pm.write.addr.poke(m._1)
pm.write.data.poke(m._2)
memMap.zipWithIndex.foreach { case (data, addr) =>
pm.write.addr.poke(addr.U(addrWidth.W))
pm.write.data.poke(data)
pm.clock.step()
}
......@@ -39,114 +38,114 @@ class ProgramMemoryTest extends FlatSpec with ChiselScalatestTester {
behavior of "The Program Memory"
it should "be writable and readable as PC increments" in {
test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
c.br.abs.poke(false.B)
c.br.rel.poke(false.B)
c.br.addr.poke(0.U(addrWidth.W))
c.br.addr.poke(0.U(immWidth.W))
initMemory(c)
c.cw.expect("h0000".U(cwWidth.W))
c.cw.expect(memMap.head)
c.clock.step()
// NOTE: FPGA block RAM is synchronous-read. At this moment a new value
// has been fetched, but this 1-cycle delay exists because it hasn't been
// stored into the read register yet.
c.cw.expect("h0000".U(cwWidth.W))
c.cw.expect(memMap.head)
c.clock.step()
memMap.takeRight(memMap.length - 1).foreach { m =>
c.cw.expect(m._2)
memMap.tail.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
test(new ProgramMemory(addrWidth, cwWidth, size, false)) { c =>
test(new ProgramMemory(addrWidth, cwWidth, nWords, false)) { c =>
c.br.abs.poke(false.B)
c.br.rel.poke(false.B)
c.br.addr.poke(0.U(addrWidth.W))
c.br.addr.poke(0.U(immWidth.W))
initMemory(c)
memMap.foreach { m =>
c.cw.expect(m._2)
memMap.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
}
it should "do relative branching correctly" in {
test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
c.br.abs.poke(false.B)
c.br.rel.poke(false.B)
c.br.addr.poke(0.U(addrWidth.W))
c.br.addr.poke(0.U(immWidth.W))
initMemory(c)
c.cw.expect("h0000".U(cwWidth.W))
c.cw.expect(memMap.head)
c.clock.step()
memMap.take(3).foreach { m =>
c.cw.expect(m._2)
memMap.take(3).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
// @ 0x03 -> 0x369a
c.cw.expect("h369a".U(cwWidth.W))
// @ 0x03
c.cw.expect(memMap(3))
// > 0x06 -> 0x6efc
// > 0x06
// NOTE: Because of the synchronous BRAM, a 1-cycle delay slot is
// introduced. At this moment 0x04 has been fetched, so the offset should
// be calculated based on 0x04 rather than 0x03. Here 4 + 2 = 6.
c.br.addr.poke(2.U(addrWidth.W))
c.br.addr.poke(2.U(immWidth.W))
c.br.rel.poke(true.B)
c.clock.step()
c.br.rel.poke(false.B)
// 0x04 is now present, but 0x06 has been fetched
c.cw.expect("h4c2d".U(cwWidth.W))
c.cw.expect(memMap(4))
c.clock.step()
// @ 0x06 now
memMap.takeRight(2).foreach { m =>
c.cw.expect(m._2)
memMap.takeRight(2).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
}
it should "do absolute branching correctly" in {
test(new ProgramMemory(addrWidth, cwWidth, size)) { c =>
test(new ProgramMemory(addrWidth, cwWidth, nWords)) { c =>
c.br.abs.poke(false.B)
c.br.rel.poke(false.B)
c.br.addr.poke(0.U(addrWidth.W))
c.br.addr.poke(0.U(immWidth.W))
initMemory(c)
c.cw.expect("h0000".U(cwWidth.W))
c.cw.expect(memMap.head)
c.clock.step()
memMap.take(5).foreach { m =>
c.cw.expect(m._2)
memMap.take(5).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
// @ 0x05 -> 0x59f7
c.cw.expect("h59f7".U(cwWidth.W))
// @ 0x05
c.cw.expect(memMap(5))
// > 0x01 -> 0x1234
c.br.addr.poke("h01".U(addrWidth.W))
// > 0x01
c.br.addr.poke("h01".U(immWidth.W))
c.br.abs.poke(true.B)
c.clock.step()
c.br.abs.poke(false.B)
// Delay slot: 0x06 will present no matter what
c.cw.expect("h6efc".U(cwWidth.W))
c.cw.expect(memMap(6))
c.clock.step()
// @ 0x01 now
memMap.takeRight(memMap.length - 1).foreach { m =>
c.cw.expect(m._2)
memMap.tail.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment