Add SimpleBusInterconnect
This commit is contained in:
parent
4ed4af6a3e
commit
00bf84b7f8
|
@ -9,68 +9,6 @@ import spinal.lib.misc.{HexTools, InterruptCtrl, Prescaler, Timer}
|
||||||
import spinal.lib._
|
import spinal.lib._
|
||||||
import vexriscv.plugin.{DBusSimpleBus, IBusSimpleBus}
|
import vexriscv.plugin.{DBusSimpleBus, IBusSimpleBus}
|
||||||
|
|
||||||
case class SimpleBusConfig(addressWidth : Int, dataWidth : Int)
|
|
||||||
|
|
||||||
case class SimpleBusCmd(config : SimpleBusConfig) extends Bundle{
|
|
||||||
val wr = Bool
|
|
||||||
val address = UInt(config.addressWidth bits)
|
|
||||||
val data = Bits(config.dataWidth bits)
|
|
||||||
val mask = Bits(4 bit)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class SimpleBusRsp(config : SimpleBusConfig) extends Bundle{
|
|
||||||
val data = Bits(config.dataWidth bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
object SimpleBus{
|
|
||||||
def apply(addressWidth : Int, dataWidth : Int) = new SimpleBus(SimpleBusConfig(addressWidth, dataWidth))
|
|
||||||
}
|
|
||||||
case class SimpleBus(config : SimpleBusConfig) extends Bundle with IMasterSlave {
|
|
||||||
val cmd = Stream(SimpleBusCmd(config))
|
|
||||||
val rsp = Flow(SimpleBusRsp(config))
|
|
||||||
|
|
||||||
override def asMaster(): Unit = {
|
|
||||||
master(cmd)
|
|
||||||
slave(rsp)
|
|
||||||
}
|
|
||||||
|
|
||||||
def <<(m : SimpleBus) : Unit = {
|
|
||||||
val s = this
|
|
||||||
assert(m.config.addressWidth >= s.config.addressWidth)
|
|
||||||
assert(m.config.dataWidth == s.config.dataWidth)
|
|
||||||
s.cmd.valid := m.cmd.valid
|
|
||||||
s.cmd.wr := m.cmd.wr
|
|
||||||
s.cmd.address := m.cmd.address.resized
|
|
||||||
s.cmd.data := m.cmd.data
|
|
||||||
s.cmd.mask := m.cmd.mask
|
|
||||||
m.cmd.ready := s.cmd.ready
|
|
||||||
m.rsp.valid := s.rsp.valid
|
|
||||||
m.rsp.data := s.rsp.data
|
|
||||||
}
|
|
||||||
def >>(s : SimpleBus) : Unit = s << this
|
|
||||||
|
|
||||||
def cmdM2sPipe(): SimpleBus = {
|
|
||||||
val ret = cloneOf(this)
|
|
||||||
this.cmd.m2sPipe() >> ret.cmd
|
|
||||||
this.rsp << ret.rsp
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
def cmdS2mPipe(): SimpleBus = {
|
|
||||||
val ret = cloneOf(this)
|
|
||||||
this.cmd.s2mPipe() >> ret.cmd
|
|
||||||
this.rsp << ret.rsp
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
def rspPipe(): SimpleBus = {
|
|
||||||
val ret = cloneOf(this)
|
|
||||||
this.cmd >> ret.cmd
|
|
||||||
this.rsp << ret.rsp.stage()
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MuraxMasterArbiter(simpleBusConfig : SimpleBusConfig) extends Component{
|
class MuraxMasterArbiter(simpleBusConfig : SimpleBusConfig) extends Component{
|
||||||
val io = new Bundle{
|
val io = new Bundle{
|
||||||
val iBus = slave(IBusSimpleBus(false))
|
val iBus = slave(IBusSimpleBus(false))
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
package vexriscv.demo
|
||||||
|
|
||||||
|
|
||||||
|
import spinal.core._
|
||||||
|
import spinal.lib.bus.misc._
|
||||||
|
import spinal.lib._
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
|
case class SimpleBusConfig(addressWidth : Int, dataWidth : Int)
|
||||||
|
|
||||||
|
case class SimpleBusCmd(config : SimpleBusConfig) extends Bundle{
|
||||||
|
val wr = Bool
|
||||||
|
val address = UInt(config.addressWidth bits)
|
||||||
|
val data = Bits(config.dataWidth bits)
|
||||||
|
val mask = Bits(4 bit)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SimpleBusRsp(config : SimpleBusConfig) extends Bundle{
|
||||||
|
val data = Bits(config.dataWidth bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
object SimpleBus{
|
||||||
|
def apply(addressWidth : Int, dataWidth : Int) = new SimpleBus(SimpleBusConfig(addressWidth, dataWidth))
|
||||||
|
}
|
||||||
|
case class SimpleBus(config : SimpleBusConfig) extends Bundle with IMasterSlave {
|
||||||
|
val cmd = Stream(SimpleBusCmd(config))
|
||||||
|
val rsp = Flow(SimpleBusRsp(config))
|
||||||
|
|
||||||
|
override def asMaster(): Unit = {
|
||||||
|
master(cmd)
|
||||||
|
slave(rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
def <<(m : SimpleBus) : Unit = {
|
||||||
|
val s = this
|
||||||
|
assert(m.config.addressWidth >= s.config.addressWidth)
|
||||||
|
assert(m.config.dataWidth == s.config.dataWidth)
|
||||||
|
s.cmd.valid := m.cmd.valid
|
||||||
|
s.cmd.wr := m.cmd.wr
|
||||||
|
s.cmd.address := m.cmd.address.resized
|
||||||
|
s.cmd.data := m.cmd.data
|
||||||
|
s.cmd.mask := m.cmd.mask
|
||||||
|
m.cmd.ready := s.cmd.ready
|
||||||
|
m.rsp.valid := s.rsp.valid
|
||||||
|
m.rsp.data := s.rsp.data
|
||||||
|
}
|
||||||
|
def >>(s : SimpleBus) : Unit = s << this
|
||||||
|
|
||||||
|
def cmdM2sPipe(): SimpleBus = {
|
||||||
|
val ret = cloneOf(this)
|
||||||
|
this.cmd.m2sPipe() >> ret.cmd
|
||||||
|
this.rsp << ret.rsp
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
def cmdS2mPipe(): SimpleBus = {
|
||||||
|
val ret = cloneOf(this)
|
||||||
|
this.cmd.s2mPipe() >> ret.cmd
|
||||||
|
this.rsp << ret.rsp
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
def rspPipe(): SimpleBus = {
|
||||||
|
val ret = cloneOf(this)
|
||||||
|
this.cmd >> ret.cmd
|
||||||
|
this.rsp << ret.rsp.stage()
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
object SimpleBusArbiter{
|
||||||
|
def apply(inputs : Seq[SimpleBus], pendingRspMax : Int): SimpleBus = {
|
||||||
|
val c = SimpleBusArbiter(inputs.head.config, inputs.size, pendingRspMax)
|
||||||
|
(inputs, c.io.inputs).zipped.foreach(_ <> _)
|
||||||
|
c.io.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SimpleBusArbiter(simpleBusConfig : SimpleBusConfig, portCount : Int, pendingRspMax : Int) extends Component{
|
||||||
|
val io = new Bundle{
|
||||||
|
val inputs = Vec(slave(SimpleBus(simpleBusConfig)), portCount)
|
||||||
|
val output = master(SimpleBus(simpleBusConfig))
|
||||||
|
}
|
||||||
|
val logic = if(portCount == 1) new Area{
|
||||||
|
io.output << io.inputs(0)
|
||||||
|
} else new Area {
|
||||||
|
val arbiter = StreamArbiterFactory.lowerFirst.transactionLock.build(SimpleBusCmd(simpleBusConfig), portCount)
|
||||||
|
(arbiter.io.inputs, io.inputs).zipped.foreach(_ <> _.cmd)
|
||||||
|
|
||||||
|
val (outputCmdFork, routeCmdFork) = StreamFork2(arbiter.io.output)
|
||||||
|
io.output.cmd << outputCmdFork
|
||||||
|
|
||||||
|
val rspRoute = routeCmdFork.translateWith(arbiter.io.chosen).throwWhen(routeCmdFork.wr).queueLowLatency(size = pendingRspMax, latency = 1)
|
||||||
|
rspRoute.ready := io.output.rsp.valid
|
||||||
|
|
||||||
|
for ((input, id) <- io.inputs.zipWithIndex) {
|
||||||
|
input.rsp.valid := io.output.rsp.valid && rspRoute.payload === id
|
||||||
|
input.rsp.payload := io.output.rsp.payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleBusSlaveFactory(bus: SimpleBus) extends BusSlaveFactoryDelayed{
|
||||||
|
bus.cmd.ready := True
|
||||||
|
|
||||||
|
val readAtCmd = Flow(Bits(bus.config.dataWidth bits))
|
||||||
|
val readAtRsp = readAtCmd.stage()
|
||||||
|
|
||||||
|
val askWrite = (bus.cmd.valid && bus.cmd.wr).allowPruning()
|
||||||
|
val askRead = (bus.cmd.valid && !bus.cmd.wr).allowPruning()
|
||||||
|
val doWrite = (askWrite && bus.cmd.ready).allowPruning()
|
||||||
|
val doRead = (askRead && bus.cmd.ready).allowPruning()
|
||||||
|
|
||||||
|
bus.rsp.valid := readAtRsp.valid
|
||||||
|
bus.rsp.data := readAtRsp.payload
|
||||||
|
|
||||||
|
readAtCmd.valid := doRead
|
||||||
|
readAtCmd.payload := 0
|
||||||
|
|
||||||
|
def readAddress() : UInt = bus.cmd.address
|
||||||
|
def writeAddress() : UInt = bus.cmd.address
|
||||||
|
|
||||||
|
override def readHalt(): Unit = bus.cmd.ready := False
|
||||||
|
override def writeHalt(): Unit = bus.cmd.ready := False
|
||||||
|
|
||||||
|
override def build(): Unit = {
|
||||||
|
super.doNonStopWrite(bus.cmd.data)
|
||||||
|
|
||||||
|
def doMappedElements(jobs : Seq[BusSlaveFactoryElement]) = super.doMappedElements(
|
||||||
|
jobs = jobs,
|
||||||
|
askWrite = askWrite,
|
||||||
|
askRead = askRead,
|
||||||
|
doWrite = doWrite,
|
||||||
|
doRead = doRead,
|
||||||
|
writeData = bus.cmd.data,
|
||||||
|
readData = readAtCmd.payload
|
||||||
|
)
|
||||||
|
|
||||||
|
switch(bus.cmd.address) {
|
||||||
|
for ((address, jobs) <- elementsPerAddress if address.isInstanceOf[SingleMapping]) {
|
||||||
|
is(address.asInstanceOf[SingleMapping].address) {
|
||||||
|
doMappedElements(jobs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((address, jobs) <- elementsPerAddress if !address.isInstanceOf[SingleMapping]) {
|
||||||
|
when(address.hit(bus.cmd.address)){
|
||||||
|
doMappedElements(jobs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def busDataWidth: Int = bus.config.dataWidth
|
||||||
|
override def wordAddressInc: Int = busDataWidth / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SimpleBusDecoder(busConfig : SimpleBusConfig, mappings : Seq[AddressMapping], pendingMax : Int = 7) extends Component{
|
||||||
|
val io = new Bundle {
|
||||||
|
val input = slave(SimpleBus(busConfig))
|
||||||
|
val outputs = Vec(master(SimpleBus(busConfig)), mappings.size)
|
||||||
|
}
|
||||||
|
val hits = Vec(Bool, mappings.size)
|
||||||
|
for((slaveBus, memorySpace, hit) <- (io.outputs, mappings, hits).zipped) yield {
|
||||||
|
hit := (memorySpace match {
|
||||||
|
case DefaultMapping => !hits.filterNot(_ == hit).orR
|
||||||
|
case _ => memorySpace.hit(io.input.cmd.address)
|
||||||
|
})
|
||||||
|
slaveBus.cmd.valid := io.input.cmd.valid && hit
|
||||||
|
slaveBus.cmd.payload := io.input.cmd.payload.resized
|
||||||
|
}
|
||||||
|
val noHit = !hits.orR
|
||||||
|
io.input.cmd.ready := (hits,io.outputs).zipped.map(_ && _.cmd.ready).orR || noHit
|
||||||
|
|
||||||
|
val rspPendingCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init(0)
|
||||||
|
rspPendingCounter := rspPendingCounter + U(io.input.cmd.fire && !io.input.cmd.wr) - U(io.input.rsp.valid)
|
||||||
|
val rspHits = RegNextWhen(hits, io.input.cmd.fire)
|
||||||
|
val rspPending = rspPendingCounter =/= 0
|
||||||
|
val rspNoHit = !rspHits.orR
|
||||||
|
io.input.rsp.valid := io.outputs.map(_.rsp.valid).orR || (rspPending && rspNoHit)
|
||||||
|
io.input.rsp.payload := io.outputs.map(_.rsp.payload).read(OHToUInt(rspHits))
|
||||||
|
|
||||||
|
val cmdWait = (io.input.cmd.valid && rspPending && hits =/= rspHits) || rspPendingCounter === pendingMax
|
||||||
|
when(cmdWait){
|
||||||
|
io.input.cmd.ready := False
|
||||||
|
io.outputs.foreach(_.cmd.valid := False)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object SimpleBusConnectors{
|
||||||
|
def direct(m : SimpleBus, s : SimpleBus) : Unit = m >> s
|
||||||
|
}
|
||||||
|
|
||||||
|
case class SimpleBusInterconnect(){
|
||||||
|
case class MasterModel(var connector : (SimpleBus,SimpleBus) => Unit = SimpleBusConnectors.direct)
|
||||||
|
case class SlaveModel(mapping : AddressMapping, var connector : (SimpleBus,SimpleBus) => Unit = SimpleBusConnectors.direct)
|
||||||
|
case class ConnectionModel(m : SimpleBus, s : SimpleBus, var connector : (SimpleBus,SimpleBus) => Unit = SimpleBusConnectors.direct)
|
||||||
|
|
||||||
|
val masters = mutable.LinkedHashMap[SimpleBus, MasterModel]()
|
||||||
|
val slaves = mutable.LinkedHashMap[SimpleBus, SlaveModel]()
|
||||||
|
val connections = ArrayBuffer[ConnectionModel]()
|
||||||
|
var pendingRspMax = 2
|
||||||
|
|
||||||
|
|
||||||
|
def setConnector(bus : SimpleBus)( connector : (SimpleBus,SimpleBus) => Unit): Unit = (masters.get(bus), slaves.get(bus)) match {
|
||||||
|
case (Some(m), _) => m.connector = connector
|
||||||
|
case (None, Some(s)) => s.connector = connector
|
||||||
|
}
|
||||||
|
|
||||||
|
def setConnector(m : SimpleBus, s : SimpleBus)(connector : (SimpleBus,SimpleBus) => Unit): Unit = connections.find(e => e.m == m && e.s == s) match {
|
||||||
|
case Some(c) => c.connector = connector
|
||||||
|
}
|
||||||
|
|
||||||
|
def addSlave(bus: SimpleBus,mapping: AddressMapping) : this.type = {
|
||||||
|
slaves(bus) = SlaveModel(mapping)
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
def addSlaves(orders : (SimpleBus,AddressMapping)*) : this.type = {
|
||||||
|
orders.foreach(order => addSlave(order._1,order._2))
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
def addMaster(bus : SimpleBus, accesses : Seq[SimpleBus]) : this.type = {
|
||||||
|
masters(bus) = MasterModel()
|
||||||
|
for(s <- accesses) connections += ConnectionModel(bus, s)
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
def addMasters(specs : (SimpleBus,Seq[SimpleBus])*) : this.type = {
|
||||||
|
specs.foreach(spec => addMaster(spec._1,spec._2))
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(): Unit ={
|
||||||
|
def applyName(bus : Bundle,name : String, onThat : Nameable) : Unit = {
|
||||||
|
if(bus.component == Component.current)
|
||||||
|
onThat.setCompositeName(bus,name)
|
||||||
|
else if(bus.isNamed)
|
||||||
|
onThat.setCompositeName(bus.component,bus.getName() + "_" + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val connectionsInput = mutable.HashMap[ConnectionModel,SimpleBus]()
|
||||||
|
val connectionsOutput = mutable.HashMap[ConnectionModel,SimpleBus]()
|
||||||
|
for((bus, model) <- masters){
|
||||||
|
val busConnections = connections.filter(_.m == bus)
|
||||||
|
val busSlaves = busConnections.map(c => slaves(c.s))
|
||||||
|
val decoder = new SimpleBusDecoder(bus.config, busSlaves.map(_.mapping))
|
||||||
|
applyName(bus,"decoder",decoder)
|
||||||
|
model.connector(bus, decoder.io.input)
|
||||||
|
for((connection, decoderOutput) <- (busConnections, decoder.io.outputs).zipped) {
|
||||||
|
connectionsInput(connection) = decoderOutput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for((bus, model) <- slaves){
|
||||||
|
val busConnections = connections.filter(_.s == bus)
|
||||||
|
val busMasters = busConnections.map(c => masters(c.m))
|
||||||
|
val arbiter = new SimpleBusArbiter(bus.config, busMasters.size, pendingRspMax)
|
||||||
|
applyName(bus,"arbiter",arbiter)
|
||||||
|
model.connector(arbiter.io.output, bus)
|
||||||
|
for((connection, arbiterInput) <- (busConnections, arbiter.io.inputs).zipped) {
|
||||||
|
connectionsOutput(connection) = arbiterInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(connection <- connections){
|
||||||
|
connection.connector(connectionsInput(connection), connectionsOutput(connection))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Will make SpinalHDL calling the build function at the end of the current component elaboration
|
||||||
|
Component.current.addPrePopTask(build)
|
||||||
|
}
|
Loading…
Reference in New Issue