diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala b/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala index ab63be09a2ce59506588cd9606986dd3dab0b8a0..8760cb18c5661aa1826c840c40f1e76eef3d7718 100644 --- a/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala +++ b/src/main/scala/uk/ac/soton/ecs/can/core/ControlWord.scala @@ -1,3 +1,6 @@ +// 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) } diff --git a/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala b/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala index 1575d7f5444ece5e90635752980f7dd2aff237d8..f5997254dd9a39dccab1c3a7aba431683a86993a 100644 --- a/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala +++ b/src/main/scala/uk/ac/soton/ecs/can/core/ProgramMemory.scala @@ -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 + } } diff --git a/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala b/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala index 0ac5312126b0212c743b473ef70aefea71f3466b..96ffa8961ac3f5d5cb3168384766e486d7b3365a 100644 --- a/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala +++ b/src/test/scala/uk/ac/soton/ecs/can/core/ProgramMemoryTest.scala @@ -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() } }