Skip to content

Commit

Permalink
Implement modularization of aclint, which divide clint into mtimer an…
Browse files Browse the repository at this point in the history
…d mswi.

Co-authored-by: Hongren (Zenithal) Zheng <[email protected]>
Co-authored-by: jerryz123 <[email protected]>
  • Loading branch information
3 people committed May 23, 2023
1 parent 767a61c commit 7de0dd6
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 106 deletions.
158 changes: 60 additions & 98 deletions src/main/scala/devices/tilelink/CLINT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,108 +12,70 @@ 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
* If isACLINT is true, here is the template:
* CLINTParams(
* isACLINT: Boolean = true,
* mtimer: Option[MTIMERParams] = Some(MTIMERParams(MTIMECMPBaseAddress= yyy)),
* mswi: Option[MSWIParams] = Some(MSWIParams(BaseAddress = xxx))
* )
*/
case class CLINTParams(
isACLINT: Boolean = false,
mtimer: Option[MTIMERParams] = None,
mswi: Option[MSWIParams] = Some(MSWIParams())
){
require(mtimer.isDefined || mswi.isDefined, "If both mtimer and mswi are empty, please directly set CLINTKey to empty")
require(!(!isACLINT && mtimer.isDefined), "The mtimer should not be specified when isACLINT = false")
}

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, MTIMERParams(MTIMEBaseAddress = params.BaseAddress + SWIConsts.size), 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
}
}
}
140 changes: 140 additions & 0 deletions src/main/scala/devices/tilelink/MSWI.scala
Original file line number Diff line number Diff line change
@@ -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 SWIConsts
{
def sipOffset(hart: Int) = hart * sipBytes
def sipBytes = 4
def size = 0x4000
def ipiWidth = 32
def ints = 1
def clintSize = 0x10000
}

case class MSWIParams(BaseAddress: BigInt = 0x02000000, intStages: Int = 0)
{
def address = AddressSet(BaseAddress, SWIConsts.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 SWIConsts._

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, clintSize - 1)),
device = device,
beatBytes = beatBytes
)
}

val ints = if (isACLINT) {
SWIConsts.ints
} else {
SWIConsts.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 mswiRegGroup: Seq[RegField] = SWI("m", intnode, mswiParams.intStages)

val io = IO(new Bundle {
val rtcTick = (!isACLINT).option(Input(Bool()))
})

val mtimerRegGroup: Option[(Seq[RegField], Seq[RegField])] = (!isACLINT).option(MTIMER(io.rtcTick.get, intnode, 1, 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
*/
val mtimerRegMapping = if (!isACLINT) Seq(
0x4000 -> mtimerRegGroup.get._1,
0xbff8 -> mtimerRegGroup.get._2
) else Nil

val mswiRegMapping = Seq(
0 -> mswiRegGroup
)

val mapping = mswiRegMapping ++ mtimerRegMapping
node.regmap(mapping:_*)
}
}

object SWI {
def apply(mswiType: String, intnode: IntNexusNode, intStages: Int) = {
import SWIConsts._

val nTiles = intnode.out.size
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), intStages)
}

val swiRegGroup = RegFieldGroup(mswiType + "sip", Some(mswiType.toUpperCase + "SIP Bits"), ipi.zipWithIndex.flatMap { case (r, i) =>
RegField(1, r, RegFieldDesc(mswiType+s"sip_$i", mswiType.toUpperCase+s"SIP bit for Hart $i", reset=Some(0))) :: RegField(SWIConsts.ipiWidth - 1) :: Nil })

swiRegGroup
}
}
Loading

0 comments on commit 7de0dd6

Please sign in to comment.