From cd8008f96945d7e49c3e9655b3d0f0f193f2908f Mon Sep 17 00:00:00 2001 From: Porterlu <1258210724@qq.com> Date: Mon, 24 Apr 2023 08:20:46 -0700 Subject: [PATCH] Implement modularization of aclint, which divide clint into mtimer and mswi. --- src/main/scala/devices/tilelink/CLINT.scala | 150 +++++++------------ src/main/scala/devices/tilelink/MSWI.scala | 140 +++++++++++++++++ src/main/scala/devices/tilelink/Mtimer.scala | 105 +++++++++++++ src/main/scala/subsystem/HasTiles.scala | 22 ++- src/main/scala/subsystem/RTC.scala | 14 +- 5 files changed, 325 insertions(+), 106 deletions(-) create mode 100644 src/main/scala/devices/tilelink/MSWI.scala create mode 100644 src/main/scala/devices/tilelink/Mtimer.scala diff --git a/src/main/scala/devices/tilelink/CLINT.scala b/src/main/scala/devices/tilelink/CLINT.scala index 8156706ee8d..d436eeac6c5 100644 --- a/src/main/scala/devices/tilelink/CLINT.scala +++ b/src/main/scala/devices/tilelink/CLINT.scala @@ -12,108 +12,62 @@ import freechips.rocketchip.subsystem._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.util._ -object CLINTConsts -{ - def msipOffset(hart: Int) = hart * msipBytes - def timecmpOffset(hart: Int) = 0x4000 + hart * timecmpBytes - def timeOffset = 0xbff8 - def msipBytes = 4 - def timecmpBytes = 8 - def size = 0x10000 - def timeWidth = 64 - def ipiWidth = 32 - def ints = 2 -} - -case class CLINTParams(baseAddress: BigInt = 0x02000000, intStages: Int = 0) -{ - def address = AddressSet(baseAddress, CLINTConsts.size-1) +// if isACLINT is false, the code will generate a clint +case class CLINTParams( + isACLINT: Boolean = false, + mtimer: Option[MTIMERParams] = Some(MTIMERParams()), + mswi: Option[MSWIParams] = Some(MSWIParams()) +){ + require(mtimer.isDefined || mswi.isDefined, "If both mtimer and mswi are empty, please directly set CLINTKey to empty") } case object CLINTKey extends Field[Option[CLINTParams]](None) -case class CLINTAttachParams( - slaveWhere: TLBusWrapperLocation = CBUS -) - -case object CLINTAttachKey extends Field(CLINTAttachParams()) - -class CLINT(params: CLINTParams, beatBytes: Int)(implicit p: Parameters) extends LazyModule -{ - import CLINTConsts._ - - // clint0 => at most 4095 devices - val device = new SimpleDevice("clint", Seq("riscv,clint0")) { - override val alwaysExtended = true - } - - val node: TLRegisterNode = TLRegisterNode( - address = Seq(params.address), - device = device, - beatBytes = beatBytes) - - val intnode : IntNexusNode = IntNexusNode( - sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(ints, Seq(Resource(device, "int"))))) }, - sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, - outputRequiresInput = false) - - lazy val module = new Impl - class Impl extends LazyModuleImp(this) { - Annotated.params(this, params) - require (intnode.edges.in.size == 0, "CLINT only produces interrupts; it does not accept them") - - val io = IO(new Bundle { - val rtcTick = Input(Bool()) - }) - - val time = RegInit(0.U(timeWidth.W)) - when (io.rtcTick) { time := time + 1.U } - - val nTiles = intnode.out.size - val timecmp = Seq.fill(nTiles) { Reg(UInt(timeWidth.W)) } - val ipi = Seq.fill(nTiles) { RegInit(0.U(1.W)) } - - val (intnode_out, _) = intnode.out.unzip - intnode_out.zipWithIndex.foreach { case (int, i) => - int(0) := ShiftRegister(ipi(i)(0), params.intStages) // msip - int(1) := ShiftRegister(time.asUInt >= timecmp(i).asUInt, params.intStages) // mtip - } - - /* 0000 msip hart 0 - * 0004 msip hart 1 - * 4000 mtimecmp hart 0 lo - * 4004 mtimecmp hart 0 hi - * 4008 mtimecmp hart 1 lo - * 400c mtimecmp hart 1 hi - * bff8 mtime lo - * bffc mtime hi - */ - - node.regmap( - 0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.flatMap{ case (r, i) => - RegField(1, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0))) :: RegField(ipiWidth - 1) :: Nil }), - timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), - RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))}, - timeOffset -> RegFieldGroup("mtime", Some("Timer Register"), - RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0), volatile=true)))) - ) - } -} - -/** Trait that will connect a CLINT to a subsystem */ +// if isACLINT is false, a clint will be generated according to base address of mswi device +// The Chisel MSWI device will implement the entirety of the CLINT in this case trait CanHavePeripheryCLINT { this: BaseSubsystem => - val clintOpt = p(CLINTKey).map { params => - val tlbus = locateTLBusWrapper(p(CLINTAttachKey).slaveWhere) - val clint = LazyModule(new CLINT(params, cbus.beatBytes)) - clint.node := tlbus.coupleTo("clint") { TLFragmenter(tlbus) := _ } - - // Override the implicit clock and reset -- could instead include a clockNode in the clint, and make it a RawModuleImp? - InModuleBody { - clint.module.clock := tlbus.module.clock - clint.module.reset := tlbus.module.reset - } - - clint + val mswiOpt = p(CLINTKey) match { + case Some(clintParams) => + { + val mswiOpt = clintParams.mswi.map { params => + val tlbus = locateTLBusWrapper(p(MSWIAttachKey).slaveWhere) + val beatBytes = tlbus.beatBytes + val mswi = LazyModule(new MSWI(params, clintParams.mtimer.getOrElse(MTIMERParams()), clintParams.isACLINT, beatBytes)) + mswi.node := tlbus.coupleTo("mswi") { TLFragmenter(tlbus) := _ } + + InModuleBody { + mswi.module.clock := tlbus.module.clock + mswi.module.reset := tlbus.module.reset + } + + mswi + } + + mswiOpt + } + case _ => None + } + val mtimerOpt = p(CLINTKey) match { + case Some(clintParams) if clintParams.isACLINT => + { + val mtimerOpt = clintParams.mtimer.map { params => + val tlbus = locateTLBusWrapper(p(MTIMERAttachKey).slaveWhere) + val beatBytes = tlbus.beatBytes + val mtimer = LazyModule(new MTIMER(params, beatBytes)) + mtimer.mtimecmpNode := tlbus.coupleTo("mtimecmp") { TLFragmenter(tlbus) := _ } + mtimer.mtimeNode := tlbus.coupleTo("mtime") { TLFragmenter(tlbus) := _ } + + InModuleBody { + mtimer.module.clock := tlbus.module.clock + mtimer.module.reset := tlbus.module.reset + } + + mtimer + } + + mtimerOpt + } + case _ => None } -} +} \ No newline at end of file diff --git a/src/main/scala/devices/tilelink/MSWI.scala b/src/main/scala/devices/tilelink/MSWI.scala new file mode 100644 index 00000000000..e35a31edfe2 --- /dev/null +++ b/src/main/scala/devices/tilelink/MSWI.scala @@ -0,0 +1,140 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.devices.tilelink + +import chisel3._ +import chisel3.util.ShiftRegister +import org.chipsalliance.cde.config.{Field, Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.interrupts._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.util._ + +object MSWIConsts +{ + def msipOffset(hart: Int) = hart * msipBytes + def msipBytes = 4 + def size = 0x4000 + def ipiWidth = 32 + def ints = 1 +} + +case class MSWIParams(BaseAddress: BigInt = 0x02000000, intStages: Int = 0) +{ + def address = AddressSet(BaseAddress, MSWIConsts.size - 1) +} + +case class MSWIAttachParams( + slaveWhere: TLBusWrapperLocation = CBUS +) + +case object MSWIAttachKey extends Field(MSWIAttachParams()) + +class MSWI(mswiParams: MSWIParams, mtimerParams: MTIMERParams, isACLINT: Boolean = false, beatBytes: Int)(implicit p: Parameters) extends LazyModule +{ + import MSWIConsts._ + + val device = if (isACLINT) { + new SimpleDevice("mswi", Seq("riscv,aclint-mswi")) { + override val alwaysExtended = true + } + } else { + new SimpleDevice("clint", Seq("riscv,clint0")) { + override val alwaysExtended = true + } + } + + val node: TLRegisterNode = if (isACLINT) { + TLRegisterNode( + address = Seq(mswiParams.address), + device = device, + beatBytes = beatBytes + ) + } else { + TLRegisterNode( + address = Seq(AddressSet(mswiParams.address.base, 0x10000 - 1)), + device = device, + beatBytes = beatBytes + ) + } + + val ints = if (isACLINT) { + MSWIConsts.ints + } else { + MSWIConsts.ints + MTIMERConsts.ints + } + + val intnode : IntNexusNode = IntNexusNode( + sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(ints, Seq(Resource(device, "int")))))}, + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, + outputRequiresInput = false + ) + + lazy val module = new Impl + class Impl extends LazyModuleImp(this) { + if (isACLINT) { + Annotated.params(this, mswiParams) + } else { + Annotated.params(this, mswiParams) + Annotated.params(this, mtimerParams) + } + + require (intnode.edges.in.size == 0, "MSWI only produces interrupts; it does not accept them") + + val nTiles = intnode.out.size + val ipi = Seq.fill(nTiles) { RegInit(0.U(1.W)) } + + val io = IO(new Bundle { + val rtcTick = Input(Bool()) + }) + + val timecmp = if (!isACLINT) { Seq.fill(nTiles) { Reg(UInt(MTIMERConsts.mtimeWidth.W))} } else { Seq.fill(nTiles){ Reg(UInt(0.W))} } + val time = if (!isACLINT) { RegInit(0.U(MTIMERConsts.mtimeWidth.W)) } else { RegInit(0.U(0.W)) } + + if (!isACLINT) + { + when (io.rtcTick) { time := time + 1.U } + } + + val (intnode_out, _) = intnode.out.unzip + intnode_out.zipWithIndex.foreach { case (int, i) => + int(0) := ShiftRegister(ipi(i)(0), mswiParams.intStages) + if (!isACLINT) { + int(1) := ShiftRegister(time.asUInt >= timecmp(i).asUInt, mtimerParams.intStages) + } + } + + /* aclint: + * 0 msip hart 0 + * 4 msip hart 1 + * + * clint: + * 0000 msip hart 0 + * 0004 msip hart 1 + * 4000 mtimecmp hart 0 lo + * 4004 mtimecmp hart 0 hi + * 4008 mtimecmp hart 1 lo + * 400c mtimecmp hart 1 hi + * bff8 mtime lo + * bffc mtime hi + */ + if (!isACLINT) { + node.regmap( + 0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.flatMap{ case (r, i) => + RegField(1, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0))) :: RegField(MSWIConsts.ipiWidth - 1) :: Nil }), + 0x4000 + MTIMERConsts.mtimecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), + RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))}, + 0xbff8 -> RegFieldGroup("mtime", Some("Timer Register"), + RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0), volatile=true)))) + ) + } else { + node.regmap( + 0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.flatMap{ case (r, i) => + RegField(1, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0))) :: RegField(ipiWidth - 1) :: Nil }) + ) + } + } +} + diff --git a/src/main/scala/devices/tilelink/Mtimer.scala b/src/main/scala/devices/tilelink/Mtimer.scala new file mode 100644 index 00000000000..7ec31abaa00 --- /dev/null +++ b/src/main/scala/devices/tilelink/Mtimer.scala @@ -0,0 +1,105 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.devices.tilelink + +import chisel3._ +import chisel3.util.ShiftRegister +import org.chipsalliance.cde.config.{Field, Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.interrupts._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.util._ + +object MTIMERConsts +{ + def mtimecmpOffset(hart: Int) = hart * mtimecmpBytes + def mtimecmpBytes = 8 + def mtimeWidth = 64 + def mtimecmpSize = 0x8000 + def mtimeSize = 0x100 + def ints = 1 +} + +// Notice: Remember to ensure that the size and address meet the requirements of the AddressSet, if you use ACLINT +case class MTIMERParams(MTIMECMPBaseAddress: BigInt = 0x02008000, MTIMEBaseAddress: BigInt = 0x02010000, intStages: Int = 0) +{ + def mtimecmpAddress = AddressSet(MTIMECMPBaseAddress, MTIMERConsts.mtimecmpSize - 1) + + def mtimeAddress = AddressSet(MTIMEBaseAddress, MTIMERConsts.mtimeSize - 1) +} + +case class MTIMERAttachParams( + slaveWhere: TLBusWrapperLocation = CBUS +) + +case object MTIMERAttachKey extends Field(MTIMERAttachParams()) + +class MTIMER(params: MTIMERParams, beatBytes: Int)(implicit p: Parameters) extends LazyModule +{ + import MTIMERConsts._ + + val device = new SimpleDevice("mtimer", Seq("riscv,aclint-mtimer")) { + override val alwaysExtended = true + } + + val mtimecmpNode: TLRegisterNode = TLRegisterNode( + address = Seq(params.mtimecmpAddress), + device = device, + beatBytes = beatBytes + ) + + val mtimeNode: TLRegisterNode = TLRegisterNode( + address = Seq(params.mtimeAddress), + device = device, + beatBytes = beatBytes + ) + + val intnode: IntNexusNode = IntNexusNode( + sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(ints, Seq(Resource(device, "int")))))}, + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters()))}, + outputRequiresInput = false + ) + + lazy val module = new Impl + class Impl extends LazyModuleImp(this) { + Annotated.params(this, params) + require (intnode.edges.in.size == 0, "MTIMER Device only produces interrupts; it does not accept them") + + val io = IO(new Bundle{ + val rtcTick = Input(Bool()) + }) + + val time = RegInit(0.U(mtimeWidth.W)) + when (io.rtcTick) { time := time + 1.U } + + val nTiles = intnode.out.size + val timecmp = Seq.fill(nTiles) { Reg(UInt(mtimeWidth.W)) } + + val (intnode_out, _) = intnode.out.unzip + intnode_out.zipWithIndex.foreach { case (int, i) => + int(0) := ShiftRegister(time.asUInt >= timecmp(i).asUInt, params.intStages) + } + + /* + * Two base addresses: + * 0 mtimecmp hart 0 lo + * 4 mtimecmp hart 0 hi + * 8 mtimecmp hart 1 lo + * c mtimecmp hart 1 hi + * + * 0 mtime lo + * 4 mtime hi + */ + mtimecmpNode.regmap( + 0 -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), + RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))} + ) + + mtimeNode.regmap( + 0 -> RegFieldGroup("mtime", Some("Timer Register"), + RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0), volatile=true)))) + ) + } +} \ No newline at end of file diff --git a/src/main/scala/subsystem/HasTiles.scala b/src/main/scala/subsystem/HasTiles.scala index 5c65ed4cd81..78a826c471f 100644 --- a/src/main/scala/subsystem/HasTiles.scala +++ b/src/main/scala/subsystem/HasTiles.scala @@ -5,7 +5,7 @@ package freechips.rocketchip.subsystem import chisel3._ import chisel3.dontTouch import org.chipsalliance.cde.config.{Field, Parameters} -import freechips.rocketchip.devices.tilelink.{BasicBusBlocker, BasicBusBlockerParams, CLINTConsts, PLICKey, CanHavePeripheryPLIC, CanHavePeripheryCLINT} +import freechips.rocketchip.devices.tilelink.{BasicBusBlocker, BasicBusBlockerParams, MTIMERConsts, MSWIConsts, CLINTKey, PLICKey, CanHavePeripheryPLIC, CanHavePeripheryCLINT} import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ import freechips.rocketchip.tile._ @@ -296,10 +296,22 @@ trait CanAttachTile { // 2. The CLINT and PLIC output interrupts are synchronous to the TileLink bus clock, // so might need to be synchronized depending on the Tile's crossing type. - // From CLINT: "msip" and "mtip" - domain.crossIntIn(crossingParams.crossingType) := - context.clintOpt.map { _.intnode } - .getOrElse { NullIntSource(sources = CLINTConsts.ints) } + // From ACLINT: "msip" and "mtip" + val clintParams = p(CLINTKey).map { params => + if (params.isACLINT) { + domain.crossIntIn(crossingParams.crossingType) := + context.mswiOpt.map { _.intnode } + .getOrElse { NullIntSource(sources = MSWIConsts.ints) } + + domain.crossIntIn(crossingParams.crossingType) := + context.mtimerOpt.map { _.intnode } + .getOrElse { NullIntSource(sources = MTIMERConsts.ints) } + } else { + domain.crossIntIn(crossingParams.crossingType) := + context.mswiOpt.map { _.intnode } + .getOrElse { NullIntSource(sources = MSWIConsts.ints + MTIMERConsts.ints) } + } + } // From PLIC: "meip" domain.crossIntIn(crossingParams.crossingType) := diff --git a/src/main/scala/subsystem/RTC.scala b/src/main/scala/subsystem/RTC.scala index 499633f679a..00259d30562 100644 --- a/src/main/scala/subsystem/RTC.scala +++ b/src/main/scala/subsystem/RTC.scala @@ -5,7 +5,7 @@ package freechips.rocketchip.subsystem import chisel3._ import chisel3.util.Counter import freechips.rocketchip.diplomacy.{LazyModuleImp, DTSTimebase} -import freechips.rocketchip.devices.tilelink.CanHavePeripheryCLINT +import freechips.rocketchip.devices.tilelink.{CanHavePeripheryCLINT, CLINTKey} trait HasRTCModuleImp extends LazyModuleImp { val outer: BaseSubsystem with CanHavePeripheryCLINT @@ -22,8 +22,16 @@ trait HasRTCModuleImp extends LazyModuleImp { // Use the static period to toggle the RTC chisel3.withClockAndReset(pbus.module.clock, pbus.module.reset) { val (_, int_rtc_tick) = Counter(true.B, internalPeriod.toInt) - outer.clintOpt.foreach { clint => - clint.module.io.rtcTick := int_rtc_tick + p(CLINTKey).map { params => + if(!params.isACLINT) { + outer.mswiOpt.map { mtimer => + mtimer.module.io.rtcTick := int_rtc_tick + } + } else { + outer.mtimerOpt.map { mtimer => + mtimer.module.io.rtcTick := int_rtc_tick + } + } } } }