diff --git a/npc/core/src/main/scala/components/CSR.scala b/npc/core/src/main/scala/components/CSR.scala new file mode 100644 index 0000000..5ea795f --- /dev/null +++ b/npc/core/src/main/scala/components/CSR.scala @@ -0,0 +1,108 @@ +package flow.components + +import chisel3._ +import chisel3.util.log2Ceil +import scala.reflect.runtime.universe._ +import cats.instances.MapInstances +import dataclass.data +import chisel3.util.experimental.decode.{decoder, TruthTable} +import shapeless.HNil +import flow.Params +import chisel3.util.BitPat +import chisel3.util.Fill + +class CSRControlInterface extends Bundle { + object csrRead extends ChiselEnum { + val csrReadDisabled, csrReadEnabled = Value + } + + object csrWrite extends ChiselEnum { + val csrWriteDisabled, csrWriteEnabled = Value + } + + val readEnable = Input(csrRead()) + val writeEnable = Input(csrWrite()) + + def ctrlBindPorts = { + readEnable :: writeEnable :: HNil + } +} + +class CSRCore(implicit val p: Params) extends Module { + val control = IO(new CSRControlInterface) + + val in = IO(new Bundle { + val csrAddr = Input(UInt(p.csrAddrWidth)) + val writeData = Input(UInt(p.XLEN)) + }) + + val out = IO(new Bundle { + val readData = Output(UInt(p.XLEN)) + val readValid = Output(Bool()) + }) + + implicit class fromChiselEnumToBool[T <: EnumType](x: T) { + def B: Bool = { + x.asUInt =/= 0.U + } + } + + val nameToAddr = Map( + "mstatus" -> 0x300, + "mtvec" -> 0x305, + "mie" -> 0x304, + "mepc" -> 0x341, + "mcause" -> 0x342, + "mtval" -> 0x343, + "mip" -> 0x344 + ) + val csrSize = nameToAddr.size + + val addrToIndex = nameToAddr.zipWithIndex + .map(x => { + val (name: String, csrAddr: Int) = x._1 + val index = x._2 + + csrAddr -> index + }) + .toMap + val indexToAddr = addrToIndex.map(_.swap) + + val csrIndexWidth = log2Ceil(csrSize).W + + private val align = (x: UInt, w: Width) => BitPat(x.litValue.U(w)) + val csrIndex = decoder( + in.csrAddr, + TruthTable( + addrToIndex.map(x => + // Addr Index + (align(x._1.U, p.csrAddrWidth), align(x._2.U, csrIndexWidth)) + ), + align(addrToIndex.head._2.U, csrIndexWidth) + ) + ) + + val csrIndexValid = !( + csrIndex === BitPat(0.U) && + in.csrAddr =/= align(indexToAddr(0).U, p.csrAddrWidth) + ) + + val regs = RegInit(VecInit(Seq.fill(csrSize)(0.U(p.XLEN)))) + + val regReadValue = regs(csrIndex) + + val delayWriteData = RegNext(in.writeData, 0.U(p.XLEN)) + val delayWriteEnable = RegNext(control.writeEnable) + + when(control.writeEnable.B) { + regs(csrIndex) := delayWriteData + } + + when(control.readEnable.B) { + out.readData := regReadValue + out.readValid := true.B && csrIndexValid + } otherwise { + out.readData := 0.U(p.XLEN) + out.readValid := false.B && csrIndexValid + } +} diff --git a/npc/core/src/main/scala/top/Config.scala b/npc/core/src/main/scala/top/Config.scala index 515c080..d2f3a2e 100644 --- a/npc/core/src/main/scala/top/Config.scala +++ b/npc/core/src/main/scala/top/Config.scala @@ -14,3 +14,9 @@ import io.circe.generic.JsonCodec enableDifftest: Boolean = true, traceConfig: TraceConfig = TraceConfig() ) + +import chisel3._ +case class Params( + XLEN: Width, + csrAddrWidth: Width = 12.W +) diff --git a/npc/core/src/test/scala/CSR.scala b/npc/core/src/test/scala/CSR.scala new file mode 100644 index 0000000..35d41e3 --- /dev/null +++ b/npc/core/src/test/scala/CSR.scala @@ -0,0 +1,59 @@ +package flow.tests + +import chisel3._ +import chiseltest._ +import org.scalatest.freespec.AnyFreeSpec +import chiseltest.simulator.WriteVcdAnnotation + +import flow.components.CSRCore +import flow.tests.defaultParams + +class CSRSpec extends AnyFreeSpec with ChiselScalatestTester { + implicit val p: flow.Params = defaultParams() + "should compile" in { + test(new CSRCore) { c => + c.clock.step(1) + } + } + "Write" - { + "delayed" in { + test(new CSRCore) { c => + val tv = BigInt("deadbeef", 16) + c.in.csrAddr.poke(c.nameToAddr("mstatus")) + c.in.writeData.poke(tv) + c.control.writeEnable.poke(c.control.csrWrite.csrWriteEnabled) + c.clock.step(1) + + c.control.readEnable.poke(c.control.csrRead.csrReadEnabled) + c.out.readData.expect(0) + c.out.readValid.expect(1) + + c.clock.step(1) + c.out.readValid.expect(1) + c.out.readData.expect(tv) + } + } + } + + "Read" - { + "controlled by readEnable" in { + test(new CSRCore) { c => + val tv = BigInt("deadbeef", 16) + c.in.csrAddr.poke(c.nameToAddr("mstatus")) + c.in.writeData.poke(tv) + c.control.readEnable.poke(c.control.csrRead.csrReadEnabled) + c.control.writeEnable.poke(c.control.csrWrite.csrWriteEnabled) + c.clock.step(1) + + c.control.readEnable.poke(c.control.csrRead.csrReadDisabled) + c.out.readData.expect(0) + c.out.readValid.expect(0) + + c.clock.step(1) + c.out.readData.expect(0) + c.out.readValid.expect(0) + } + } + } + +} diff --git a/npc/core/src/test/scala/params.scala b/npc/core/src/test/scala/params.scala new file mode 100644 index 0000000..2bb752d --- /dev/null +++ b/npc/core/src/test/scala/params.scala @@ -0,0 +1,8 @@ +package flow.tests + +import chisel3._ +import flow.Params + +object defaultParams { + def apply(): Params = new Params(XLEN = 32.W) +}