Separate memory interconnect from IO interconnect.
Since we're separating memory and MMIO traffic in the L1 to L2 network, we won't need to route between memory and MMIO at the AXI interconnect. This means we can have separate (and simpler) AXI interconnects for each. One consequence of this is that the starting address of the IO interconnect can no longer be assumed to be 0 by default.
This commit is contained in:
parent
a56a502ced
commit
bfdf5a538a
@ -30,14 +30,13 @@ trait HasAddrMapParameters {
|
|||||||
val pgLevelBits = p(PgLevelBits)
|
val pgLevelBits = p(PgLevelBits)
|
||||||
val asIdBits = p(ASIdBits)
|
val asIdBits = p(ASIdBits)
|
||||||
|
|
||||||
val addrMap = new AddrHashMap(p(GlobalAddrMap))
|
val addrMap = new AddrHashMap(p(GlobalAddrMap), p(MMIOBase))
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MemRegion { def size: BigInt }
|
abstract class MemRegion { def size: BigInt }
|
||||||
|
|
||||||
case class MemSize(size: BigInt, prot: Int) extends MemRegion
|
case class MemSize(size: BigInt, prot: Int) extends MemRegion
|
||||||
case class MemSubmap(size: BigInt, entries: AddrMap) extends MemRegion
|
case class MemSubmap(size: BigInt, entries: AddrMap) extends MemRegion
|
||||||
case class MemChannels(size: BigInt, nchannels: Int, prot: Int) extends MemRegion
|
|
||||||
|
|
||||||
object AddrMapConsts {
|
object AddrMapConsts {
|
||||||
val R = 0x1
|
val R = 0x1
|
||||||
@ -68,7 +67,6 @@ class AddrMap(entries: Seq[AddrMapEntry]) extends scala.collection.IndexedSeq[Ad
|
|||||||
this map { entry: AddrMapEntry => entry.region match {
|
this map { entry: AddrMapEntry => entry.region match {
|
||||||
case MemSize(_, _) => 1
|
case MemSize(_, _) => 1
|
||||||
case MemSubmap(_, submap) => submap.countSlaves
|
case MemSubmap(_, submap) => submap.countSlaves
|
||||||
case MemChannels(_, nchannels, _) => nchannels
|
|
||||||
}} reduceLeft(_ + _)
|
}} reduceLeft(_ + _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,12 +75,12 @@ object AddrMap {
|
|||||||
def apply(elems: AddrMapEntry*): AddrMap = new AddrMap(elems)
|
def apply(elems: AddrMapEntry*): AddrMap = new AddrMap(elems)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddrHashMap(addrmap: AddrMap) {
|
class AddrHashMap(addrmap: AddrMap, start: BigInt) {
|
||||||
val mapping = new HashMap[String, AddrHashMapEntry]
|
val mapping = new HashMap[String, AddrHashMapEntry]
|
||||||
|
|
||||||
private def genPairs(am: AddrMap): Seq[(String, AddrHashMapEntry)] = {
|
private def genPairs(am: AddrMap, start: BigInt): Seq[(String, AddrHashMapEntry)] = {
|
||||||
var ind = 0
|
var ind = 0
|
||||||
var base = BigInt(0)
|
var base = start
|
||||||
var pairs = Seq[(String, AddrHashMapEntry)]()
|
var pairs = Seq[(String, AddrHashMapEntry)]()
|
||||||
am.foreach { case AddrMapEntry(name, startOpt, region) =>
|
am.foreach { case AddrMapEntry(name, startOpt, region) =>
|
||||||
region match {
|
region match {
|
||||||
@ -94,32 +92,21 @@ class AddrHashMap(addrmap: AddrMap) {
|
|||||||
}
|
}
|
||||||
case MemSubmap(size, submap) => {
|
case MemSubmap(size, submap) => {
|
||||||
if (!startOpt.isEmpty) base = startOpt.get
|
if (!startOpt.isEmpty) base = startOpt.get
|
||||||
val subpairs = genPairs(submap).map {
|
val subpairs = genPairs(submap, base).map {
|
||||||
case (subname, AddrHashMapEntry(subind, subbase, subsize, prot)) =>
|
case (subname, AddrHashMapEntry(subind, subbase, subsize, prot)) =>
|
||||||
(name + ":" + subname,
|
(name + ":" + subname,
|
||||||
AddrHashMapEntry(ind + subind, base + subbase, subsize, prot))
|
AddrHashMapEntry(ind + subind, subbase, subsize, prot))
|
||||||
}
|
}
|
||||||
pairs = subpairs ++ pairs
|
pairs = subpairs ++ pairs
|
||||||
ind += subpairs.size
|
ind += subpairs.size
|
||||||
base += size
|
base += size
|
||||||
}
|
}
|
||||||
// every channel gets the same base and size
|
|
||||||
case MemChannels(size, nchannels, prot) => {
|
|
||||||
if (!startOpt.isEmpty) base = startOpt.get
|
|
||||||
val subpairs = (0 until nchannels).map { i =>
|
|
||||||
val chname = name + ":" + i.toString
|
|
||||||
(chname, AddrHashMapEntry(ind + i, base, size, prot))
|
|
||||||
}
|
|
||||||
pairs = subpairs ++ pairs
|
|
||||||
ind += nchannels
|
|
||||||
base += size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pairs
|
pairs
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((name, ind) <- genPairs(addrmap)) { mapping(name) = ind }
|
for ((name, ind) <- genPairs(addrmap, start)) { mapping(name) = ind }
|
||||||
|
|
||||||
def nEntries: Int = mapping.size
|
def nEntries: Int = mapping.size
|
||||||
def apply(name: String): AddrHashMapEntry = mapping(name)
|
def apply(name: String): AddrHashMapEntry = mapping(name)
|
||||||
@ -133,15 +120,18 @@ class AddrHashMap(addrmap: AddrMap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def isValid(addr: UInt): Bool = {
|
def isValid(addr: UInt): Bool = {
|
||||||
sortedEntries().map { case (_, base, size, _) =>
|
addr < UInt(start) || sortedEntries().map {
|
||||||
addr >= UInt(base) && addr < UInt(base + size)
|
case (_, base, size, _) =>
|
||||||
|
addr >= UInt(base) && addr < UInt(base + size)
|
||||||
}.reduceLeft(_ || _)
|
}.reduceLeft(_ || _)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getProt(addr: UInt): AddrMapProt = {
|
def getProt(addr: UInt): AddrMapProt = {
|
||||||
Mux1H(sortedEntries().map { case (_, base, size, prot) =>
|
val protBits = Mux(addr < UInt(start),
|
||||||
(addr >= UInt(base) && addr < UInt(base + size),
|
Bits(AddrMapConsts.RWX, 3),
|
||||||
new AddrMapProt().fromBits(Bits(prot, 3)))
|
Mux1H(sortedEntries().map { case (_, base, size, prot) =>
|
||||||
})
|
(addr >= UInt(base) && addr < UInt(base + size), Bits(prot, 3))
|
||||||
|
}))
|
||||||
|
new AddrMapProt().fromBits(protBits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,26 +479,6 @@ class NastiCrossbar(nMasters: Int, nSlaves: Int, routeSel: UInt => UInt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object NastiMultiChannelRouter {
|
|
||||||
def apply(master: NastiIO, nChannels: Int)(implicit p: Parameters): Vec[NastiIO] = {
|
|
||||||
if (nChannels == 1) {
|
|
||||||
Vec(master)
|
|
||||||
} else {
|
|
||||||
val dataBytes = p(MIFDataBits) * p(MIFDataBeats) / 8
|
|
||||||
val selOffset = log2Up(dataBytes)
|
|
||||||
val selBits = log2Ceil(nChannels)
|
|
||||||
// Consecutive blocks route to alternating channels
|
|
||||||
val routeSel = (addr: UInt) => {
|
|
||||||
val sel = addr(selOffset + selBits - 1, selOffset)
|
|
||||||
Vec.tabulate(nChannels)(i => sel === UInt(i)).toBits
|
|
||||||
}
|
|
||||||
val router = Module(new NastiRouter(nChannels, routeSel))
|
|
||||||
router.io.master <> master
|
|
||||||
router.io.slave
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NastiInterconnectIO(val nMasters: Int, val nSlaves: Int)
|
class NastiInterconnectIO(val nMasters: Int, val nSlaves: Int)
|
||||||
(implicit p: Parameters) extends Bundle {
|
(implicit p: Parameters) extends Bundle {
|
||||||
/* This is a bit confusing. The interconnect is a slave to the masters and
|
/* This is a bit confusing. The interconnect is a slave to the masters and
|
||||||
@ -517,10 +497,8 @@ abstract class NastiInterconnect(implicit p: Parameters) extends NastiModule()(p
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NastiRecursiveInterconnect(
|
class NastiRecursiveInterconnect(
|
||||||
val nMasters: Int,
|
val nMasters: Int, val nSlaves: Int,
|
||||||
val nSlaves: Int,
|
addrmap: AddrMap, base: BigInt)
|
||||||
addrmap: AddrMap,
|
|
||||||
base: BigInt = 0)
|
|
||||||
(implicit p: Parameters) extends NastiInterconnect()(p) {
|
(implicit p: Parameters) extends NastiInterconnect()(p) {
|
||||||
var lastEnd = base
|
var lastEnd = base
|
||||||
var slaveInd = 0
|
var slaveInd = 0
|
||||||
@ -530,13 +508,16 @@ class NastiRecursiveInterconnect(
|
|||||||
addrmap.zipWithIndex.foreach { case (AddrMapEntry(name, startOpt, region), i) =>
|
addrmap.zipWithIndex.foreach { case (AddrMapEntry(name, startOpt, region), i) =>
|
||||||
val start = startOpt.getOrElse(lastEnd)
|
val start = startOpt.getOrElse(lastEnd)
|
||||||
val size = region.size
|
val size = region.size
|
||||||
realAddrMap(i) = (start, size)
|
|
||||||
lastEnd = start + size
|
|
||||||
|
|
||||||
require(bigIntPow2(size),
|
require(bigIntPow2(size),
|
||||||
s"Region $name size $size is not a power of 2")
|
s"Region $name size $size is not a power of 2")
|
||||||
require(start % size == 0,
|
require(start % size == 0,
|
||||||
f"Region $name start address 0x$start%x not divisible by 0x$size%x" )
|
f"Region $name start address 0x$start%x not divisible by 0x$size%x" )
|
||||||
|
require(start >= lastEnd,
|
||||||
|
f"Region $name start address 0x$start%x before previous region end")
|
||||||
|
|
||||||
|
realAddrMap(i) = (start, size)
|
||||||
|
lastEnd = start + size
|
||||||
}
|
}
|
||||||
|
|
||||||
val routeSel = (addr: UInt) => {
|
val routeSel = (addr: UInt) => {
|
||||||
@ -567,10 +548,6 @@ class NastiRecursiveInterconnect(
|
|||||||
o <> s
|
o <> s
|
||||||
slaveInd += subSlaves
|
slaveInd += subSlaves
|
||||||
}
|
}
|
||||||
case MemChannels(_, nchannels, _) =>
|
|
||||||
require(nchannels == 1, "Recursive interconnect cannot handle MultiChannel interface")
|
|
||||||
io.slaves(slaveInd) <> xbarSlave
|
|
||||||
slaveInd += 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,69 +562,37 @@ class ChannelHelper(nChannels: Int)
|
|||||||
val blockOffset = selOffset + chanSelBits
|
val blockOffset = selOffset + chanSelBits
|
||||||
|
|
||||||
def getSelect(addr: UInt) =
|
def getSelect(addr: UInt) =
|
||||||
addr(blockOffset - 1, selOffset)
|
if (nChannels > 1) addr(blockOffset - 1, selOffset) else UInt(0)
|
||||||
|
|
||||||
def getAddr(addr: UInt) =
|
def getAddr(addr: UInt) =
|
||||||
Cat(addr(nastiXAddrBits - 1, blockOffset), addr(selOffset - 1, 0))
|
if (nChannels > 1)
|
||||||
|
Cat(addr(nastiXAddrBits - 1, blockOffset), addr(selOffset - 1, 0))
|
||||||
|
else addr
|
||||||
}
|
}
|
||||||
|
|
||||||
/** NASTI interconnect for multi-channel memory + regular IO
|
class NastiMemoryInterconnect(
|
||||||
* We do routing for the memory channels differently from the IO ports
|
nBanksPerChannel: Int, nChannels: Int)
|
||||||
* Routing memory banks onto memory channels is done via arbiters
|
(implicit p: Parameters) extends NastiInterconnect()(p) {
|
||||||
* (N-to-1 correspondence between banks and channels)
|
|
||||||
* Routing extra NASTI masters to memory requires a channel selecting router
|
|
||||||
* Routing anything to IO just uses standard recursive interconnect
|
|
||||||
*/
|
|
||||||
class NastiPerformanceInterconnect(
|
|
||||||
nBanksPerChannel: Int,
|
|
||||||
nChannels: Int,
|
|
||||||
nExtraMasters: Int,
|
|
||||||
nExtraSlaves: Int,
|
|
||||||
addrmap: AddrMap)(implicit p: Parameters) extends NastiInterconnect()(p) {
|
|
||||||
|
|
||||||
val nBanks = nBanksPerChannel * nChannels
|
val nBanks = nBanksPerChannel * nChannels
|
||||||
val nMasters = nBanks + nExtraMasters
|
val nMasters = nBanks
|
||||||
val nSlaves = nChannels + nExtraSlaves
|
val nSlaves = nChannels
|
||||||
|
|
||||||
val split = addrmap.head.region.size
|
|
||||||
val iomap = new AddrMap(addrmap.tail)
|
|
||||||
|
|
||||||
def routeMemOrIO(addr: UInt): UInt = {
|
|
||||||
Cat(addr >= UInt(split), addr < UInt(split))
|
|
||||||
}
|
|
||||||
|
|
||||||
val chanHelper = new ChannelHelper(nChannels)
|
val chanHelper = new ChannelHelper(nChannels)
|
||||||
|
|
||||||
def connectChannel(outer: NastiIO, inner: NastiIO) {
|
def connectChannel(outer: NastiIO, inner: NastiIO) {
|
||||||
outer <> inner
|
outer <> inner
|
||||||
outer.ar.bits.addr := chanHelper.getAddr(inner.ar.bits.addr)
|
outer.ar.bits.addr := chanHelper.getAddr(inner.ar.bits.addr)
|
||||||
outer.aw.bits.addr := chanHelper.getAddr(inner.aw.bits.addr)
|
outer.aw.bits.addr := chanHelper.getAddr(inner.aw.bits.addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
val topRouters = List.fill(nMasters){Module(new NastiRouter(2, routeMemOrIO(_)))}
|
|
||||||
topRouters.zip(io.masters).foreach {
|
|
||||||
case (router, master) => router.io.master <> master
|
|
||||||
}
|
|
||||||
val channelRouteFunc = (addr: UInt) => UIntToOH(chanHelper.getSelect(addr))
|
|
||||||
val channelXbar = Module(new NastiCrossbar(nExtraMasters, nChannels, channelRouteFunc))
|
|
||||||
channelXbar.io.masters <> topRouters.drop(nBanks).map(_.io.slave(0))
|
|
||||||
|
|
||||||
for (i <- 0 until nChannels) {
|
for (i <- 0 until nChannels) {
|
||||||
/* Bank assignments to channels are strided so that consecutive banks
|
/* Bank assignments to channels are strided so that consecutive banks
|
||||||
* map to different channels. That way, consecutive cache lines also
|
* map to different channels. That way, consecutive cache lines also
|
||||||
* map to different channels */
|
* map to different channels */
|
||||||
val banks = (i until nBanks by nChannels).map(j => topRouters(j).io.slave(0))
|
val banks = (i until nBanks by nChannels).map(j => io.masters(j))
|
||||||
val extra = channelXbar.io.slaves(i)
|
|
||||||
|
|
||||||
val channelArb = Module(new NastiArbiter(nBanksPerChannel + nExtraMasters))
|
val channelArb = Module(new NastiArbiter(nBanksPerChannel))
|
||||||
channelArb.io.master <> (banks :+ extra)
|
channelArb.io.master <> banks
|
||||||
connectChannel(io.slaves(i), channelArb.io.slave)
|
connectChannel(io.slaves(i), channelArb.io.slave)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ioslaves = Vec(io.slaves.drop(nChannels))
|
|
||||||
val iomasters = topRouters.map(_.io.slave(1))
|
|
||||||
val ioxbar = Module(new NastiRecursiveInterconnect(
|
|
||||||
nMasters, nExtraSlaves, iomap, split))
|
|
||||||
ioxbar.io.masters <> iomasters
|
|
||||||
ioslaves <> ioxbar.io.slaves
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user