msi: add a MSIMaster to bridge interrupts over ChipLink
This commit is contained in:
parent
3db375ef43
commit
ac55313e8e
75
src/main/scala/devices/msi/MSIMaster.scala
Normal file
75
src/main/scala/devices/msi/MSIMaster.scala
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package sifive.blocks.devices.msi
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import freechips.rocketchip.config.Parameters
|
||||||
|
import freechips.rocketchip.diplomacy._
|
||||||
|
import freechips.rocketchip.interrupts._
|
||||||
|
import freechips.rocketchip.tilelink._
|
||||||
|
import freechips.rocketchip.util.leftOR
|
||||||
|
|
||||||
|
case class MSITarget(address: BigInt, spacing: Int, number: Int)
|
||||||
|
{
|
||||||
|
require (number >= 0)
|
||||||
|
require (address >= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MSIMaster(targets: Seq[MSITarget])(implicit p: Parameters) extends LazyModule
|
||||||
|
{
|
||||||
|
val masterNode = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters("MSI Master", sourceId = IdRange(0,2))))))
|
||||||
|
|
||||||
|
// A terminal interrupt node of flexible number
|
||||||
|
val intNode = IntNexusNode(
|
||||||
|
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Nil))) },
|
||||||
|
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) },
|
||||||
|
inputRequiresOutput = false)
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val (io, masterEdge) = masterNode.out(0)
|
||||||
|
val interrupts = intNode.in.flatMap { case (i, e) => i.take(e.source.num) }
|
||||||
|
|
||||||
|
// Construct a map of the addresses to update for interrupts
|
||||||
|
val targetMap = targets.flatMap { case MSITarget(address, spacing, number) =>
|
||||||
|
address until (address+spacing*number) by spacing
|
||||||
|
} .map { addr =>
|
||||||
|
val m = masterEdge.manager.find(addr)
|
||||||
|
require (m.isDefined, s"MSIMaster ${name} was pointed at address 0x${addr}%x which does not exist")
|
||||||
|
require (m.get.supportsPutFull.contains(1), s"MSIMaster ${name} requires device ${m.get.name} supportPutFull of 1 byte (${m.get.supportsPutFull})")
|
||||||
|
UInt(addr)
|
||||||
|
}.take(interrupts.size max 1)
|
||||||
|
|
||||||
|
require (interrupts.size <= targetMap.size, s"MSIMaster ${name} has more interrupts (${interrupts.size}) than addresses to use (${targetMap.size})")
|
||||||
|
require (intNode.out.isEmpty, s"MSIMaster ${name} intNode is not a source!")
|
||||||
|
|
||||||
|
val busy = RegInit(Bool(false))
|
||||||
|
val remote = RegInit(UInt(0, width=interrupts.size max 1))
|
||||||
|
val local = if (interrupts.isEmpty) UInt(0) else Cat(interrupts.reverse)
|
||||||
|
val pending = remote ^ local
|
||||||
|
val select = ~(leftOR(pending) << 1) & pending
|
||||||
|
|
||||||
|
io.a.valid := pending.orR && !busy
|
||||||
|
io.a.bits := masterEdge.Put(
|
||||||
|
fromSource = UInt(0),
|
||||||
|
toAddress = Mux1H(select, targetMap),
|
||||||
|
lgSize = UInt(0),
|
||||||
|
data = (select & local).orR)._2
|
||||||
|
|
||||||
|
// When A is sent, toggle our model of the remote state
|
||||||
|
when (io.a.fire()) {
|
||||||
|
remote := remote ^ select
|
||||||
|
busy := Bool(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sink D messages to clear busy
|
||||||
|
io.d.ready := Bool(true)
|
||||||
|
when (io.d.fire()) {
|
||||||
|
busy := Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
io.b.ready := Bool(false)
|
||||||
|
io.c.valid := Bool(false)
|
||||||
|
io.e.valid := Bool(false)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user