Skip to content
Snippets Groups Projects
Verified Commit 3bd8edb2 authored by Minyong Li's avatar Minyong Li :speech_balloon:
Browse files

can: refactor

This commit includes a lot of damages:

- change to a more structural pkg hierarchy: config, core, types
- refactor some modules to use a more OOP paradigm
- further integrate implicit cfg to modules
- add imm width to config
- change all modules to accept UInt, which makes all data paths in
  core UInt(512.W)
- temporary remove all test cases because of the changes above;
  they need to be rewritten
- maybe more
parent 84ac4971
No related branches found
No related tags found
No related merge requests found
Showing
with 180 additions and 421 deletions
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk> // SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0 // SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can package uk.ac.soton.ecs.can.config
import core.CanCoreConfiguration
case class CanConfiguration( case class CanConfiguration(
core: CanCoreConfiguration core: CanCoreConfiguration
......
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk> // SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0 // SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.config
case class CanCoreConfiguration( case class CanCoreConfiguration(
immediateWidth: Int,
programMemoryWords: Int, programMemoryWords: Int,
dataMemoryWords: Int, dataMemoryWords: Int,
syncReadMemory: Boolean, syncReadMemory: Boolean,
......
...@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core ...@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
class Adder extends MultiIOModule { class Adder extends MultiIOModule {
val lhs = IO(Input(Vec(16, UInt(32.W)))) val lhs = IO(Input(UInt(512.W)))
val rhs = IO(Input(Vec(16, UInt(32.W)))) val rhs = IO(Input(UInt(512.W)))
val out = IO(Output(Vec(16, UInt(32.W)))) val out = IO(Output(UInt(512.W)))
out := lhs.zip(rhs).map { case (lhs, rhs) => lhs + rhs } out := lhs + rhs
} }
// 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._
abstract class BaseQuarterRound extends MultiIOModule {
val in = IO(Input(Vec(4, UInt(32.W))))
val out = IO(Output(Vec(4, UInt(32.W))))
protected def rotateLeft(v: UInt, b: Int): UInt =
v(31 - b, 0) ## v(31, 32 - b)
}
// 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._
abstract class BaseRound extends MultiIOModule {
val in = IO(Input(UInt(512.W)))
val out = IO(Output(UInt(512.W)))
protected val _in = in.asTypeOf(Vec(16, UInt(32.W)))
protected val _out = Wire(Vec(16, UInt(32.W)))
out := _out.asUInt()
protected def wire(wireBox: Seq[Seq[Int]]): Unit = wireBox.foreach {
wireSeq =>
val quarterRound = Module(new CombinationalQuarterRound)
quarterRound.in.zip(quarterRound.out).zip(wireSeq).foreach {
case ((i, o), w) =>
i := _in(w)
_out(w) := o
}
}
}
...@@ -4,32 +4,26 @@ ...@@ -4,32 +4,26 @@
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
import uk.ac.soton.ecs.can.types.ChaCha20IETFBlock
class BlockInitializer extends MultiIOModule { class BlockInitializer extends MultiIOModule {
val fillConstants = IO(Input(Bool())) val fillConstants = IO(Input(Bool()))
val incrementBlockCount = IO(Input(Bool())) val incrementBlockCount = IO(Input(Bool()))
val in = IO(Input(Vec(16, UInt(32.W)))) val in = IO(Input(UInt(512.W)))
val out = IO(Output(Vec(16, UInt(32.W)))) val out = IO(Output(UInt(512.W)))
private val constants = VecInit( private val _in = in.asTypeOf(new ChaCha20IETFBlock)
"h61707865".U(32.W), private val _out = Wire(new ChaCha20IETFBlock)
"h3320646e".U(32.W), out := _out.asUInt()
"h79622d32".U(32.W),
"h6b206574".U(32.W)
)
private val incrementedBlockCount = in(12) + 1.U(32.W)
private val io = in.zip(out)
io.take(4).zip(constants).foreach { case ((i, o), c) =>
o := Mux(fillConstants, c, i)
}
io.slice(4, 12).foreach { case (i, o) => o := i } private val constant = "h617078653320646e79622d326b206574".U(128.W)
io(12) match { _out.constant := Mux(fillConstants, constant, _in.constant)
case (i, o) => o := Mux(incrementBlockCount, incrementedBlockCount, i) _out.key := _in.key
} _out.blockCount := Mux(
incrementBlockCount,
io.takeRight(3).foreach { case (i, o) => o := i } _in.blockCount + 1.U,
_in.blockCount
)
_out.nonce := _in.nonce
} }
...@@ -5,13 +5,15 @@ package uk.ac.soton.ecs.can.core ...@@ -5,13 +5,15 @@ package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
import chisel3.util.log2Ceil import chisel3.util.log2Ceil
import uk.ac.soton.ecs.can.config.CanCoreConfiguration
import uk.ac.soton.ecs.can.types.CanCoreControlWord
class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule { class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
// ========== Calculated Parameters ========== // // ========== Calculated Parameters ========== //
private val programMemoryAddressWidth = log2Ceil(cfg.programMemoryWords) private val programMemoryAddressWidth = log2Ceil(cfg.programMemoryWords)
private val controlWordWidth = ControlWord(programMemoryAddressWidth).getWidth private val controlWordWidth = (new CanCoreControlWord).getWidth
private val dataMemoryAddressWidth = log2Ceil(cfg.dataMemoryWords) private val dataMemoryAddressWidth = log2Ceil(cfg.dataMemoryWords)
private val blockWidth = 512 private val blockWidth = 512
...@@ -45,25 +47,11 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule { ...@@ -45,25 +47,11 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
// ========== Modules ========== // // ========== Modules ========== //
private val programMemory = Module( private val programMemory = Module(new ProgramMemory)
new ProgramMemory( private val dataMemory = Module(new DataMemory)
programMemoryAddressWidth,
controlWordWidth,
cfg.programMemoryWords,
cfg.syncReadMemory
)
)
private val dataMemory = Module(
new DataMemory(
dataMemoryAddressWidth,
blockWidth,
cfg.dataMemoryWords,
cfg.syncReadMemory
)
)
private val blockInitializer = Module(new BlockInitializer) private val blockInitializer = Module(new BlockInitializer)
private val columnarRound = Module(ChaChaRound.columnar) private val columnarRound = Module(new ColumnarRound)
private val diagonalRound = Module(ChaChaRound.diagonal) private val diagonalRound = Module(new DiagonalRound)
private val adder = Module(new Adder) private val adder = Module(new Adder)
private val xorer = Module(new Xorer) private val xorer = Module(new Xorer)
...@@ -71,28 +59,26 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule { ...@@ -71,28 +59,26 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
private val afterBlockInitializer = private val afterBlockInitializer =
if (cfg.regAfterBlockInitializer) if (cfg.regAfterBlockInitializer)
Reg(Vec(16, UInt(32.W))) Reg(UInt(512.W))
else else
Wire(Vec(16, UInt(32.W))) Wire(UInt(512.W))
private val betweenRounds = private val betweenRounds =
if (cfg.regBetweenRounds) if (cfg.regBetweenRounds)
Reg(Vec(16, UInt(32.W))) Reg(UInt(512.W))
else else
Wire(Vec(16, UInt(32.W))) Wire(UInt(512.W))
private val afterRounds = Reg(Vec(16, UInt(32.W))) private val afterRounds = Reg(UInt(512.W))
private val afterAdder = private val afterAdder =
if (cfg.regAfterAdder) if (cfg.regAfterAdder)
Reg(Vec(16, UInt(32.W))) Reg(UInt(512.W))
else else
Wire(Vec(16, UInt(32.W))) Wire(UInt(512.W))
private val afterXorer = Wire(Vec(16, UInt(32.W))) private val afterXorer = Wire(UInt(512.W))
// ========== Buses (Port Aliases) ========== // // ========== Buses (Port Aliases) ========== //
private val ctrl = private val ctrl = programMemory.cw.asTypeOf(new CanCoreControlWord)
programMemory.cw.asTypeOf(ControlWord(programMemoryAddressWidth)) private val data = dataMemory.read.map(_.data)
private val data =
dataMemory.read.map(_.data.asTypeOf(Vec(16, UInt(32.W))))
// ========== Multiplexers ========== // // ========== Multiplexers ========== //
...@@ -113,22 +99,22 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule { ...@@ -113,22 +99,22 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
programMemory.read <> io.programMemory.read programMemory.read <> io.programMemory.read
programMemory.write <> io.programMemory.write programMemory.write <> io.programMemory.write
dataMemory.read(0).addr := ctrl.ramReadAddress(0) dataMemory.read(0).addr := ctrl.dataMemoryReadAddress(0)
dataMemory.read(1).addr := Mux( dataMemory.read(1).addr := Mux(
io.dataMemory.take, io.dataMemory.take,
io.dataMemory.read.addr, io.dataMemory.read.addr,
ctrl.ramReadAddress(1) ctrl.dataMemoryReadAddress(1)
) )
io.dataMemory.read.data := dataMemory.read(1).data io.dataMemory.read.data := dataMemory.read(1).data
dataMemory.write.en := Mux( dataMemory.write.en := Mux(
io.dataMemory.take, io.dataMemory.take,
io.dataMemory.write.en, io.dataMemory.write.en,
ctrl.ramWriteEnable ctrl.dataMemoryWriteEnable
) )
dataMemory.write.addr := Mux( dataMemory.write.addr := Mux(
io.dataMemory.take, io.dataMemory.take,
io.dataMemory.write.addr, io.dataMemory.write.addr,
ctrl.ramWriteAddress ctrl.dataMemoryWriteAddress
) )
dataMemory.write.data := Mux( dataMemory.write.data := Mux(
io.dataMemory.take, io.dataMemory.take,
......
// 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._
class ChaChaRound(wires: Seq[Seq[Int]]) extends MultiIOModule {
val in = IO(Input(Vec(16, UInt(32.W))))
val out = IO(Output(Vec(16, UInt(32.W))))
wires.foreach { qRWires =>
val quarterRound = Module(new QuarterRound)
qRWires.zipWithIndex.foreach { case (qRWire, index) =>
quarterRound.in(index) := in(qRWire)
out(qRWire) := quarterRound.out(index)
}
}
}
object ChaChaRound {
def columnar = new ChaChaRound(
Seq(
Seq(0, 4, 8, 12),
Seq(1, 5, 9, 13),
Seq(2, 6, 10, 14),
Seq(3, 7, 11, 15)
)
)
def diagonal = new ChaChaRound(
Seq(
Seq(0, 5, 10, 15),
Seq(1, 6, 11, 12),
Seq(2, 7, 8, 13),
Seq(3, 4, 9, 14)
)
)
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.core
class ColumnarRound extends BaseRound {
wire(
Seq(
Seq(0, 4, 8, 12),
Seq(1, 5, 9, 13),
Seq(2, 6, 10, 14),
Seq(3, 7, 11, 15)
)
)
}
...@@ -3,15 +3,7 @@ ...@@ -3,15 +3,7 @@
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.core
import chisel3._ class CombinationalQuarterRound extends BaseQuarterRound {
class QuarterRound extends MultiIOModule {
val in = IO(Input(Vec(4, UInt(32.W))))
val out = IO(Output(Vec(4, UInt(32.W))))
private def rotateLeft(v: UInt, b: Int): UInt =
v(31 - b, 0) ## v(31, 32 - b)
private val a0 = in(0) private val a0 = in(0)
private val b0 = in(1) private val b0 = in(1)
private val c0 = in(2) private val c0 = in(2)
......
...@@ -4,31 +4,32 @@ ...@@ -4,31 +4,32 @@
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
import chisel3.util.log2Ceil
import uk.ac.soton.ecs.can.config.CanCoreConfiguration
class DataMemory(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
private val addrWidth = log2Ceil(cfg.dataMemoryWords)
class DataMemory(
addrWidth: Int,
dataWidth: Int,
size: Int,
syncMem: Boolean
) extends MultiIOModule {
val read = IO( val read = IO(
Vec( Vec(
2, 2,
new Bundle { new Bundle {
val addr = Input(UInt(addrWidth.W)) val addr = Input(UInt(addrWidth.W))
val data = Output(UInt(dataWidth.W)) val data = Output(UInt(512.W))
} }
) )
) )
val write = IO(new Bundle { val write = IO(new Bundle {
val en = Input(Bool()) val en = Input(Bool())
val addr = Input(UInt(addrWidth.W)) val addr = Input(UInt(addrWidth.W))
val data = Input(UInt(dataWidth.W)) val data = Input(UInt(512.W))
}) })
private val mem = private val mem =
if (syncMem) SyncReadMem(size, UInt(dataWidth.W)) if (cfg.syncReadMemory)
else Mem(size, UInt(dataWidth.W)) SyncReadMem(cfg.dataMemoryWords, UInt(512.W))
else
Mem(cfg.dataMemoryWords, UInt(512.W))
read.foreach(p => p.data := mem(p.addr)) read.foreach(p => p.data := mem(p.addr))
......
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.core
class DiagonalRound extends BaseRound {
wire(
Seq(
Seq(0, 5, 10, 15),
Seq(1, 6, 11, 12),
Seq(2, 7, 8, 13),
Seq(3, 4, 9, 14)
)
)
}
...@@ -4,13 +4,14 @@ ...@@ -4,13 +4,14 @@
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
import chisel3.util.log2Ceil
import uk.ac.soton.ecs.can.types.CanCoreControlWord
import uk.ac.soton.ecs.can.config.CanCoreConfiguration
class ProgramMemory(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
private val addrWidth = log2Ceil(cfg.programMemoryWords)
private val cwWidth = (new CanCoreControlWord).getWidth
class ProgramMemory(
addrWidth: Int,
cwWidth: Int,
nWords: Int,
syncMem: Boolean
) extends MultiIOModule {
val br = IO(new Bundle { val br = IO(new Bundle {
val abs = Input(Bool()) val abs = Input(Bool())
val rel = Input(Bool()) val rel = Input(Bool())
...@@ -29,8 +30,11 @@ class ProgramMemory( ...@@ -29,8 +30,11 @@ class ProgramMemory(
}) })
private val mem = private val mem =
if (syncMem) SyncReadMem(nWords, UInt(cwWidth.W)) if (cfg.syncReadMemory)
else Mem(nWords, UInt(cwWidth.W)) SyncReadMem(cfg.programMemoryWords, UInt(cwWidth.W))
else
Mem(cfg.programMemoryWords, UInt(cwWidth.W))
private val pc = RegInit(0.U(addrWidth.W)) private val pc = RegInit(0.U(addrWidth.W))
when(br.abs) { when(br.abs) {
......
...@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core ...@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core
import chisel3._ import chisel3._
class Xorer extends MultiIOModule { class Xorer extends MultiIOModule {
val lhs = IO(Input(Vec(16, UInt(32.W)))) val lhs = IO(Input(UInt(512.W)))
val rhs = IO(Input(Vec(16, UInt(32.W)))) val rhs = IO(Input(UInt(512.W)))
val out = IO(Output(Vec(16, UInt(32.W)))) val out = IO(Output(UInt(512.W)))
out := lhs.zip(rhs).map { case (lhs, rhs) => lhs ^ rhs } out := lhs ^ rhs
} }
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk> // SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0 // SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.core package uk.ac.soton.ecs.can.types
import chisel3._ import chisel3._
import chisel3.util.log2Ceil
import uk.ac.soton.ecs.can.config.CanCoreConfiguration
class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle { class CanCoreControlWord(implicit val cfg: CanCoreConfiguration)
val immediate = UInt(immWidth.W) extends Bundle {
private val dataMemoryAddrWidth = log2Ceil(cfg.dataMemoryWords)
val immediate = UInt(cfg.immediateWidth.W)
val absoluteBranch = Bool() val absoluteBranch = Bool()
val relativeBranch = Bool() val relativeBranch = Bool()
val ramReadAddress = Vec(2, UInt(addrWidth.W)) val dataMemoryReadAddress = Vec(2, UInt(dataMemoryAddrWidth.W))
val ramWriteEnable = Bool() val dataMemoryWriteEnable = Bool()
val ramWriteAddress = UInt(addrWidth.W) val dataMemoryWriteAddress = UInt(dataMemoryAddrWidth.W)
val fillConstants = Bool() val fillConstants = Bool()
val incrementBlockCount = Bool() val incrementBlockCount = Bool()
val roundLoop = Bool() val roundLoop = Bool()
...@@ -20,8 +25,3 @@ class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle { ...@@ -20,8 +25,3 @@ class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle {
val writeBackFromInit = Bool() val writeBackFromInit = Bool()
val writeBackFromRound = Bool() val writeBackFromRound = Bool()
} }
object ControlWord {
def apply(addrWidth: Int, immWidth: Int = 8) =
new ControlWord(addrWidth, immWidth)
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can.types
import chisel3._
class ChaCha20IETFBlock extends Bundle {
val constant = UInt(128.W)
val key = UInt(256.W)
val blockCount = UInt(32.W)
val nonce = UInt(96.W)
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: GPL-3.0-or-later
package uk.ac.soton.ecs.can.core
import org.scalatest._
import chiseltest._
import chisel3._
import scala.util.Random
import scala.math.abs
class AdderTest extends FlatSpec with ChiselScalatestTester {
private val maxUInt = (Int.MaxValue.toLong << 1) | 1
behavior of "The Adder"
it should "sum the 16 32b unsigned integers" in {
test(new Adder) { c =>
val randomLhs = c.lhs.map(_ => abs(Random.nextInt))
val randomRhs = c.rhs.map(_ => abs(Random.nextInt))
val randomRes = randomLhs.zip(randomRhs).map { case (l, r) =>
(l.toLong + r.toLong) % maxUInt
}
c.lhs.zip(randomLhs).foreach { case (p, r) => p.poke(r.U) }
c.rhs.zip(randomRhs).foreach { case (p, r) => p.poke(r.U) }
c.out.zip(randomRes).foreach { case (p, r) => p.expect(r.U) }
}
}
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: GPL-3.0-or-later
package uk.ac.soton.ecs.can.core
import org.scalatest._
import chiseltest._
import chisel3._
class BlockInitializerTest extends FlatSpec with ChiselScalatestTester {
private val input = Seq.fill(16)(0.U(32.W))
behavior of "The Block Initializer"
it should "fill the ChaCha constant when requested" in {
test(new BlockInitializer) { c =>
c.in.zip(input).foreach { case (p, n) => p.poke(n) }
c.fillConstants.poke(true.B)
c.out(0).expect("h61707865".U(32.W))
c.out(1).expect("h3320646e".U(32.W))
c.out(2).expect("h79622d32".U(32.W))
c.out(3).expect("h6b206574".U(32.W))
c.out.takeRight(12).foreach(_.expect(0.U(32.W)))
c.fillConstants.poke(false.B)
c.out.foreach(_.expect(0.U(32.W)))
}
}
it should "increment the block count when requested" in {
test(new BlockInitializer) { c =>
c.in.zip(input).foreach { case (p, n) => p.poke(n) }
c.incrementBlockCount.poke(true.B)
(0 until 10).foreach { i =>
c.in(12).poke(i.U(32.W))
c.out(12).expect((i + 1).U(32.W))
}
c.in(12).poke(0.U(32.W))
c.incrementBlockCount.poke(false.B)
c.out.foreach(_.expect(0.U(32.W)))
}
}
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: GPL-3.0-or-later
package uk.ac.soton.ecs.can.core
import org.scalatest._
import chiseltest._
import chisel3._
class DataMemoryTest extends FlatSpec with ChiselScalatestTester {
private val addrWidth = 8
private val dataWidth = 16
private val size = 32
behavior of "The Data Memory"
it should "store some values" in {
test(new DataMemory(addrWidth, dataWidth, size)) { c =>
c.write.addr.poke("h01".U(addrWidth.W))
c.write.data.poke("h1234".U(dataWidth.W))
c.write.en.poke(true.B)
c.clock.step()
c.write.en.poke(false.B)
c.read.foreach(_.addr.poke("h01".U(addrWidth.W)))
c.clock.step()
c.read.foreach(_.data.expect("h1234".U(dataWidth.W)))
c.write.addr.poke("h0a".U(addrWidth.W))
c.write.data.poke("hfefe".U(dataWidth.W))
c.write.en.poke(true.B)
c.clock.step()
c.write.en.poke(false.B)
c.read.foreach(_.addr.poke("h0a".U(addrWidth.W)))
c.clock.step()
c.read.foreach(_.data.expect("hfefe".U(dataWidth.W)))
}
}
it should "not write without write enable" in {
test(new DataMemory(addrWidth, dataWidth, size)) { c =>
c.write.addr.poke("h06".U(addrWidth.W))
c.write.data.poke("hcafe".U(dataWidth.W))
c.write.en.poke(true.B)
c.clock.step()
c.write.en.poke(false.B)
c.read.foreach(_.addr.poke("h06".U(addrWidth.W)))
c.clock.step()
c.read.foreach(_.data.expect("hcafe".U(dataWidth.W)))
c.write.data.poke("hefac".U(dataWidth.W))
c.clock.step()
c.read.foreach(_.data.expect("hcafe".U(dataWidth.W)))
}
}
}
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: GPL-3.0-or-later
package uk.ac.soton.ecs.can.core
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 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 nWords = memMap.length
private def initMemory(pm: ProgramMemory) {
pm.reset.poke(true.B)
pm.write.en.poke(true.B)
memMap.zipWithIndex.foreach { case (data, addr) =>
pm.write.addr.poke(addr.U(addrWidth.W))
pm.write.data.poke(data)
pm.clock.step()
}
pm.write.en.poke(false.B)
pm.reset.poke(false.B)
}
behavior of "The Program Memory"
it should "be writable and readable as PC increments" in {
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(immWidth.W))
initMemory(c)
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(memMap.head)
c.clock.step()
memMap.tail.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
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(immWidth.W))
initMemory(c)
memMap.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
}
it should "do relative branching correctly" in {
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(immWidth.W))
initMemory(c)
c.cw.expect(memMap.head)
c.clock.step()
memMap.take(3).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
// @ 0x03
c.cw.expect(memMap(3))
// > 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(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(memMap(4))
c.clock.step()
// @ 0x06 now
memMap.takeRight(2).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
}
it should "do absolute branching correctly" in {
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(immWidth.W))
initMemory(c)
c.cw.expect(memMap.head)
c.clock.step()
memMap.take(5).foreach { data =>
c.cw.expect(data)
c.clock.step()
}
// @ 0x05
c.cw.expect(memMap(5))
// > 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(memMap(6))
c.clock.step()
// @ 0x01 now
memMap.tail.foreach { data =>
c.cw.expect(data)
c.clock.step()
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment