2016-11-28 01:16:37 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
2016-05-10 09:25:13 +02:00
|
|
|
|
2016-06-28 20:21:38 +02:00
|
|
|
package uncore.devices
|
2016-05-10 09:25:13 +02:00
|
|
|
|
|
|
|
import Chisel._
|
|
|
|
import Chisel.ImplicitConversions._
|
|
|
|
|
|
|
|
import junctions._
|
2016-10-29 06:20:49 +02:00
|
|
|
import diplomacy._
|
2016-10-26 22:52:23 +02:00
|
|
|
import regmapper._
|
|
|
|
import uncore.tilelink2._
|
2016-11-18 23:05:14 +01:00
|
|
|
import config._
|
2016-10-29 06:20:49 +02:00
|
|
|
import scala.math.min
|
2017-02-09 22:59:09 +01:00
|
|
|
import tile.XLen
|
2017-04-28 04:35:20 +02:00
|
|
|
import util._
|
2016-05-10 09:25:13 +02:00
|
|
|
|
|
|
|
class GatewayPLICIO extends Bundle {
|
|
|
|
val valid = Bool(OUTPUT)
|
|
|
|
val ready = Bool(INPUT)
|
|
|
|
val complete = Bool(INPUT)
|
|
|
|
}
|
|
|
|
|
|
|
|
class LevelGateway extends Module {
|
|
|
|
val io = new Bundle {
|
|
|
|
val interrupt = Bool(INPUT)
|
|
|
|
val plic = new GatewayPLICIO
|
|
|
|
}
|
|
|
|
|
|
|
|
val inFlight = Reg(init=Bool(false))
|
|
|
|
when (io.interrupt && io.plic.ready) { inFlight := true }
|
|
|
|
when (io.plic.complete) { inFlight := false }
|
|
|
|
io.plic.valid := io.interrupt && !inFlight
|
|
|
|
}
|
|
|
|
|
2016-10-27 22:09:11 +02:00
|
|
|
object PLICConsts
|
|
|
|
{
|
2016-05-10 09:25:13 +02:00
|
|
|
def maxDevices = 1023
|
2016-06-06 05:33:51 +02:00
|
|
|
def maxHarts = 15872
|
2016-10-26 22:52:23 +02:00
|
|
|
def priorityBase = 0x0
|
2016-06-06 05:33:51 +02:00
|
|
|
def pendingBase = 0x1000
|
|
|
|
def enableBase = 0x2000
|
|
|
|
def hartBase = 0x200000
|
|
|
|
|
|
|
|
def claimOffset = 4
|
|
|
|
def priorityBytes = 4
|
2016-05-10 09:25:13 +02:00
|
|
|
|
2016-10-27 22:09:11 +02:00
|
|
|
def enableOffset(i: Int) = i * ((maxDevices+7)/8)
|
|
|
|
def hartOffset(i: Int) = i * 0x1000
|
|
|
|
def enableBase(i: Int):Int = enableOffset(i) + enableBase
|
|
|
|
def hartBase(i: Int):Int = hartOffset(i) + hartBase
|
|
|
|
|
|
|
|
def size = hartBase(maxHarts)
|
|
|
|
require(hartBase >= enableBase(maxHarts))
|
|
|
|
}
|
|
|
|
|
2016-10-29 06:20:49 +02:00
|
|
|
/** Platform-Level Interrupt Controller */
|
2017-03-02 22:37:25 +01:00
|
|
|
class TLPLIC(maxPriorities: Int, address: BigInt = 0xC000000)(implicit p: Parameters) extends LazyModule
|
2016-10-29 06:20:49 +02:00
|
|
|
{
|
|
|
|
require (maxPriorities >= 0)
|
|
|
|
|
2017-03-01 08:12:36 +01:00
|
|
|
// plic0 => max devices 1023
|
|
|
|
val device = new SimpleDevice("interrupt-controller", Seq("riscv,plic0")) {
|
|
|
|
override val alwaysExtended = true
|
|
|
|
override def describe(resources: ResourceBindings): Description = {
|
|
|
|
val Description(name, mapping) = super.describe(resources)
|
|
|
|
val extra = Map(
|
|
|
|
"interrupt-controller" -> Nil,
|
|
|
|
"riscv,ndev" -> Seq(ResourceInt(nDevices)),
|
|
|
|
"#interrupt-cells" -> Seq(ResourceInt(1)),
|
|
|
|
"#address-cells" -> Seq(ResourceInt(0)))
|
|
|
|
Description(name, mapping ++ extra)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-29 06:20:49 +02:00
|
|
|
val node = TLRegisterNode(
|
2017-03-20 22:48:51 +01:00
|
|
|
address = Seq(AddressSet(address, PLICConsts.size-1)),
|
2017-03-01 08:12:36 +01:00
|
|
|
device = device,
|
2017-02-09 22:59:09 +01:00
|
|
|
beatBytes = p(XLen)/8,
|
2017-04-20 04:44:51 +02:00
|
|
|
undefZero = false,
|
2017-04-28 04:35:20 +02:00
|
|
|
concurrency = 1) // limiting concurrency handles RAW hazards on claim registers
|
2016-10-29 06:20:49 +02:00
|
|
|
|
2017-01-30 00:17:52 +01:00
|
|
|
val intnode = IntNexusNode(
|
2016-10-29 06:20:49 +02:00
|
|
|
numSourcePorts = 0 to 1024,
|
|
|
|
numSinkPorts = 0 to 1024,
|
2017-03-02 22:37:25 +01:00
|
|
|
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Seq(Resource(device, "int"))))) },
|
2016-10-29 06:20:49 +02:00
|
|
|
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
|
|
|
|
|
2016-11-22 23:58:03 +01:00
|
|
|
/* Negotiated sizes */
|
2017-03-01 08:12:36 +01:00
|
|
|
def nDevices: Int = intnode.edgesIn.map(_.source.num).sum
|
2016-11-22 23:58:03 +01:00
|
|
|
def nPriorities = min(maxPriorities, nDevices)
|
|
|
|
def nHarts = intnode.edgesOut.map(_.source.num).sum
|
|
|
|
|
2017-03-01 08:12:36 +01:00
|
|
|
// Assign all the devices unique ranges
|
|
|
|
lazy val sources = intnode.edgesIn.map(_.source)
|
|
|
|
lazy 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
|
|
|
|
|
|
|
|
ResourceBinding {
|
|
|
|
flatSources.foreach { s => s.resources.foreach { r =>
|
|
|
|
// +1 because interrupt 0 is reserved
|
|
|
|
(s.range.start until s.range.end).foreach { i => r.bind(device, ResourceInt(i+1)) }
|
|
|
|
} }
|
|
|
|
}
|
|
|
|
|
2016-10-29 06:20:49 +02:00
|
|
|
lazy val module = new LazyModuleImp(this) {
|
|
|
|
val io = new Bundle {
|
|
|
|
val tl_in = node.bundleIn
|
|
|
|
val devices = intnode.bundleIn
|
|
|
|
val harts = intnode.bundleOut
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2017-03-02 20:02:35 +01:00
|
|
|
println(s"Interrupt map (${nHarts} harts ${nDevices} interrupts):")
|
2016-10-29 06:20:49 +02:00
|
|
|
flatSources.foreach { s =>
|
2016-10-29 21:39:04 +02:00
|
|
|
// +1 because 0 is reserved, +1-1 because the range is half-open
|
|
|
|
println(s" [${s.range.start+1}, ${s.range.end}] => ${s.name}")
|
2016-10-29 06:20:49 +02:00
|
|
|
}
|
2017-03-02 20:02:35 +01:00
|
|
|
println("")
|
2016-10-29 06:20:49 +02:00
|
|
|
|
2016-11-22 23:58:03 +01:00
|
|
|
require (nDevices == interrupts.size)
|
|
|
|
require (nHarts == harts.size)
|
2016-10-29 06:20:49 +02:00
|
|
|
|
|
|
|
require(nDevices <= PLICConsts.maxDevices)
|
|
|
|
require(nHarts > 0 && nHarts <= PLICConsts.maxHarts)
|
|
|
|
|
|
|
|
// For now, use LevelGateways for all TL2 interrupts
|
2017-04-28 04:35:20 +02:00
|
|
|
val gateways = Vec((false.B +: interrupts).map { case i =>
|
2016-10-29 06:20:49 +02:00
|
|
|
val gateway = Module(new LevelGateway)
|
|
|
|
gateway.io.interrupt := i
|
|
|
|
gateway.io.plic
|
2017-04-28 04:35:20 +02:00
|
|
|
})
|
2016-10-29 06:20:49 +02:00
|
|
|
|
|
|
|
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())))
|
|
|
|
|
2017-04-28 04:35:20 +02:00
|
|
|
for ((p, g) <- pending zip gateways) {
|
2016-10-29 06:20:49 +02:00
|
|
|
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)
|
2017-04-28 04:35:20 +02:00
|
|
|
val left = findMax(x take half)
|
|
|
|
val right = findMax(x drop half)
|
|
|
|
MuxT(left._1 >= right._1, left, (right._1, UInt(half) | right._2))
|
2016-10-29 06:20:49 +02:00
|
|
|
} else (x.head, UInt(0))
|
|
|
|
}
|
|
|
|
|
|
|
|
val maxDevs = Reg(Vec(nHarts, UInt(width = log2Up(pending.size))))
|
|
|
|
for (hart <- 0 until nHarts) {
|
2017-04-28 04:35:20 +02:00
|
|
|
val effectivePriority = (UInt(1) << priority(0).getWidth) +:
|
|
|
|
(for (((p, en), pri) <- (pending zip enables(hart) zip priority).tail)
|
|
|
|
yield Cat(p && en, pri))
|
|
|
|
val (maxPri, maxDev) = findMax(effectivePriority)
|
2016-10-29 06:20:49 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
(Bool(true), maxDevs(i))
|
|
|
|
},
|
|
|
|
RegWriteFn { (valid, data) =>
|
2017-04-28 04:35:20 +02:00
|
|
|
val irq = data.extract(log2Ceil(nDevices+1)-1, 0)
|
|
|
|
when (valid && enables(i)(irq)) {
|
|
|
|
gateways(irq).complete := Bool(true)
|
2016-10-29 06:20:49 +02:00
|
|
|
}
|
|
|
|
Bool(true)
|
2016-10-26 22:52:23 +02:00
|
|
|
}
|
2016-10-29 06:20:49 +02:00
|
|
|
)
|
2016-10-26 22:52:23 +02:00
|
|
|
)
|
2016-10-29 06:20:49 +02:00
|
|
|
}
|
2016-05-10 09:25:13 +02:00
|
|
|
|
2016-10-29 06:20:49 +02:00
|
|
|
node.regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*)
|
2016-10-26 22:52:23 +02:00
|
|
|
|
2016-10-29 06:20:49 +02:00
|
|
|
priority(0) := 0
|
|
|
|
pending(0) := false
|
|
|
|
for (e <- enables)
|
|
|
|
e(0) := false
|
|
|
|
}
|
2016-05-10 09:25:13 +02:00
|
|
|
}
|