Verified Commit 3bd8edb2 authored by Minyong Li's avatar Minyong Li 💬
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
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// SPDX-License-Identifier: CERN-OHL-W-2.0
package uk.ac.soton.ecs.can
import core.CanCoreConfiguration
package uk.ac.soton.ecs.can.config
case class CanConfiguration(
core: CanCoreConfiguration
......
// SPDX-FileCopyrightText: 2021 Minyong Li <ml10g20@soton.ac.uk>
// 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(
immediateWidth: Int,
programMemoryWords: Int,
dataMemoryWords: Int,
syncReadMemory: Boolean,
......
......@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core
import chisel3._
class Adder extends MultiIOModule {
val lhs = IO(Input(Vec(16, UInt(32.W))))
val rhs = IO(Input(Vec(16, UInt(32.W))))
val out = IO(Output(Vec(16, UInt(32.W))))
val lhs = IO(Input(UInt(512.W)))
val rhs = IO(Input(UInt(512.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 @@
package uk.ac.soton.ecs.can.core
import chisel3._
import uk.ac.soton.ecs.can.types.ChaCha20IETFBlock
class BlockInitializer extends MultiIOModule {
val fillConstants = IO(Input(Bool()))
val incrementBlockCount = IO(Input(Bool()))
val in = IO(Input(Vec(16, UInt(32.W))))
val out = IO(Output(Vec(16, UInt(32.W))))
val in = IO(Input(UInt(512.W)))
val out = IO(Output(UInt(512.W)))
private val constants = VecInit(
"h61707865".U(32.W),
"h3320646e".U(32.W),
"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)
}
private val _in = in.asTypeOf(new ChaCha20IETFBlock)
private val _out = Wire(new ChaCha20IETFBlock)
out := _out.asUInt()
io.slice(4, 12).foreach { case (i, o) => o := i }
private val constant = "h617078653320646e79622d326b206574".U(128.W)
io(12) match {
case (i, o) => o := Mux(incrementBlockCount, incrementedBlockCount, i)
}
io.takeRight(3).foreach { case (i, o) => o := i }
_out.constant := Mux(fillConstants, constant, _in.constant)
_out.key := _in.key
_out.blockCount := Mux(
incrementBlockCount,
_in.blockCount + 1.U,
_in.blockCount
)
_out.nonce := _in.nonce
}
......@@ -5,13 +5,15 @@ package uk.ac.soton.ecs.can.core
import chisel3._
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 {
// ========== Calculated Parameters ========== //
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 blockWidth = 512
......@@ -45,25 +47,11 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
// ========== Modules ========== //
private val programMemory = Module(
new ProgramMemory(
programMemoryAddressWidth,
controlWordWidth,
cfg.programMemoryWords,
cfg.syncReadMemory
)
)
private val dataMemory = Module(
new DataMemory(
dataMemoryAddressWidth,
blockWidth,
cfg.dataMemoryWords,
cfg.syncReadMemory
)
)
private val programMemory = Module(new ProgramMemory)
private val dataMemory = Module(new DataMemory)
private val blockInitializer = Module(new BlockInitializer)
private val columnarRound = Module(ChaChaRound.columnar)
private val diagonalRound = Module(ChaChaRound.diagonal)
private val columnarRound = Module(new ColumnarRound)
private val diagonalRound = Module(new DiagonalRound)
private val adder = Module(new Adder)
private val xorer = Module(new Xorer)
......@@ -71,28 +59,26 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
private val afterBlockInitializer =
if (cfg.regAfterBlockInitializer)
Reg(Vec(16, UInt(32.W)))
Reg(UInt(512.W))
else
Wire(Vec(16, UInt(32.W)))
Wire(UInt(512.W))
private val betweenRounds =
if (cfg.regBetweenRounds)
Reg(Vec(16, UInt(32.W)))
Reg(UInt(512.W))
else
Wire(Vec(16, UInt(32.W)))
private val afterRounds = Reg(Vec(16, UInt(32.W)))
Wire(UInt(512.W))
private val afterRounds = Reg(UInt(512.W))
private val afterAdder =
if (cfg.regAfterAdder)
Reg(Vec(16, UInt(32.W)))
Reg(UInt(512.W))
else
Wire(Vec(16, UInt(32.W)))
private val afterXorer = Wire(Vec(16, UInt(32.W)))
Wire(UInt(512.W))
private val afterXorer = Wire(UInt(512.W))
// ========== Buses (Port Aliases) ========== //
private val ctrl =
programMemory.cw.asTypeOf(ControlWord(programMemoryAddressWidth))
private val data =
dataMemory.read.map(_.data.asTypeOf(Vec(16, UInt(32.W))))
private val ctrl = programMemory.cw.asTypeOf(new CanCoreControlWord)
private val data = dataMemory.read.map(_.data)
// ========== Multiplexers ========== //
......@@ -113,22 +99,22 @@ class CanCore(implicit cfg: CanCoreConfiguration) extends MultiIOModule {
programMemory.read <> io.programMemory.read
programMemory.write <> io.programMemory.write
dataMemory.read(0).addr := ctrl.ramReadAddress(0)
dataMemory.read(0).addr := ctrl.dataMemoryReadAddress(0)
dataMemory.read(1).addr := Mux(
io.dataMemory.take,
io.dataMemory.read.addr,
ctrl.ramReadAddress(1)
ctrl.dataMemoryReadAddress(1)
)
io.dataMemory.read.data := dataMemory.read(1).data
dataMemory.write.en := Mux(
io.dataMemory.take,
io.dataMemory.write.en,
ctrl.ramWriteEnable
ctrl.dataMemoryWriteEnable
)
dataMemory.write.addr := Mux(
io.dataMemory.take,
io.dataMemory.write.addr,
ctrl.ramWriteAddress
ctrl.dataMemoryWriteAddress
)
dataMemory.write.data := Mux(
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 @@
package uk.ac.soton.ecs.can.core
import chisel3._
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)
class CombinationalQuarterRound extends BaseQuarterRound {
private val a0 = in(0)
private val b0 = in(1)
private val c0 = in(2)
......
......@@ -4,31 +4,32 @@
package uk.ac.soton.ecs.can.core
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(
Vec(
2,
new Bundle {
val addr = Input(UInt(addrWidth.W))
val data = Output(UInt(dataWidth.W))
val data = Output(UInt(512.W))
}
)
)
val write = IO(new Bundle {
val en = Input(Bool())
val addr = Input(UInt(addrWidth.W))
val data = Input(UInt(dataWidth.W))
val data = Input(UInt(512.W))
})
private val mem =
if (syncMem) SyncReadMem(size, UInt(dataWidth.W))
else Mem(size, UInt(dataWidth.W))
if (cfg.syncReadMemory)
SyncReadMem(cfg.dataMemoryWords, UInt(512.W))
else
Mem(cfg.dataMemoryWords, UInt(512.W))
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 @@
package uk.ac.soton.ecs.can.core
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 abs = Input(Bool())
val rel = Input(Bool())
......@@ -29,8 +30,11 @@ class ProgramMemory(
})
private val mem =
if (syncMem) SyncReadMem(nWords, UInt(cwWidth.W))
else Mem(nWords, UInt(cwWidth.W))
if (cfg.syncReadMemory)
SyncReadMem(cfg.programMemoryWords, UInt(cwWidth.W))
else
Mem(cfg.programMemoryWords, UInt(cwWidth.W))
private val pc = RegInit(0.U(addrWidth.W))
when(br.abs) {
......
......@@ -6,9 +6,9 @@ package uk.ac.soton.ecs.can.core
import chisel3._
class Xorer extends MultiIOModule {
val lhs = IO(Input(Vec(16, UInt(32.W))))
val rhs = IO(Input(Vec(16, UInt(32.W))))
val out = IO(Output(Vec(16, UInt(32.W))))
val lhs = IO(Input(UInt(512.W)))
val rhs = IO(Input(UInt(512.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
package uk.ac.soton.ecs.can.types
import chisel3._
import chisel3.util.log2Ceil
import uk.ac.soton.ecs.can.config.CanCoreConfiguration
class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle {
val immediate = UInt(immWidth.W)
class CanCoreControlWord(implicit val cfg: CanCoreConfiguration)
extends Bundle {
private val dataMemoryAddrWidth = log2Ceil(cfg.dataMemoryWords)
val immediate = UInt(cfg.immediateWidth.W)
val absoluteBranch = Bool()
val relativeBranch = Bool()
val ramReadAddress = Vec(2, UInt(addrWidth.W))
val ramWriteEnable = Bool()
val ramWriteAddress = UInt(addrWidth.W)
val dataMemoryReadAddress = Vec(2, UInt(dataMemoryAddrWidth.W))
val dataMemoryWriteEnable = Bool()
val dataMemoryWriteAddress = UInt(dataMemoryAddrWidth.W)
val fillConstants = Bool()
val incrementBlockCount = Bool()
val roundLoop = Bool()
......@@ -20,8 +25,3 @@ class ControlWord(addrWidth: Int, immWidth: Int = 8) extends Bundle {
val writeBackFromInit = 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._