diff --git a/npc/core/build.sbt b/npc/core/build.sbt index 792a764..16f31f3 100644 --- a/npc/core/build.sbt +++ b/npc/core/build.sbt @@ -6,10 +6,11 @@ val chiselVersion = "5.1.0" lazy val root = (project in file(".")) .settings( - name := "ChiselLearning", + name := "flow", libraryDependencies ++= Seq( "org.chipsalliance" %% "chisel" % chiselVersion, - "edu.berkeley.cs" %% "chiseltest" % "5.0.2" % "test" + "edu.berkeley.cs" %% "chiseltest" % "5.0.2" % "test", + "com.chuusai" %% "shapeless" % "2.3.3" ), scalacOptions ++= Seq( "-language:reflectiveCalls", diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/RegisterFile.scala index fbf8a94..a6c5a62 100644 --- a/npc/core/src/main/scala/RegisterFile.scala +++ b/npc/core/src/main/scala/RegisterFile.scala @@ -1,25 +1,73 @@ -package npc.util +package flowpc.components import chisel3._ +import chisel3.util.log2Ceil +import chisel3.util.UIntToOH +import chisel3.util.MuxLookup -class RegisterFile(readPorts: Int) extends Module { - require(readPorts >= 0) - val io = IO(new Bundle { - val writeEnable = Input(Bool()) - val writeAddr = Input(UInt(5.W)) - val writeData = Input(UInt(32.W)) - val readAddr = Input(Vec(readPorts, UInt(5.W))) - val readData = Output(Vec(readPorts, UInt(32.W))) - }) +class RegControl extends Bundle { + val writeEnable = Input(Bool()) - val regFile = RegInit(VecInit(Seq.fill(32)(0.U(32.W)))) - for (i <- 1 until 32) { - regFile(i) := regFile(i) + object WriteSelect extends ChiselEnum { + val rAluOut, rMemOut = Value + } + val writeSelect = Input(WriteSelect()) +} + +class RegFileData[T <: Data](size:Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { + val write = new Bundle { + val addr = Input(UInt(size.W)) + val data = Vec(numWritePorts, Input(tpe)) + } + val read = Vec(numReadPorts, new Bundle { + val rs = Input(UInt(size.W)) + val src = Output(tpe) + }) +} + +class RegFileInterface[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { + val control = new RegControl + val data = new RegFileData(size, tpe, numReadPorts, numWritePorts) +} + +class RegisterFileCore[T <: Data](size: Int, tpe: T, numReadPorts: Int) extends Module { + require(numReadPorts >= 0) + val writePort = IO(new Bundle { + val enable = Input(Bool()) + val addr = Input(UInt(log2Ceil(size).W)) + val data = Input(tpe) + }) + val readPorts = IO(Vec(numReadPorts, new Bundle { + val addr = Input(UInt(log2Ceil(size).W)) + val data = Output(tpe) + })) + + val regFile = RegInit(VecInit(Seq.fill(size)(0.U(tpe.getWidth.W)))) + val writeAddrOH = UIntToOH(writePort.addr) + for ((reg, i) <- regFile.zipWithIndex.tail) { + reg := Mux(writeAddrOH(i) && writePort.enable, writePort.data, reg) } - regFile(io.writeAddr) := Mux(io.writeEnable, io.writeData, regFile(io.writeAddr)) regFile(0) := 0.U - for (i <- 0 until readPorts) { - io.readData(i) := regFile(io.readAddr(i)) + for (readPort <- readPorts) { + readPort.data := regFile(readPort.addr) + } +} + +object RegisterFile { + def apply[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int): RegFileInterface[T] = { + val core = Module(new RegisterFileCore(size, tpe, numReadPorts)) + val _out = Wire(new RegFileInterface(size, tpe, numReadPorts, numWritePorts)) + val clock = core.clock + for (i <- 0 until numReadPorts) { + core.readPorts(i).addr := _out.data.read(i).rs + _out.data.read(i).src := core.readPorts(i).data + } + core.writePort.addr := _out.data.write.addr + core.writePort.data := MuxLookup(_out.control.writeSelect, 0.U)( + _out.control.WriteSelect.all.map(x => (x -> _out.data.write.data(x.asUInt).asUInt)) + ) + core.writePort.enable := _out.control.writeEnable + _out } } diff --git a/npc/core/src/test/scala/RegisterFile.scala b/npc/core/src/test/scala/RegisterFile.scala new file mode 100644 index 0000000..87be171 --- /dev/null +++ b/npc/core/src/test/scala/RegisterFile.scala @@ -0,0 +1,63 @@ +package flowpc + +import chisel3._ +import chiseltest._ +import org.scalatest.freespec.AnyFreeSpec +import chiseltest.simulator.WriteVcdAnnotation + +import flowpc.components._ +class RegisterFileSpec extends AnyFreeSpec with ChiselScalatestTester { + "RegisterFileCore" - { + "register 0 is always 0" in { + test(new RegisterFileCore(32, UInt(32.W), 2)) { c => + c.readPorts(0).addr.poke(0) + c.readPorts(1).addr.poke(0) + c.writePort.enable.poke(true) + c.writePort.addr.poke(0) + c.writePort.data.poke(0x1234) + + c.readPorts(0).data.expect(0) + c.readPorts(1).data.expect(0) + c.clock.step(2) + c.readPorts(0).data.expect(0) + c.readPorts(1).data.expect(0) + } + } + "register other than 0 can be written" in { + test(new RegisterFileCore(32, UInt(32.W), 2)) { c => + import scala.util.Random + val r = new Random() + for (i <- 1 until 32) { + val v = r.nextLong() & 0xFFFFFFFFL + c.readPorts(0).addr.poke(i) + c.writePort.enable.poke(true) + c.writePort.addr.poke(i) + c.writePort.data.poke(v) + + c.clock.step(1) + c.readPorts(0).data.expect(v) + } + } + } + } + "RegisterInterface" - { + "worked" in { + class Top extends Module { + val io = IO(new RegFileInterface(32, UInt(32.W), 2, 2)) + val rf = RegisterFile(32, UInt(32.W), 2, 2) + io :<>= rf + } + test(new Top).withAnnotations(Seq(WriteVcdAnnotation)) { c => + import c.io.control.WriteSelect._ + val writePort = rAluOut.litValue.toInt + c.io.control.writeEnable.poke(true) + c.io.control.writeSelect.poke(rAluOut) + c.io.data.write.addr.poke(5) + c.io.data.write.data(writePort).poke(0xcdef) + c.io.data.read(0).rs.poke(5) + c.clock.step(1) + c.io.data.read(0).src.expect(0xcdef) + } + } + } +}