First draft of interrupt controller
This commit is contained in:
parent
14a6e470c9
commit
e15e9c5085
170
uncore/src/main/scala/plic.scala
Normal file
170
uncore/src/main/scala/plic.scala
Normal file
@ -0,0 +1,170 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package uncore
|
||||
|
||||
import Chisel._
|
||||
import Chisel.ImplicitConversions._
|
||||
|
||||
import junctions._
|
||||
import cde.Parameters
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
case class PLICConfig(nHartsIn: Int, supervisor: Boolean, nDevices: Int, nPriorities: Int) {
|
||||
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 + hartOffset(context(i, mode)) + claimOffset
|
||||
def threshAddr(i: Int, mode: Char) = hartBase + hartOffset(context(i, mode))
|
||||
def enableAddr(i: Int, mode: Char) = enableBase + enableOffset(context(i, mode))
|
||||
def size = hartBase + hartOffset(maxHarts)
|
||||
|
||||
def maxDevices = 1023
|
||||
def maxHarts = 992
|
||||
def pendingBase = 0x800
|
||||
def enableBase = 0x1000
|
||||
def enableOffset(i: Int) = i * ((maxDevices+7)/8)
|
||||
def hartBase = enableBase + enableOffset(maxHarts)
|
||||
def hartOffset(i: Int) = i * 0x1000
|
||||
def claimOffset = 2
|
||||
|
||||
require(nDevices > 0 && nDevices <= maxDevices)
|
||||
require(nHarts > 0 && nHarts <= maxHarts)
|
||||
require(nPriorities > 0 && nPriorities <= nDevices)
|
||||
}
|
||||
|
||||
/** Platform-Level Interrupt Controller */
|
||||
class PLIC(val cfg: PLICConfig)(implicit val p: Parameters) extends Module
|
||||
with HasTileLinkParameters
|
||||
with HasAddrMapParameters {
|
||||
val io = new Bundle {
|
||||
val devices = Vec(cfg.nDevices, new GatewayPLICIO).flip
|
||||
val harts = Vec(cfg.nHarts, Bool()).asOutput
|
||||
val tl = new ClientUncachedTileLinkIO().flip
|
||||
}
|
||||
|
||||
val priority = Reg(Vec(cfg.nDevices+1, UInt(width=log2Up(cfg.nPriorities+1))))
|
||||
val pending = Reg(init=Vec.fill(cfg.nDevices+1){Bool(false)})
|
||||
val enables = Reg(Vec(cfg.nHarts, UInt(width = cfg.nDevices+1)))
|
||||
val threshold = Reg(Vec(cfg.nHarts, UInt(width = log2Up(cfg.nPriorities+1))))
|
||||
|
||||
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 = Wire(Vec(cfg.nHarts, UInt(width = log2Up(pending.size))))
|
||||
for (hart <- 0 until cfg.nHarts) {
|
||||
val effectivePriority =
|
||||
for (((p, en), pri) <- pending zip enables(hart).toBools zip priority)
|
||||
yield Cat(p && en, pri)
|
||||
val (maxPri, maxDev) = findMax(effectivePriority)
|
||||
|
||||
io.harts(hart) := Reg(next = maxPri > Cat(UInt(1), threshold(hart)))
|
||||
maxDevs(hart) := maxDev
|
||||
}
|
||||
|
||||
val acq = Queue(io.tl.acquire, 1)
|
||||
val read = acq.valid && acq.bits.isBuiltInType(Acquire.getType)
|
||||
val write = acq.valid && acq.bits.isBuiltInType(Acquire.putType)
|
||||
assert(!acq.valid || read || write, "unsupported PLIC operation")
|
||||
val addr = acq.bits.full_addr()(log2Up(cfg.size)-1,0)
|
||||
|
||||
val claimant =
|
||||
if (cfg.nHarts == 1) UInt(0)
|
||||
else (addr - cfg.hartBase)(log2Up(cfg.hartOffset(cfg.nHarts))-1,log2Up(cfg.hartOffset(1)))
|
||||
val hart = Wire(init = claimant)
|
||||
val myMaxDev = maxDevs(claimant) + UInt(0) // XXX FIRRTL bug w/o the + UInt(0)
|
||||
val myThresh = Cat(UInt(0, 16-threshold(0).getWidth), threshold(claimant))
|
||||
val myEnables = enables(hart) >> 1 << 1
|
||||
val rdata_fast = Wire(init = myEnables)
|
||||
val rdata = Wire(init = rdata_fast)
|
||||
val masked_wdata = (acq.bits.data & acq.bits.full_wmask()) | (rdata_fast & ~acq.bits.full_wmask())
|
||||
|
||||
when (addr >= cfg.hartBase) {
|
||||
rdata := Cat(myMaxDev, myThresh)
|
||||
when (read && addr(log2Ceil(cfg.claimOffset))) {
|
||||
pending(myMaxDev) := false
|
||||
}.elsewhen (write && acq.bits.wmask()(cfg.claimOffset)) {
|
||||
val dev = (acq.bits.data >> (8 * cfg.claimOffset))(log2Up(pending.size)-1,0)
|
||||
when (write && myEnables(dev)) { io.devices(dev-1).complete := true }
|
||||
}.elsewhen (write) {
|
||||
val thresh = acq.bits.data(log2Up(pending.size)-1,0)
|
||||
when (write) { threshold(claimant) := thresh }
|
||||
}
|
||||
}.elsewhen (addr >= cfg.enableBase) {
|
||||
if (cfg.nHarts > 1)
|
||||
hart := (addr - cfg.enableBase)(log2Up(cfg.enableOffset(cfg.nHarts))-1,log2Up(cfg.enableOffset(1)))
|
||||
require(enables.size <= tlDataBits) // TODO this can be relaxed
|
||||
when (write) { enables(hart) := masked_wdata >> 1 << 1 }
|
||||
}.elsewhen (addr >= cfg.pendingBase) {
|
||||
for (i <- 0 until pending.size by tlDataBytes) {
|
||||
val cond =
|
||||
if (tlDataBytes >= pending.size) Bool(true)
|
||||
else addr(log2Up(pending.size)-1,log2Up(tlDataBytes)) === i/tlDataBytes
|
||||
when (cond) {
|
||||
rdata_fast := Cat(pending.slice(i, i + tlDataBytes).map(p => Cat(UInt(0, 7), p)).reverse)
|
||||
for (j <- 0 until (tlDataBytes min (pending.size - i))) {
|
||||
when (write) { pending(i+j) := masked_wdata(j * 8) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}.otherwise {
|
||||
val regAddrBits = log2Up(log2Up(cfg.maxDevices+1)) - 3
|
||||
val regsPerBeat = tlDataBytes >> regAddrBits
|
||||
for (i <- 0 until priority.size by regsPerBeat) {
|
||||
val cond =
|
||||
if (regsPerBeat >= priority.size) Bool(true)
|
||||
else addr(log2Up(priority.size)+regAddrBits-1,log2Up(tlDataBytes)) === i/regsPerBeat
|
||||
when (cond) {
|
||||
rdata_fast := Cat(priority.slice(i, i + regsPerBeat).map(p => Cat(UInt(0, 16-p.getWidth), p)).reverse)
|
||||
for (j <- 0 until (regsPerBeat min (priority.size - i))) {
|
||||
when (write) { priority(i+j) := masked_wdata >> (j * (8 << regAddrBits)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
priority(0) := 0
|
||||
pending(0) := false
|
||||
|
||||
io.tl.grant.valid := acq.valid
|
||||
acq.ready := io.tl.grant.ready
|
||||
io.tl.grant.bits := Grant(
|
||||
is_builtin_type = Bool(true),
|
||||
g_type = acq.bits.getBuiltInGrantType(),
|
||||
client_xact_id = acq.bits.client_xact_id,
|
||||
manager_xact_id = UInt(0),
|
||||
addr_beat = UInt(0),
|
||||
data = rdata)
|
||||
}
|
Loading…
Reference in New Issue
Block a user