rocketchip: use TileLink2 interrupts
This commit is contained in:
@ -6,9 +6,11 @@ import Chisel._
|
||||
import Chisel.ImplicitConversions._
|
||||
|
||||
import junctions._
|
||||
import diplomacy._
|
||||
import regmapper._
|
||||
import uncore.tilelink2._
|
||||
import cde.Parameters
|
||||
import scala.math.min
|
||||
|
||||
class GatewayPLICIO extends Bundle {
|
||||
val valid = Bool(OUTPUT)
|
||||
@ -49,113 +51,161 @@ object PLICConsts
|
||||
require(hartBase >= enableBase(maxHarts))
|
||||
}
|
||||
|
||||
case class PLICConfig(nHartsIn: Int, supervisor: Boolean, nDevices: Int, nPriorities: Int) {
|
||||
import PLICConsts._
|
||||
|
||||
def contextsPerHart = if (supervisor) 2 else 1
|
||||
def nHarts = contextsPerHart * nHartsIn
|
||||
def context(i: Int, mode: Char) = mode match {
|
||||
case 'M' => i * contextsPerHart
|
||||
case 'S' => require(supervisor); i * contextsPerHart + 1
|
||||
}
|
||||
def claimAddr(i: Int, mode: Char) = hartBase(context(i, mode)) + claimOffset
|
||||
def threshAddr(i: Int, mode: Char) = hartBase(context(i, mode))
|
||||
def enableAddr(i: Int, mode: Char) = enableBase(context(i, mode))
|
||||
|
||||
require(nDevices <= maxDevices)
|
||||
require(nHarts > 0 && nHarts <= maxHarts)
|
||||
require(nPriorities >= 0 && nPriorities <= nDevices)
|
||||
}
|
||||
|
||||
trait HasPLICParameters {
|
||||
val params: (() => PLICConfig, Parameters)
|
||||
val cfg = params._1 ()
|
||||
implicit val p = params._2
|
||||
}
|
||||
|
||||
trait PLICBundle extends Bundle with HasPLICParameters {
|
||||
val devices = Vec(cfg.nDevices, new GatewayPLICIO).flip
|
||||
val harts = Vec(cfg.nHarts, Bool()).asOutput
|
||||
}
|
||||
|
||||
trait PLICModule extends Module with HasRegMap with HasPLICParameters {
|
||||
val io: PLICBundle
|
||||
|
||||
val priority =
|
||||
if (cfg.nPriorities > 0) Reg(Vec(cfg.nDevices+1, UInt(width=log2Up(cfg.nPriorities+1))))
|
||||
else Wire(init=Vec.fill(cfg.nDevices+1)(UInt(1)))
|
||||
val threshold =
|
||||
if (cfg.nPriorities > 0) Reg(Vec(cfg.nHarts, UInt(width = log2Up(cfg.nPriorities+1))))
|
||||
else Wire(init=Vec.fill(cfg.nHarts)(UInt(0)))
|
||||
val pending = Reg(init=Vec.fill(cfg.nDevices+1){Bool(false)})
|
||||
val enables = Reg(Vec(cfg.nHarts, Vec(cfg.nDevices+1, Bool())))
|
||||
|
||||
for ((p, g) <- pending.tail zip io.devices) {
|
||||
g.ready := !p
|
||||
g.complete := false
|
||||
when (g.valid) { p := true }
|
||||
}
|
||||
|
||||
def findMax(x: Seq[UInt]): (UInt, UInt) = {
|
||||
if (x.length > 1) {
|
||||
val half = 1 << (log2Ceil(x.length) - 1)
|
||||
val lMax = findMax(x take half)
|
||||
val rMax = findMax(x drop half)
|
||||
val useLeft = lMax._1 >= rMax._1
|
||||
(Mux(useLeft, lMax._1, rMax._1), Mux(useLeft, lMax._2, UInt(half) | rMax._2))
|
||||
} else (x.head, UInt(0))
|
||||
}
|
||||
|
||||
val maxDevs = Reg(Vec(cfg.nHarts, UInt(width = log2Up(pending.size))))
|
||||
for (hart <- 0 until cfg.nHarts) {
|
||||
val effectivePriority =
|
||||
for (((p, en), pri) <- (pending zip enables(hart) zip priority).tail)
|
||||
yield Cat(p && en, pri)
|
||||
val (maxPri, maxDev) = findMax((UInt(1) << priority(0).getWidth) +: effectivePriority)
|
||||
|
||||
maxDevs(hart) := maxDev
|
||||
io.harts(hart) := Reg(next = maxPri) > Cat(UInt(1), threshold(hart))
|
||||
}
|
||||
|
||||
def priorityRegField(x: UInt) = if (cfg.nPriorities > 0) RegField(32, x) else RegField.r(32, x)
|
||||
val priorityRegFields = Seq(PLICConsts.priorityBase -> priority.map(p => priorityRegField(p)))
|
||||
val pendingRegFields = Seq(PLICConsts.pendingBase -> pending .map(b => RegField.r(1, b)))
|
||||
|
||||
val enableRegFields = enables.zipWithIndex.map { case (e, i) =>
|
||||
PLICConsts.enableBase(i) -> e.map(b => RegField(1, b))
|
||||
}
|
||||
|
||||
val hartRegFields = Seq.tabulate(cfg.nHarts) { i =>
|
||||
PLICConsts.hartBase(i) -> Seq(
|
||||
priorityRegField(threshold(i)),
|
||||
RegField(32,
|
||||
RegReadFn { valid =>
|
||||
when (valid) {
|
||||
pending(maxDevs(i)) := Bool(false)
|
||||
maxDevs(i) := UInt(0) // flush pipeline
|
||||
}
|
||||
(Bool(true), maxDevs(i))
|
||||
},
|
||||
RegWriteFn { (valid, data) =>
|
||||
when (valid && enables(i)(data)) {
|
||||
io.devices(data - UInt(1)).complete := Bool(true)
|
||||
}
|
||||
Bool(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*)
|
||||
|
||||
priority(0) := 0
|
||||
pending(0) := false
|
||||
for (e <- enables)
|
||||
e(0) := false
|
||||
}
|
||||
|
||||
/** Platform-Level Interrupt Controller */
|
||||
class TLPLIC(c: () => PLICConfig, address: BigInt = 0xC000000)(implicit val p: Parameters)
|
||||
extends TLRegisterRouter(address, size = PLICConsts.size, beatBytes = p(rocket.XLen)/8, undefZero = false)(
|
||||
new TLRegBundle((c, p), _) with PLICBundle)(
|
||||
new TLRegModule((c, p), _, _) with PLICModule)
|
||||
class TLPLIC(supervisor: Boolean, maxPriorities: Int, address: BigInt = 0xC000000)(implicit val p: Parameters) extends LazyModule
|
||||
{
|
||||
val contextsPerHart = if (supervisor) 2 else 1
|
||||
require (maxPriorities >= 0)
|
||||
|
||||
val node = TLRegisterNode(
|
||||
address = AddressSet(address, PLICConsts.size-1),
|
||||
beatBytes = p(rocket.XLen)/8,
|
||||
undefZero = false)
|
||||
|
||||
val intnode = IntAdapterNode(
|
||||
numSourcePorts = 0 to 1024,
|
||||
numSinkPorts = 0 to 1024,
|
||||
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) },
|
||||
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val tl_in = node.bundleIn
|
||||
val devices = intnode.bundleIn
|
||||
val harts = intnode.bundleOut
|
||||
}
|
||||
|
||||
// Assign all the devices unique ranges
|
||||
val sources = intnode.edgesIn.map(_.source)
|
||||
val flatSources = (sources zip sources.map(_.num).scanLeft(0)(_+_).init).map {
|
||||
case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o)))
|
||||
}.flatten
|
||||
// Compact the interrupt vector the same way
|
||||
val interrupts = (intnode.edgesIn zip io.devices).map { case (e, i) => i.take(e.source.num) }.flatten
|
||||
// This flattens the harts into an MSMSMSMSMS... or MMMMM.... sequence
|
||||
val harts = io.harts.flatten
|
||||
|
||||
println("\nInterrupt map:")
|
||||
flatSources.foreach { s =>
|
||||
println(s" [${s.range.start}, ${s.range.end}) => ${s.name}")
|
||||
}
|
||||
|
||||
val nDevices = interrupts.size
|
||||
val nPriorities = min(maxPriorities, nDevices)
|
||||
val nHarts = harts.size
|
||||
|
||||
require(nDevices <= PLICConsts.maxDevices)
|
||||
require(nHarts > 0 && nHarts <= PLICConsts.maxHarts)
|
||||
|
||||
def context(i: Int, mode: Char) = mode match {
|
||||
case 'M' => i * contextsPerHart
|
||||
case 'S' => require(supervisor); i * contextsPerHart + 1
|
||||
}
|
||||
def claimAddr(i: Int, mode: Char) = address + PLICConsts.hartBase(context(i, mode)) + PLICConsts.claimOffset
|
||||
def threshAddr(i: Int, mode: Char) = address + PLICConsts.hartBase(context(i, mode))
|
||||
def enableAddr(i: Int, mode: Char) = address + PLICConsts.enableBase(context(i, mode))
|
||||
|
||||
// Create the global PLIC config string
|
||||
val globalConfigString = Seq(
|
||||
s"plic {\n",
|
||||
s" priority 0x${address.toString(16)};\n",
|
||||
s" pending 0x${(address + PLICConsts.pendingBase).toString(16)};\n",
|
||||
s" ndevs ${nDevices};\n",
|
||||
s"};\n").mkString
|
||||
|
||||
// Create the per-Hart config string
|
||||
val hartConfigStrings = io.harts.zipWithIndex.map { case (_, i) => (Seq(
|
||||
s" plic {\n",
|
||||
s" m {\n",
|
||||
s" ie 0x${enableAddr(i, 'M').toString(16)};\n",
|
||||
s" thresh 0x${threshAddr(i, 'M').toString(16)};\n",
|
||||
s" claim 0x${claimAddr(i, 'M').toString(16)};\n",
|
||||
s" };\n") ++ (if (!supervisor) Seq() else Seq(
|
||||
s" s {\n",
|
||||
s" ie 0x${enableAddr(i, 'S').toString(16)};\n",
|
||||
s" thresh 0x${threshAddr(i, 'S').toString(16)};\n",
|
||||
s" claim 0x${claimAddr(i, 'S').toString(16)};\n",
|
||||
s" };\n")) ++ Seq(
|
||||
s" };\n")).mkString
|
||||
}
|
||||
|
||||
// For now, use LevelGateways for all TL2 interrupts
|
||||
val gateways = Vec(interrupts.map { case i =>
|
||||
val gateway = Module(new LevelGateway)
|
||||
gateway.io.interrupt := i
|
||||
gateway.io.plic
|
||||
})
|
||||
|
||||
val priority =
|
||||
if (nPriorities > 0) Reg(Vec(nDevices+1, UInt(width=log2Up(nPriorities+1))))
|
||||
else Wire(init=Vec.fill(nDevices+1)(UInt(1)))
|
||||
val threshold =
|
||||
if (nPriorities > 0) Reg(Vec(nHarts, UInt(width = log2Up(nPriorities+1))))
|
||||
else Wire(init=Vec.fill(nHarts)(UInt(0)))
|
||||
val pending = Reg(init=Vec.fill(nDevices+1){Bool(false)})
|
||||
val enables = Reg(Vec(nHarts, Vec(nDevices+1, Bool())))
|
||||
|
||||
for ((p, g) <- pending.tail zip gateways) {
|
||||
g.ready := !p
|
||||
g.complete := false
|
||||
when (g.valid) { p := true }
|
||||
}
|
||||
|
||||
def findMax(x: Seq[UInt]): (UInt, UInt) = {
|
||||
if (x.length > 1) {
|
||||
val half = 1 << (log2Ceil(x.length) - 1)
|
||||
val lMax = findMax(x take half)
|
||||
val rMax = findMax(x drop half)
|
||||
val useLeft = lMax._1 >= rMax._1
|
||||
(Mux(useLeft, lMax._1, rMax._1), Mux(useLeft, lMax._2, UInt(half) | rMax._2))
|
||||
} else (x.head, UInt(0))
|
||||
}
|
||||
|
||||
val maxDevs = Reg(Vec(nHarts, UInt(width = log2Up(pending.size))))
|
||||
for (hart <- 0 until nHarts) {
|
||||
val effectivePriority =
|
||||
for (((p, en), pri) <- (pending zip enables(hart) zip priority).tail)
|
||||
yield Cat(p && en, pri)
|
||||
val (maxPri, maxDev) = findMax((UInt(1) << priority(0).getWidth) +: effectivePriority)
|
||||
|
||||
maxDevs(hart) := maxDev
|
||||
harts(hart) := Reg(next = maxPri) > Cat(UInt(1), threshold(hart))
|
||||
}
|
||||
|
||||
def priorityRegField(x: UInt) = if (nPriorities > 0) RegField(32, x) else RegField.r(32, x)
|
||||
val priorityRegFields = Seq(PLICConsts.priorityBase -> priority.map(p => priorityRegField(p)))
|
||||
val pendingRegFields = Seq(PLICConsts.pendingBase -> pending .map(b => RegField.r(1, b)))
|
||||
|
||||
val enableRegFields = enables.zipWithIndex.map { case (e, i) =>
|
||||
PLICConsts.enableBase(i) -> e.map(b => RegField(1, b))
|
||||
}
|
||||
|
||||
val hartRegFields = Seq.tabulate(nHarts) { i =>
|
||||
PLICConsts.hartBase(i) -> Seq(
|
||||
priorityRegField(threshold(i)),
|
||||
RegField(32,
|
||||
RegReadFn { valid =>
|
||||
when (valid) {
|
||||
pending(maxDevs(i)) := Bool(false)
|
||||
maxDevs(i) := UInt(0) // flush pipeline
|
||||
}
|
||||
(Bool(true), maxDevs(i))
|
||||
},
|
||||
RegWriteFn { (valid, data) =>
|
||||
when (valid && enables(i)(data)) {
|
||||
gateways(data - UInt(1)).complete := Bool(true)
|
||||
}
|
||||
Bool(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
node.regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*)
|
||||
|
||||
priority(0) := 0
|
||||
pending(0) := false
|
||||
for (e <- enables)
|
||||
e(0) := false
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import Chisel._
|
||||
import junctions._
|
||||
import junctions.NastiConstants._
|
||||
import regmapper._
|
||||
import diplomacy._
|
||||
import uncore.tilelink2._
|
||||
import uncore.util._
|
||||
import util._
|
||||
@ -20,22 +21,19 @@ class CoreplexLocalInterrupts extends Bundle {
|
||||
val msip = Bool()
|
||||
}
|
||||
|
||||
case class CoreplexLocalInterrupterConfig(address: BigInt = 0x02000000) {
|
||||
object ClintConsts
|
||||
{
|
||||
def msipOffset(hart: Int) = hart * msipBytes
|
||||
def msipAddress(hart: Int) = address + msipOffset(hart)
|
||||
def timecmpOffset(hart: Int) = 0x4000 + hart * timecmpBytes
|
||||
def timecmpAddress(hart: Int) = address + timecmpOffset(hart)
|
||||
def timeOffset = 0xbff8
|
||||
def timeAddress = address + timeOffset
|
||||
def msipBytes = 4
|
||||
def timecmpBytes = 8
|
||||
def size = 0x10000
|
||||
}
|
||||
|
||||
trait MixCoreplexLocalInterrupterParameters {
|
||||
val params: (CoreplexLocalInterrupterConfig, Parameters)
|
||||
val c = params._1
|
||||
implicit val p = params._2
|
||||
val params: Parameters
|
||||
implicit val p = params
|
||||
}
|
||||
|
||||
trait CoreplexLocalInterrupterBundle extends Bundle with MixCoreplexLocalInterrupterParameters {
|
||||
@ -45,6 +43,7 @@ trait CoreplexLocalInterrupterBundle extends Bundle with MixCoreplexLocalInterru
|
||||
|
||||
trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCoreplexLocalInterrupterParameters {
|
||||
val io: CoreplexLocalInterrupterBundle
|
||||
val address: AddressSet
|
||||
|
||||
val timeWidth = 64
|
||||
val regWidth = 32
|
||||
@ -64,6 +63,15 @@ trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCorep
|
||||
tile.mtip := time.asUInt >= timecmp(i).asUInt
|
||||
}
|
||||
|
||||
val globalConfigString = Seq(
|
||||
s"rtc {\n",
|
||||
s" addr 0x${(address.base + ClintConsts.timeOffset).toString(16)};\n",
|
||||
s"};\n").mkString
|
||||
val hartConfigStrings = (0 until p(NTiles)).map { i => Seq(
|
||||
s" timecmp 0x${(address.base + ClintConsts.timecmpOffset(i)).toString(16)};\n",
|
||||
s" ipi 0x${(address.base + ClintConsts.msipOffset(i)).toString(16)};\n").mkString
|
||||
}
|
||||
|
||||
/* 0000 msip hart 0
|
||||
* 0004 msip hart 1
|
||||
* 4000 mtimecmp hart 0 lo
|
||||
@ -75,16 +83,16 @@ trait CoreplexLocalInterrupterModule extends Module with HasRegMap with MixCorep
|
||||
*/
|
||||
|
||||
regmap(
|
||||
0 -> makeRegFields(ipi),
|
||||
c.timecmpOffset(0) -> makeRegFields(timecmp.flatten),
|
||||
c.timeOffset -> makeRegFields(time))
|
||||
0 -> makeRegFields(ipi),
|
||||
ClintConsts.timecmpOffset(0) -> makeRegFields(timecmp.flatten),
|
||||
ClintConsts.timeOffset -> makeRegFields(time))
|
||||
|
||||
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
|
||||
}
|
||||
|
||||
/** Power, Reset, Clock, Interrupt */
|
||||
// Magic TL2 Incantation to create a TL2 Slave
|
||||
class CoreplexLocalInterrupter(c: CoreplexLocalInterrupterConfig)(implicit val p: Parameters)
|
||||
extends TLRegisterRouter(c.address, size = c.size, beatBytes = p(rocket.XLen)/8, undefZero = false)(
|
||||
new TLRegBundle((c, p), _) with CoreplexLocalInterrupterBundle)(
|
||||
new TLRegModule((c, p), _, _) with CoreplexLocalInterrupterModule)
|
||||
class CoreplexLocalInterrupter(address: BigInt = 0x02000000)(implicit val p: Parameters)
|
||||
extends TLRegisterRouter(address, size = ClintConsts.size, beatBytes = p(rocket.XLen)/8, undefZero = false)(
|
||||
new TLRegBundle(p, _) with CoreplexLocalInterrupterBundle)(
|
||||
new TLRegModule(p, _, _) with CoreplexLocalInterrupterModule)
|
||||
|
@ -79,9 +79,6 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In
|
||||
}
|
||||
|
||||
case class IntIdentityNode() extends IdentityNode(IntImp)
|
||||
case class IntOutputNode() extends OutputNode(IntImp)
|
||||
case class IntInputNode() extends InputNode(IntImp)
|
||||
|
||||
case class IntSourceNode(num: Int) extends SourceNode(IntImp)(
|
||||
IntSourcePortParameters(Seq(IntSourceParameters(num))), (if (num == 0) 0 else 1) to 1)
|
||||
case class IntSinkNode() extends SinkNode(IntImp)(
|
||||
@ -94,11 +91,20 @@ case class IntAdapterNode(
|
||||
numSinkPorts: Range.Inclusive = 1 to 1)
|
||||
extends InteriorNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts)
|
||||
|
||||
case class IntOutputNode() extends OutputNode(IntImp)
|
||||
case class IntInputNode() extends InputNode(IntImp)
|
||||
|
||||
case class IntBlindOutputNode() extends BlindOutputNode(IntImp)(IntSinkPortParameters(Seq(IntSinkParameters())))
|
||||
case class IntBlindInputNode(num: Int) extends BlindInputNode(IntImp)(IntSourcePortParameters(Seq(IntSourceParameters(num))))
|
||||
|
||||
case class IntInternalOutputNode() extends InternalOutputNode(IntImp)(IntSinkPortParameters(Seq(IntSinkParameters())))
|
||||
case class IntInternalInputNode(num: Int) extends InternalInputNode(IntImp)(IntSourcePortParameters(Seq(IntSourceParameters(num))))
|
||||
|
||||
class IntXbar extends LazyModule
|
||||
{
|
||||
val intnode = IntAdapterNode(
|
||||
numSourcePorts = 1 to 1, // does it make sense to have more than one interrupt sink?
|
||||
numSinkPorts = 1 to 128,
|
||||
numSinkPorts = 0 to 128,
|
||||
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) },
|
||||
sourceFn = { seq =>
|
||||
IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map {
|
||||
@ -116,3 +122,17 @@ class IntXbar extends LazyModule
|
||||
io.out.foreach { _ := cat }
|
||||
}
|
||||
}
|
||||
|
||||
class IntXing extends LazyModule
|
||||
{
|
||||
val intnode = IntIdentityNode()
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = intnode.bundleIn
|
||||
val out = intnode.bundleOut
|
||||
}
|
||||
|
||||
io.out := RegNext(RegNext(RegNext(io.in)))
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ object TLRegisterNode
|
||||
// register mapped device from a totally abstract register mapped device.
|
||||
// See GPIO.scala in this directory for an example
|
||||
|
||||
abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean) extends LazyModule
|
||||
abstract class TLRegisterRouterBase(val address: AddressSet, interrupts: Int, concurrency: Int, beatBytes: Int, undefZero: Boolean, executable: Boolean) extends LazyModule
|
||||
{
|
||||
val node = TLRegisterNode(address, concurrency, beatBytes, undefZero, executable)
|
||||
val intnode = IntSourceNode(interrupts)
|
||||
@ -100,6 +100,7 @@ class TLRegModule[P, B <: TLRegBundleBase](val params: P, bundleBuilder: => B, r
|
||||
{
|
||||
val io = bundleBuilder
|
||||
val interrupts = if (io.interrupts.isEmpty) Vec(0, Bool()) else io.interrupts(0)
|
||||
val address = router.address
|
||||
def regmap(mapping: RegField.Map*) = router.node.regmap(mapping:_*)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user