diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala index 36254d47..f763d5b6 100644 --- a/src/main/scala/coreplex/Configs.scala +++ b/src/main/scala/coreplex/Configs.scala @@ -124,8 +124,7 @@ class BaseCoreplexConfig extends Config ( case UseCompressed => true case DMKey => new DefaultDebugModuleConfig(site(NTiles), site(XLen)) case NCustomMRWCSRs => 0 - case ResetVector => BigInt(0x1000) - case MtvecInit => BigInt(0x1010) + case MtvecInit => None case MtvecWritable => true //Uncore Paramters case LNEndpoints => site(TLKey(site(TLId))).nManagers + site(TLKey(site(TLId))).nClients diff --git a/src/main/scala/coreplex/Coreplex.scala b/src/main/scala/coreplex/Coreplex.scala index 3523293d..1cd8add5 100644 --- a/src/main/scala/coreplex/Coreplex.scala +++ b/src/main/scala/coreplex/Coreplex.scala @@ -58,6 +58,7 @@ abstract class Coreplex(implicit val p: Parameters, implicit val c: CoreplexConf val debug = new DebugBusIO()(p).flip val clint = Vec(c.nTiles, new CoreplexLocalInterrupts).asInput val success = Bool(OUTPUT) + val resetVector = UInt(INPUT, p(XLen)) } val io = new CoreplexIO @@ -155,6 +156,7 @@ class DefaultCoreplex(tp: Parameters, tc: CoreplexConfig) extends Coreplex()(tp, tile.io.interrupts.seip.foreach(_ := plic.io.harts(plic.cfg.context(i, 'S'))) tile.io.interrupts.debug := debugModule.io.debugInterrupts(i) tile.io.hartid := i + tile.io.resetVector := io.resetVector } val tileSlavePorts = (0 until tc.nTiles) map (i => s"int:dmem$i") filter (ioAddrMap contains _) diff --git a/src/main/scala/junctions/addrmap.scala b/src/main/scala/junctions/addrmap.scala index 19613ae7..275e4ff3 100644 --- a/src/main/scala/junctions/addrmap.scala +++ b/src/main/scala/junctions/addrmap.scala @@ -125,7 +125,7 @@ class AddrMap( val brEnd = br.start + br.size val abOverlaps = ar.start < brEnd && br.start < arEnd require(!abOverlaps, - "region $an@0x${ar.start.toString(16)} overlaps region $bn@0x${br.start.toString(16)}") + s"region $an@0x${ar.start.toString(16)} overlaps region $bn@0x${br.start.toString(16)}") } def toRange: MemRange = MemRange(start, size, attr) diff --git a/src/main/scala/rocket/csr.scala b/src/main/scala/rocket/csr.scala index 8b3ecff0..e542d233 100644 --- a/src/main/scala/rocket/csr.scala +++ b/src/main/scala/rocket/csr.scala @@ -227,7 +227,11 @@ class CSRFile(implicit p: Parameters) extends CoreModule()(p) val reg_mcause = Reg(Bits(width = xLen)) val reg_mbadaddr = Reg(UInt(width = vaddrBitsExtended)) val reg_mscratch = Reg(Bits(width = xLen)) - val reg_mtvec = Reg(init=UInt(p(MtvecInit), paddrBits min xLen)) + val mtvecWidth = paddrBits min xLen + val reg_mtvec = p(MtvecInit) match { + case Some(addr) => Reg(init=UInt(addr, mtvecWidth)) + case None => Reg(UInt(width = mtvecWidth)) + } val reg_mucounteren = Reg(UInt(width = 32)) val reg_mscounteren = Reg(UInt(width = 32)) val delegable_counters = (BigInt(1) << (nPerfCounters + CSR.firstHPM)) - 1 diff --git a/src/main/scala/rocket/frontend.scala b/src/main/scala/rocket/frontend.scala index 25c47b80..7fdc37a5 100644 --- a/src/main/scala/rocket/frontend.scala +++ b/src/main/scala/rocket/frontend.scala @@ -35,6 +35,7 @@ class Frontend(implicit p: Parameters) extends CoreModule()(p) with HasL1CachePa val cpu = new FrontendIO().flip val ptw = new TLBPTWIO() val mem = new ClientUncachedTileLinkIO + val resetVector = UInt(INPUT, vaddrBitsExtended) } val icache = Module(new ICache(latency = 2)) @@ -45,7 +46,7 @@ class Frontend(implicit p: Parameters) extends CoreModule()(p) with HasL1CachePa val s1_speculative = Reg(Bool()) val s1_same_block = Reg(Bool()) val s2_valid = Reg(init=Bool(true)) - val s2_pc = Reg(init=UInt(p(ResetVector))) + val s2_pc = Reg(init=io.resetVector) val s2_btb_resp_valid = Reg(init=Bool(false)) val s2_btb_resp_bits = Reg(new BTBResp) val s2_xcpt_if = Reg(init=Bool(false)) diff --git a/src/main/scala/rocket/rocket.scala b/src/main/scala/rocket/rocket.scala index 61b013b2..4939412d 100644 --- a/src/main/scala/rocket/rocket.scala +++ b/src/main/scala/rocket/rocket.scala @@ -27,8 +27,7 @@ case object FastJAL extends Field[Boolean] case object CoreInstBits extends Field[Int] case object NCustomMRWCSRs extends Field[Int] case object MtvecWritable extends Field[Boolean] -case object MtvecInit extends Field[BigInt] -case object ResetVector extends Field[BigInt] +case object MtvecInit extends Field[Option[BigInt]] case object NBreakpoints extends Field[Int] case object NPerfCounters extends Field[Int] case object NPerfEvents extends Field[Int] diff --git a/src/main/scala/rocket/tile.scala b/src/main/scala/rocket/tile.scala index 1a868fa8..9280bae2 100644 --- a/src/main/scala/rocket/tile.scala +++ b/src/main/scala/rocket/tile.scala @@ -34,6 +34,7 @@ abstract class Tile(clockSignal: Clock = null, resetSignal: Bool = null) val hartid = UInt(INPUT, p(XLen)) val interrupts = new TileInterrupts().asInput val slave = (p(DataScratchpadSize) > 0).option(new ClientUncachedTileLinkIO().flip) + val resetVector = UInt(INPUT, p(XLen)) } val io = new TileIO @@ -58,6 +59,7 @@ class RocketTile(clockSignal: Clock = null, resetSignal: Bool = null) core.io.interrupts := io.interrupts core.io.hartid := io.hartid icache.io.cpu <> core.io.imem + icache.io.resetVector := io.resetVector val fpuOpt = p(FPUKey).map(cfg => Module(new FPU(cfg))) fpuOpt.foreach(fpu => core.io.fpu <> fpu.io) diff --git a/src/main/scala/rocket/util.scala b/src/main/scala/rocket/util.scala index 182c7ba9..a311364a 100644 --- a/src/main/scala/rocket/util.scala +++ b/src/main/scala/rocket/util.scala @@ -127,21 +127,28 @@ object Split } // a counter that clock gates most of its MSBs using the LSB carry-out -case class WideCounter(width: Int, inc: UInt = UInt(1)) +case class WideCounter(width: Int, inc: UInt = UInt(1), reset: Boolean = true) { private val isWide = width > 2*inc.getWidth private val smallWidth = if (isWide) inc.getWidth max log2Up(width) else width - private val small = Reg(init=UInt(0, smallWidth)) + private val small = if (reset) Reg(init=UInt(0, smallWidth)) else Reg(UInt(width = smallWidth)) private val nextSmall = small +& inc small := nextSmall private val large = if (isWide) { - val r = Reg(init=UInt(0, width - smallWidth)) - when (nextSmall(smallWidth)) { r := r + UInt(1) } + val r = if (reset) Reg(init=UInt(0, width - smallWidth)) else Reg(UInt(width = width - smallWidth)) + when (nextSmall(smallWidth)) { r := r +& UInt(1) } r } else null val value = if (isWide) Cat(large, small) else small + lazy val carryOut = { + val lo = (small ^ nextSmall) >> 1 + if (!isWide) lo else { + val hi = Mux(nextSmall(smallWidth), large ^ (large +& UInt(1)), UInt(0)) >> 1 + Cat(hi, lo) + } + } def := (x: UInt) = { small := x diff --git a/src/main/scala/rocketchip/BaseTop.scala b/src/main/scala/rocketchip/BaseTop.scala index 31785603..0e18916b 100644 --- a/src/main/scala/rocketchip/BaseTop.scala +++ b/src/main/scala/rocketchip/BaseTop.scala @@ -27,6 +27,10 @@ abstract class BaseTop(q: Parameters) extends LazyModule { val pBusMasters = new RangeManager val pDevices = new ResourceManager[AddrMapEntry] + // Add a peripheral bus + val peripheryBus = LazyModule(new TLXbar) + lazy val peripheryManagers = peripheryBus.node.edgesIn(0).manager.managers + lazy val c = CoreplexConfig( nTiles = q(NTiles), nExtInterrupts = pInterrupts.sum, @@ -36,16 +40,14 @@ abstract class BaseTop(q: Parameters) extends LazyModule { hasExtMMIOPort = true ) - lazy val genGlobalAddrMap = GenerateGlobalAddrMap(q, pDevices.get) + lazy val genGlobalAddrMap = GenerateGlobalAddrMap(q, pDevices.get, peripheryManagers) private val qWithMap = q.alterPartial({case GlobalAddrMap => genGlobalAddrMap}) - lazy val genConfigString = GenerateConfigString(qWithMap, c, pDevices.get) + lazy val genConfigString = GenerateConfigString(qWithMap, c, pDevices.get, peripheryManagers) implicit val p = qWithMap.alterPartial({ case ConfigString => genConfigString case NCoreplexExtClients => pBusMasters.sum}) - // Add a peripheral bus - val peripheryBus = LazyModule(new TLXbar) val legacy = LazyModule(new TLLegacy()(p.alterPartial({ case TLId => "L2toMMIO" }))) peripheryBus.node := TLBuffer(TLWidthWidget(TLHintHandler(legacy.node), legacy.tlDataBytes)) @@ -55,7 +57,7 @@ class BaseTopBundle(val p: Parameters, val c: Coreplex) extends ParameterizedBun val success = Bool(OUTPUT) } -class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, b: Coreplex => B) extends LazyModuleImp(l) { +abstract class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, b: Coreplex => B) extends LazyModuleImp(l) { val outer: L = l val coreplex = p(BuildCoreplex)(p, outer.c) @@ -72,7 +74,12 @@ class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle](val p: Parameters, l: L, val name = entry.name val start = entry.region.start val end = entry.region.start + entry.region.size - 1 - println(f"\t$name%s $start%x - $end%x") + val prot = entry.region.attr.prot + val protStr = (if ((prot & AddrMapProt.R) > 0) "R" else "") + + (if ((prot & AddrMapProt.W) > 0) "W" else "") + + (if ((prot & AddrMapProt.X) > 0) "X" else "") + val cacheable = if (entry.region.attr.cacheable) " [C]" else "" + println(f"\t$name%s $start%x - $end%x, $protStr$cacheable") } println("Generated Configuration String") diff --git a/src/main/scala/rocketchip/ExampleTop.scala b/src/main/scala/rocketchip/ExampleTop.scala index 3895273d..a9018c1f 100644 --- a/src/main/scala/rocketchip/ExampleTop.scala +++ b/src/main/scala/rocketchip/ExampleTop.scala @@ -21,6 +21,7 @@ class ExampleTopBundle(p: Parameters, c: Coreplex) extends BaseTopBundle(p, c) class ExampleTopModule[+L <: ExampleTop, +B <: ExampleTopBundle](p: Parameters, l: L, b: Coreplex => B) extends BaseTopModule(p, l, b) with PeripheryBootROMModule with PeripheryDebugModule with PeripheryExtInterruptsModule with PeripheryCoreplexLocalInterrupterModule with PeripheryMasterMemModule with PeripheryMasterMMIOModule with PeripherySlaveModule + with HardwiredResetVector /** Example Top with TestRAM */ class ExampleTopWithTestRAM(q: Parameters) extends ExampleTop(q) diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index f2d867cc..f7d6d5bb 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -289,10 +289,6 @@ trait PeripheryCoreplexLocalInterrupter extends LazyModule with HasPeripheryPara val clint = LazyModule(new CoreplexLocalInterrupter(clintConfig)(innerMMIOParams)) // The periphery bus is 32-bit, so we may need to adapt its width to XLen clint.node := TLFragmenter(TLWidthWidget(peripheryBus.node, 4), beatBytes, 256) - - // TL1 legacy - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("clint", MemRange(clintConfig.address, clintConfig.size, MemAttr(AddrMapProt.RW)))) } trait PeripheryCoreplexLocalInterrupterBundle { @@ -315,12 +311,10 @@ trait PeripheryBootROM extends LazyModule { implicit val p: Parameters val peripheryBus: TLXbar - val rom = LazyModule(new TLROM(0x1000, 0x1000, GenerateBootROM(p))) + val address = 0x1000 + val size = 0x1000 + val rom = LazyModule(new TLROM(address, size, GenerateBootROM(p, address)) { override def name = "bootrom" }) rom.node := TLFragmenter(peripheryBus.node, 4, 256) - - // TL1 legacy address map - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("bootrom", MemRange(0x1000, 4096, MemAttr(AddrMapProt.RX)))) } trait PeripheryBootROMBundle { @@ -342,12 +336,8 @@ trait PeripheryTestRAM extends LazyModule { val ramBase = 0x52000000 val ramSize = 0x1000 - val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1))) + val sram = LazyModule(new TLRAM(AddressSet(ramBase, ramSize-1)) { override def name = "testram" }) sram.node := TLFragmenter(peripheryBus.node, 4, 256) - - // TL1 legacy address map - val pDevices: ResourceManager[AddrMapEntry] - pDevices.add(AddrMapEntry("testram", MemRange(ramBase, ramSize, MemAttr(AddrMapProt.RW)))) } trait PeripheryTestRAMBundle { @@ -377,3 +367,10 @@ trait PeripheryTestBusMasterModule { implicit val p: Parameters val outer: PeripheryTestBusMaster } + +///// + +trait HardwiredResetVector { + val coreplex: Coreplex + coreplex.io.resetVector := UInt(0x1000) // boot ROM +} diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index bb32006b..387e23bb 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -8,6 +8,7 @@ import uncore.devices._ import rocket._ import rocket.Util._ import coreplex._ +import uncore.tilelink2._ import java.nio.file.{Files, Paths} import java.nio.{ByteBuffer, ByteOrder} @@ -51,7 +52,7 @@ class GlobalVariable[T] { } object GenerateGlobalAddrMap { - def apply(p: Parameters, pDevicesEntries: Seq[AddrMapEntry]) = { + def apply(p: Parameters, pDevicesEntries: Seq[AddrMapEntry], peripheryManagers: Seq[TLManagerParameters]) = { lazy val intIOAddrMap: AddrMap = { val entries = collection.mutable.ArrayBuffer[AddrMapEntry]() entries += AddrMapEntry("debug", MemSize(4096, MemAttr(AddrMapProt.RWX))) @@ -64,8 +65,26 @@ object GenerateGlobalAddrMap { new AddrMap(entries) } - lazy val tl2AddrMap = new AddrMap(pDevicesEntries, collapse = true) - lazy val extIOAddrMap = new AddrMap(AddrMapEntry("TL2", tl2AddrMap) +: p(ExtMMIOPorts), collapse = true) + lazy val tl2Devices = peripheryManagers.map { manager => + val cacheable = manager.regionType match { + case RegionType.CACHED => true + case RegionType.TRACKED => true + case _ => false + } + val attr = MemAttr( + (if (manager.supportsGet) AddrMapProt.R else 0) | + (if (manager.supportsPutFull) AddrMapProt.W else 0) | + (if (manager.executable) AddrMapProt.X else 0), cacheable) + val multi = manager.address.size > 1 + manager.address.zipWithIndex.map { case (address, i) => + require (address.contiguous) // TL1 needs this + val name = manager.name + (if (multi) ".%d".format(i) else "") + AddrMapEntry(name, MemRange(address.base, address.mask+1, attr)) + } + }.flatten + + lazy val tl2AddrMap = new AddrMap(tl2Devices, collapse = true) + lazy val extIOAddrMap = new AddrMap(AddrMapEntry("TL2", tl2AddrMap) +: (p(ExtMMIOPorts) ++ pDevicesEntries), collapse = true) val memBase = 0x80000000L val memSize = p(ExtMemSize) @@ -80,7 +99,7 @@ object GenerateGlobalAddrMap { } object GenerateConfigString { - def apply(p: Parameters, c: CoreplexConfig, pDevicesEntries: Seq[AddrMapEntry]) = { + def apply(p: Parameters, c: CoreplexConfig, pDevicesEntries: Seq[AddrMapEntry], peripheryManagers: Seq[TLManagerParameters]) = { val addrMap = p(GlobalAddrMap) val plicAddr = addrMap("io:int:plic").start val clint = CoreplexLocalInterrupterConfig(0, addrMap("io:ext:TL2:clint").start) @@ -136,33 +155,27 @@ object GenerateConfigString { } res append "};\n" pDevicesEntries foreach { entry => - val region = addrMap("io:ext:TL2:" + entry.name) + val region = addrMap("io:ext:" + entry.name) res append s"${entry.name} {\n" res append s" addr 0x${region.start.toString(16)};\n" res append s" size 0x${region.size.toString(16)}; \n" res append "}\n" } + peripheryManagers.foreach { manager => res append manager.dts } res append '\u0000' res.toString } } object GenerateBootROM { - def apply(p: Parameters) = { + def apply(p: Parameters, address: BigInt) = { val romdata = Files.readAllBytes(Paths.get(p(BootROMFile))) val rom = ByteBuffer.wrap(romdata) rom.order(ByteOrder.LITTLE_ENDIAN) - // for now, have the reset vector jump straight to memory - val memBase = ( - if (p(GlobalAddrMap) contains "mem") p(GlobalAddrMap)("mem") - else p(GlobalAddrMap)("io:int:dmem0") - ).start - val resetToMemDist = memBase - p(ResetVector) - require(resetToMemDist == (resetToMemDist.toInt >> 12 << 12)) - val configStringAddr = p(ResetVector).toInt + rom.capacity - + require(address == address.toInt) + val configStringAddr = address.toInt + rom.capacity require(rom.getInt(12) == 0, "Config string address position should not be occupied by code") rom.putInt(12, configStringAddr) diff --git a/src/main/scala/uncore/devices/Prci.scala b/src/main/scala/uncore/devices/Prci.scala index ff9800dc..1d324005 100644 --- a/src/main/scala/uncore/devices/Prci.scala +++ b/src/main/scala/uncore/devices/Prci.scala @@ -87,3 +87,6 @@ class CoreplexLocalInterrupter(c: CoreplexLocalInterrupterConfig)(implicit val p extends TLRegisterRouter(c.address, 0, c.size, None, c.beatBytes, false)( new TLRegBundle((c, p), _) with CoreplexLocalInterrupterBundle)( new TLRegModule((c, p), _, _) with CoreplexLocalInterrupterModule) +{ + override def name = "clint" // defaul is "CoreplexLocalInterrupter" +} diff --git a/src/main/scala/uncore/devices/Rom.scala b/src/main/scala/uncore/devices/Rom.scala index 8e31cbe5..2986f85c 100644 --- a/src/main/scala/uncore/devices/Rom.scala +++ b/src/main/scala/uncore/devices/Rom.scala @@ -8,11 +8,12 @@ import uncore.tilelink2._ import uncore.util._ import cde.{Parameters, Field} -class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], beatBytes: Int = 4) extends LazyModule +class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4) extends LazyModule { val node = TLManagerNode(beatBytes, TLManagerParameters( address = List(AddressSet(base, size-1)), regionType = RegionType.UNCACHED, + executable = executable, supportsGet = TransferSizes(1, beatBytes), fifoId = Some(0))) diff --git a/src/main/scala/uncore/tilelink2/AddressDecoder.scala b/src/main/scala/uncore/tilelink2/AddressDecoder.scala index e937d60d..ea7cacac 100644 --- a/src/main/scala/uncore/tilelink2/AddressDecoder.scala +++ b/src/main/scala/uncore/tilelink2/AddressDecoder.scala @@ -25,13 +25,25 @@ object AddressDecoder // Verify the user did not give us an impossible problem ports.combinations(2).foreach { case Seq(x, y) => x.foreach { a => y.foreach { b => - require (!a.overlaps(b)) // it must be possible to disambiguate addresses! + require (!a.overlaps(b)) // it must be possible to disambiguate ports! } } } + val maxBits = log2Ceil(ports.map(_.map(_.max).max).max + 1) val bits = (0 until maxBits).map(BigInt(1) << _).toSeq val selected = recurse(Seq(ports.map(_.sorted).sorted(portOrder)), bits) - selected.reduceLeft(_ | _) + val output = selected.reduceLeft(_ | _) + + // Modify the AddressSets to allow the new wider match functions + val widePorts = ports.map { _.map { _.widen(~output) } } + // Verify that it remains possible to disambiguate all ports + widePorts.combinations(2).foreach { case Seq(x, y) => + x.foreach { a => y.foreach { b => + require (!a.overlaps(b)) + } } + } + + output } // A simpler version that works for a Seq[Int] @@ -51,11 +63,12 @@ object AddressDecoder // pick the bit which minimizes the number of ports in each partition // as a secondary goal, reduce the number of AddressSets within a partition - val bigValue = 100000 - def bitScore(partitions: Partitions): Int = { + def bitScore(partitions: Partitions): Seq[Int] = { val maxPortsPerPartition = partitions.map(_.size).max + val sumPortsPerPartition = partitions.map(_.size).sum val maxSetsPerPartition = partitions.map(_.map(_.size).sum).max - maxPortsPerPartition * bigValue + maxSetsPerPartition + val sumSetsPerPartition = partitions.map(_.map(_.size).sum).sum + Seq(maxPortsPerPartition, sumPortsPerPartition, maxSetsPerPartition, sumSetsPerPartition) } def partitionPort(port: Port, bit: BigInt): (Port, Port) = { @@ -77,8 +90,8 @@ object AddressDecoder def partitionPartitions(partitions: Partitions, bit: BigInt): Partitions = { val partitioned_partitions = partitions.map(p => partitionPorts(p, bit)) - val case_a_partitions = partitioned_partitions.map(_._1) - val case_b_partitions = partitioned_partitions.map(_._2) + val case_a_partitions = partitioned_partitions.map(_._1).filter(!_.isEmpty) + val case_b_partitions = partitioned_partitions.map(_._2).filter(!_.isEmpty) val new_partitions = (case_a_partitions ++ case_b_partitions).sorted(partitionOrder) // Prevent combinational memory explosion; if two partitions are equal, keep only one // Note: AddressSets in a port are sorted, and ports in a partition are sorted. @@ -106,9 +119,9 @@ object AddressDecoder val score = bitScore(result) (score, bit, result) } - val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Int, BigInt, Partitions), Int](_._1)) + val (bestScore, bestBit, bestPartitions) = candidates.min(Ordering.by[(Seq[Int], BigInt, Partitions), Iterable[Int]](_._1.toIterable)) if (debug) println("=> Selected bit 0x%x".format(bestBit)) - if (bestScore < 2*bigValue) { + if (bestScore(0) <= 1) { if (debug) println("---") Seq(bestBit) } else { diff --git a/src/main/scala/uncore/tilelink2/Edges.scala b/src/main/scala/uncore/tilelink2/Edges.scala index ceab2ca5..78c354f8 100644 --- a/src/main/scala/uncore/tilelink2/Edges.scala +++ b/src/main/scala/uncore/tilelink2/Edges.scala @@ -231,7 +231,7 @@ class TLEdgeOut( // Transfers def Acquire(fromSource: UInt, toAddress: UInt, lgSize: UInt, growPermissions: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Acquire a.param := growPermissions @@ -245,7 +245,7 @@ class TLEdgeOut( def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val c = Wire(new TLBundleC(bundle)) c.opcode := TLMessages.Release c.param := shrinkPermissions @@ -260,7 +260,7 @@ class TLEdgeOut( def Release(fromSource: UInt, toAddress: UInt, lgSize: UInt, shrinkPermissions: UInt, data: UInt) = { require (manager.anySupportAcquire) - val legal = manager.supportsAcquire(toAddress, lgSize) + val legal = manager.supportsAcquireFast(toAddress, lgSize) val c = Wire(new TLBundleC(bundle)) c.opcode := TLMessages.ReleaseData c.param := shrinkPermissions @@ -308,7 +308,7 @@ class TLEdgeOut( // Accesses def Get(fromSource: UInt, toAddress: UInt, lgSize: UInt) = { require (manager.anySupportGet) - val legal = manager.supportsGet(toAddress, lgSize) + val legal = manager.supportsGetFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Get a.param := UInt(0) @@ -322,7 +322,7 @@ class TLEdgeOut( def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt) = { require (manager.anySupportPutFull) - val legal = manager.supportsPutFull(toAddress, lgSize) + val legal = manager.supportsPutFullFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.PutFullData a.param := UInt(0) @@ -336,7 +336,7 @@ class TLEdgeOut( def Put(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, mask : UInt) = { require (manager.anySupportPutPartial) - val legal = manager.supportsPutPartial(toAddress, lgSize) + val legal = manager.supportsPutPartialFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.PutPartialData a.param := UInt(0) @@ -350,7 +350,7 @@ class TLEdgeOut( def Arithmetic(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { require (manager.anySupportArithmetic) - val legal = manager.supportsArithmetic(toAddress, lgSize) + val legal = manager.supportsArithmeticFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.ArithmeticData a.param := atomic @@ -364,7 +364,7 @@ class TLEdgeOut( def Logical(fromSource: UInt, toAddress: UInt, lgSize: UInt, data: UInt, atomic: UInt) = { require (manager.anySupportLogical) - val legal = manager.supportsLogical(toAddress, lgSize) + val legal = manager.supportsLogicalFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.LogicalData a.param := atomic @@ -378,7 +378,7 @@ class TLEdgeOut( def Hint(fromSource: UInt, toAddress: UInt, lgSize: UInt, param: UInt) = { require (manager.anySupportHint) - val legal = manager.supportsHint(toAddress, lgSize) + val legal = manager.supportsHintFast(toAddress, lgSize) val a = Wire(new TLBundleA(bundle)) a.opcode := TLMessages.Hint a.param := param @@ -445,7 +445,7 @@ class TLEdgeIn( // Transfers def Probe(fromAddress: UInt, toSource: UInt, lgSize: UInt, capPermissions: UInt) = { require (client.anySupportProbe) - val legal = client.supportsProbe(fromAddress, lgSize) + val legal = client.supportsProbe(toSource, lgSize) val b = Wire(new TLBundleB(bundle)) b.opcode := TLMessages.Probe b.param := capPermissions diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index 30775583..6c339255 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -189,7 +189,7 @@ class TLFragmenter(minSize: Int, maxSize: Int, alwaysMin: Boolean = false) exten val maxLgHints = maxHints .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m))) // If this is infront of a single manager, these become constants - val find = manager.find(edgeIn.address(in.a.bits)) + val find = manager.findFast(edgeIn.address(in.a.bits)) val maxLgArithmetic = Mux1H(find, maxLgArithmetics) val maxLgLogical = Mux1H(find, maxLgLogicals) val maxLgGet = Mux1H(find, maxLgGets) diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala index deeb936b..77efc7d4 100644 --- a/src/main/scala/uncore/tilelink2/Fuzzer.scala +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -163,10 +163,12 @@ class TLFuzzer( edge.Hint(src, addr, size, UInt(0)) } else { (glegal, gbits) } + val legal_dest = edge.manager.containsSafe(addr) + // Pick a specific message to try to send val a_type_sel = noiseMaker(3, inc) - val legal = MuxLookup(a_type_sel, glegal, Seq( + val legal = legal_dest && MuxLookup(a_type_sel, glegal, Seq( UInt("b000") -> glegal, UInt("b001") -> pflegal, UInt("b010") -> pplegal, @@ -218,14 +220,18 @@ class ClockDivider extends BlackBox { class TLFuzzRAM extends LazyModule { val model = LazyModule(new TLRAMModel) - val ram = LazyModule(new TLRAM(AddressSet(0, 0x3ff))) + val ram = LazyModule(new TLRAM(AddressSet(0x800, 0x7ff))) + val ram2 = LazyModule(new TLRAM(AddressSet(0, 0x3ff), beatBytes = 16)) val gpio = LazyModule(new RRTest1(0x400)) val xbar = LazyModule(new TLXbar) + val xbar2= LazyModule(new TLXbar) val fuzz = LazyModule(new TLFuzzer(5000)) val cross = LazyModule(new TLAsyncCrossing) model.node := fuzz.node - xbar.node := TLWidthWidget(TLHintHandler(model.node), 16) + xbar2.node := model.node + ram2.node := TLFragmenter(xbar2.node, 16, 256) + xbar.node := TLWidthWidget(TLHintHandler(xbar2.node), 16) cross.node := TLFragmenter(TLBuffer(xbar.node), 4, 256) ram.node := cross.node gpio.node := TLFragmenter(TLBuffer(xbar.node), 4, 32) diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index 4e1bd12d..376f8e90 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -45,7 +45,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f // Who wants what? val address = edgeIn.address(in.a.bits) - val handleA = if (passthrough) !edgeOut.manager.supportsHint(address, edgeIn.size(in.a.bits)) else Bool(true) + val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true) val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint val hintWantsD = in.a.valid && hintBitsAtA val outerWantsD = out.d.valid @@ -57,7 +57,7 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f assert (!hintHoldsD || hintWantsD) in.d.valid := Mux(hintWinsD, hintWantsD, outerWantsD) - in.d.bits := Mux(hintWinsD, edgeIn.HintAck(in.a.bits, edgeOut.manager.findId(address)), out.d.bits) + in.d.bits := Mux(hintWinsD, edgeIn.HintAck(in.a.bits, edgeOut.manager.findIdStartFast(address)), out.d.bits) out.d.ready := in.d.ready && !hintHoldsD in.a.ready := Mux(hintBitsAtA, hintWinsD && in.d.ready, out.a.ready) diff --git a/src/main/scala/uncore/tilelink2/IntNodes.scala b/src/main/scala/uncore/tilelink2/IntNodes.scala index df3e6152..a4acc443 100644 --- a/src/main/scala/uncore/tilelink2/IntNodes.scala +++ b/src/main/scala/uncore/tilelink2/IntNodes.scala @@ -21,9 +21,19 @@ object IntRange implicit def apply(end: Int): IntRange = apply(0, end) } -case class IntSourceParameters(device: String, range: IntRange) +case class IntSourceParameters( + range: IntRange, + nodePath: Seq[IntBaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} + +case class IntSinkParameters( + nodePath: Seq[IntBaseNode] = Seq()) +{ + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") +} -case class IntSinkPortParameters() case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) { val num = sources.map(_.range.size).sum @@ -32,6 +42,9 @@ case class IntSourcePortParameters(sources: Seq[IntSourceParameters]) // The interrupts must perfectly cover the range require (sources.map(_.range.end).max == num) } + +case class IntSinkPortParameters(sinks: Seq[IntSinkParameters]) + case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters) object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] @@ -52,16 +65,21 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In // Cannot use bulk connect, because the widths could differ (bo zip bi) foreach { case (o, i) => i := o } } + + override def mixO(po: IntSourcePortParameters, node: IntBaseNode): IntSourcePortParameters = + po.copy(sources = po.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixI(pi: IntSinkPortParameters, node: IntBaseNode): IntSinkPortParameters = + pi.copy(sinks = pi.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) } case class IntIdentityNode() extends IdentityNode(IntImp) case class IntOutputNode() extends OutputNode(IntImp) case class IntInputNode() extends InputNode(IntImp) -case class IntSourceNode(device: String, num: Int) extends SourceNode(IntImp)( - IntSourcePortParameters(Seq(IntSourceParameters(device, num))), - (if (num == 0) 0 else 1) to 1) -case class IntSinkNode() extends SinkNode(IntImp)(IntSinkPortParameters()) +case class IntSourceNode(num: Int) extends SourceNode(IntImp)( + IntSourcePortParameters(Seq(IntSourceParameters(num))), (if (num == 0) 0 else 1) to 1) +case class IntSinkNode() extends SinkNode(IntImp)( + IntSinkPortParameters(Seq(IntSinkParameters()))) case class IntAdapterNode( sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters, @@ -75,7 +93,7 @@ class IntXbar extends LazyModule val intnode = IntAdapterNode( numSourcePorts = 1 to 1, // does it make sense to have more than one interrupt sink? numSinkPorts = 1 to 128, - sinkFn = { _ => IntSinkPortParameters() }, + sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) }, sourceFn = { seq => IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map { case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o))) diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index 9ab63c9c..bd07931b 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -24,7 +24,7 @@ object TLMonitor val mask = edge.full_mask(bundle) when (bundle.opcode === TLMessages.Acquire) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'A' channel carries Acquire type unsupported by manager" + extra) assert (source_ok, "'A' channel Acquire carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'A' channel Acquire smaller than a beat" + extra) assert (is_aligned, "'A' channel Acquire address not aligned to size" + extra) @@ -33,7 +33,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Get) { - assert (edge.manager.supportsGet(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra) + assert (edge.manager.supportsGetSafe(edge.address(bundle), bundle.size), "'A' channel carries Get type unsupported by manager" + extra) assert (source_ok, "'A' channel Get carries invalid source ID" + extra) assert (is_aligned, "'A' channel Get address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel Get carries invalid param" + extra) @@ -41,7 +41,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.PutFullData) { - assert (edge.manager.supportsPutFull(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra) + assert (edge.manager.supportsPutFullSafe(edge.address(bundle), bundle.size), "'A' channel carries PutFull type unsupported by manager" + extra) assert (source_ok, "'A' channel PutFull carries invalid source ID" + extra) assert (is_aligned, "'A' channel PutFull address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel PutFull carries invalid param" + extra) @@ -49,7 +49,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.PutPartialData) { - assert (edge.manager.supportsPutPartial(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra) + assert (edge.manager.supportsPutPartialSafe(edge.address(bundle), bundle.size), "'A' channel carries PutPartial type unsupported by manager" + extra) assert (source_ok, "'A' channel PutPartial carries invalid source ID" + extra) assert (is_aligned, "'A' channel PutPartial address not aligned to size" + extra) assert (bundle.param === UInt(0), "'A' channel PutPartial carries invalid param" + extra) @@ -57,7 +57,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.ArithmeticData) { - assert (edge.manager.supportsArithmetic(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra) + assert (edge.manager.supportsArithmeticSafe(edge.address(bundle), bundle.size), "'A' channel carries Arithmetic type unsupported by manager" + extra) assert (source_ok, "'A' channel Arithmetic carries invalid source ID" + extra) assert (is_aligned, "'A' channel Arithmetic address not aligned to size" + extra) assert (TLAtomics.isArithmetic(bundle.param), "'A' channel Arithmetic carries invalid opcode param" + extra) @@ -65,7 +65,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.LogicalData) { - assert (edge.manager.supportsLogical(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra) + assert (edge.manager.supportsLogicalSafe(edge.address(bundle), bundle.size), "'A' channel carries Logical type unsupported by manager" + extra) assert (source_ok, "'A' channel Logical carries invalid source ID" + extra) assert (is_aligned, "'A' channel Logical address not aligned to size" + extra) assert (TLAtomics.isLogical(bundle.param), "'A' channel Logical carries invalid opcode param" + extra) @@ -73,7 +73,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Hint) { - assert (edge.manager.supportsHint(edge.address(bundle), bundle.size), "'A' channel carries Hint type unsupported by manager" + extra) + assert (edge.manager.supportsHintSafe(edge.address(bundle), bundle.size), "'A' channel carries Hint type unsupported by manager" + extra) assert (source_ok, "'A' channel Hint carries invalid source ID" + extra) assert (is_aligned, "'A' channel Hint address not aligned to size" + extra) assert (bundle.mask === mask, "'A' channel Hint contains invalid mask" + extra) @@ -84,7 +84,7 @@ object TLMonitor assert (TLMessages.isB(bundle.opcode), "'B' channel has invalid opcode" + extra) // Reuse these subexpressions to save some firrtl lines - val address_ok = edge.manager.contains(bundle.source) + val address_ok = edge.manager.containsSafe(edge.address(bundle)) val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) val mask = edge.full_mask(bundle) @@ -150,7 +150,7 @@ object TLMonitor val source_ok = edge.client.contains(bundle.source) val is_aligned = edge.isHiAligned(bundle.addr_hi, bundle.size) && edge.isLoAligned(bundle.addr_lo, bundle.size) - val address_ok = edge.manager.contains(bundle.source) + val address_ok = edge.manager.containsSafe(edge.address(bundle)) when (bundle.opcode === TLMessages.ProbeAck) { assert (address_ok, "'C' channel ProbeAck carries unmanaged address" + extra) @@ -171,7 +171,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.Release) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'C' channel carries Release type unsupported by manager" + extra) assert (source_ok, "'C' channel Release carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel Release smaller than a beat" + extra) assert (is_aligned, "'C' channel Release address not aligned to size" + extra) @@ -180,7 +180,7 @@ object TLMonitor } when (bundle.opcode === TLMessages.ReleaseData) { - assert (edge.manager.supportsAcquire(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra) + assert (edge.manager.supportsAcquireSafe(edge.address(bundle), bundle.size), "'C' channel carries ReleaseData type unsupported by manager" + extra) assert (source_ok, "'C' channel ReleaseData carries invalid source ID" + extra) assert (bundle.size >= UInt(log2Ceil(edge.manager.beatBytes)), "'C' channel ReleaseData smaller than a beat" + extra) assert (is_aligned, "'C' channel ReleaseData address not aligned to size" + extra) diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index a0de1a84..c9482e69 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -17,6 +17,9 @@ abstract class NodeImp[PO, PI, EO, EI, B <: Data] def bundleO(eo: Seq[EO]): Vec[B] def bundleI(ei: Seq[EI]): Vec[B] def connect(bo: B, eo: EO, bi: B, ei: EI)(implicit sourceInfo: SourceInfo): Unit + // If you want to track parameters as they flow through nodes, overload these: + def mixO(po: PO, node: BaseNode[PO, PI, EO, EI, B]): PO = po + def mixI(pi: PI, node: BaseNode[PO, PI, EO, EI, B]): PI = pi } class RootNode @@ -59,12 +62,12 @@ class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( private lazy val oParams : Seq[PO] = { val o = oFn(oPorts.size, iPorts.map{ case (i, n) => n.oParams(i) }) reqE(oPorts.size, o.size) - o + o.map(imp.mixO(_, this)) } private lazy val iParams : Seq[PI] = { val i = iFn(iPorts.size, oPorts.map{ case (o, n) => n.iParams(o) }) reqE(i.size, iPorts.size) - i + i.map(imp.mixI(_, this)) } lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => imp.edgeO(o, n.iParams(i)) } diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 6423db3c..ca5fe0e7 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -82,23 +82,29 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] { // Forbid misaligned base address (and empty sets) require ((base & mask) == 0) + require (base >= 0) // TL2 address widths are not fixed => negative is ambiguous + // We do allow negative mask (=> ignore all high bits) - def contains(x: BigInt) = ~(~(x ^ base) | mask) == 0 - def contains(x: UInt) = ~(~(x ^ UInt(base)) | UInt(mask)) === UInt(0) + def contains(x: BigInt) = ((x ^ base) & ~mask) == 0 + def contains(x: UInt) = ((x ^ UInt(base)).zext() & SInt(~mask)) === SInt(0) // overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1) def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0 - // contains iff bitwise: x.mask => mask && contains(x.base) def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0 - // 1 less than the number of bytes to which the manager should be aligned - def alignment1 = ((mask + 1) & ~mask) - 1 - def max = base | mask - // A strided slave serves discontiguous ranges - def strided = alignment1 != mask + // The number of bytes to which the manager must be aligned + def alignment = ((mask + 1) & ~mask) + // Is this a contiguous memory range + def contiguous = alignment == mask+1 - // AddressSets have one natural Ordering (the containment order) + def finite = mask >= 0 + def max = { require (finite); base | mask } + + // Widen the match function to ignore all bits in imask + def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask) + + // AddressSets have one natural Ordering (the containment order, if contiguous) def compare(x: AddressSet) = { val primary = (this.base - x.base).signum // smallest address first val secondary = (x.mask - this.mask).signum // largest mask first @@ -106,7 +112,13 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] } // We always want to see things in hex - override def toString() = "AddressSet(0x%x, 0x%x)".format(base, mask) + override def toString() = { + if (mask >= 0) { + "AddressSet(0x%x, 0x%x)".format(base, mask) + } else { + "AddressSet(0x%x, ~0x%x)".format(base, ~mask) + } + } } case class TLManagerParameters( @@ -124,8 +136,10 @@ case class TLManagerParameters( supportsPutPartial: TransferSizes = TransferSizes.none, supportsHint: TransferSizes = TransferSizes.none, // If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order - fifoId: Option[Int] = None) + fifoId: Option[Int] = None, + customDTS: Option[String]= None) { + address.foreach { a => require (a.finite) } address.combinations(2).foreach({ case Seq(x,y) => require (!x.overlaps(y)) }) @@ -140,9 +154,22 @@ case class TLManagerParameters( supportsPutFull.max, supportsPutPartial.max).max + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") + + // Generate the config string (in future device tree) + lazy val dts = customDTS.getOrElse { + val header = s"${name} {\n" + val middle = address.map { a => + require (a.contiguous) // Config String is not so flexible + " addr 0x%x;\n size 0x%x;\n".format(a.base, a.mask+1) + } + val footer = "}\n" + header + middle.reduce(_ + _) + footer + } + // The device had better not support a transfer larger than it's alignment address.foreach({ case a => - require (a.alignment1 >= maxTransfer-1) + require (a.alignment >= maxTransfer) }) } @@ -182,41 +209,59 @@ case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _) val anySupportHint = managers.map(!_.supportsHint.none) .reduce(_ || _) + // Which bits suffice to distinguish between all managers + lazy val routingMask = AddressDecoder(managers.map(_.address)) + // These return Option[TLManagerParameters] for your convenience def find(address: BigInt) = managers.find(_.address.exists(_.contains(address))) def findById(id: Int) = managers.find(_.sinkId.contains(id)) // Synthesizable lookup methods - def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id))) - def findId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.sinkId.start))) + def findIdStartSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.sinkId.start))) + def findIdStartFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.start))) + def findIdEndSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.sinkId.end))) + def findIdEndFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.sinkId.end))) + + // The safe version will check the entire address + def findSafe(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _))) + // The fast version assumes the address is valid + def findFast(address: UInt) = Vec(managers.map(_.address.map(_.widen(~routingMask)).distinct.map(_.contains(address)).reduce(_ || _))) // Note: returns the actual fifoId + 1 or 0 if None - def findFifoId(address: UInt) = Mux1H(find(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) - def hasFifoId(address: UInt) = Mux1H(find(address), managers.map(m => Bool(m.fifoId.isDefined))) + def findFifoIdSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) + def findFifoIdFast(address: UInt) = Mux1H(findFast(address), managers.map(m => UInt(m.fifoId.map(_+1).getOrElse(0)))) + def hasFifoIdSafe(address: UInt) = Mux1H(findSafe(address), managers.map(m => Bool(m.fifoId.isDefined))) + def hasFifoIdFast(address: UInt) = Mux1H(findFast(address), managers.map(m => Bool(m.fifoId.isDefined))) - lazy val addressMask = AddressDecoder(managers.map(_.address)) - // !!! need a cheaper version of find, where we assume a valid address match exists - // Does this Port manage this ID/address? - def contains(address: UInt) = find(address).reduce(_ || _) + def containsSafe(address: UInt) = findSafe(address).reduce(_ || _) + // containsFast would be useless; it could always be true def containsById(id: UInt) = findById(id).reduce(_ || _) - private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = { + private def safety_helper(member: TLManagerParameters => TransferSizes, select: UInt => Vec[Bool])(address: UInt, lgSize: UInt) = { val allSame = managers.map(member(_) == member(managers(0))).reduce(_ && _) if (allSame) member(managers(0)).containsLg(lgSize) else { - Mux1H(find(address), managers.map(member(_).containsLg(lgSize))) + Mux1H(select(address), managers.map(member(_).containsLg(lgSize))) } } // Check for support of a given operation at a specific address - val supportsAcquire = safety_helper(_.supportsAcquire) _ - val supportsArithmetic = safety_helper(_.supportsArithmetic) _ - val supportsLogical = safety_helper(_.supportsLogical) _ - val supportsGet = safety_helper(_.supportsGet) _ - val supportsPutFull = safety_helper(_.supportsPutFull) _ - val supportsPutPartial = safety_helper(_.supportsPutPartial) _ - val supportsHint = safety_helper(_.supportsHint) _ + val supportsAcquireSafe = safety_helper(_.supportsAcquire, findSafe) _ + val supportsArithmeticSafe = safety_helper(_.supportsArithmetic, findSafe) _ + val supportsLogicalSafe = safety_helper(_.supportsLogical, findSafe) _ + val supportsGetSafe = safety_helper(_.supportsGet, findSafe) _ + val supportsPutFullSafe = safety_helper(_.supportsPutFull, findSafe) _ + val supportsPutPartialSafe = safety_helper(_.supportsPutPartial, findSafe) _ + val supportsHintSafe = safety_helper(_.supportsHint, findSafe) _ + + val supportsAcquireFast = safety_helper(_.supportsAcquire, findFast) _ + val supportsArithmeticFast = safety_helper(_.supportsArithmetic, findFast) _ + val supportsLogicalFast = safety_helper(_.supportsLogical, findFast) _ + val supportsGetFast = safety_helper(_.supportsGet, findFast) _ + val supportsPutFullFast = safety_helper(_.supportsPutFull, findFast) _ + val supportsPutPartialFast = safety_helper(_.supportsPutPartial, findFast) _ + val supportsHintFast = safety_helper(_.supportsHint, findFast) _ } case class TLClientParameters( @@ -240,6 +285,8 @@ case class TLClientParameters( supportsGet.max, supportsPutFull.max, supportsPutPartial.max).max + + val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") } case class TLClientPortParameters(clients: Seq[TLClientParameters]) { diff --git a/src/main/scala/uncore/tilelink2/RAMModel.scala b/src/main/scala/uncore/tilelink2/RAMModel.scala index d6609174..6d75475a 100644 --- a/src/main/scala/uncore/tilelink2/RAMModel.scala +++ b/src/main/scala/uncore/tilelink2/RAMModel.scala @@ -119,7 +119,7 @@ class TLRAMModel extends LazyModule val a_addr_hi = a.addr_hi | (a_beats1 & ~a_counter1) val a_base = edge.address(a) val a_mask = edge.mask(a_base, a_size) - val a_fifo = edge.manager.hasFifoId(a_base) + val a_fifo = edge.manager.hasFifoIdFast(a_base) // Grab the concurrency state we need val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi)) @@ -203,7 +203,7 @@ class TLRAMModel extends LazyModule val d_base = d_flight.base val d_addr_hi = d_base >> shift | (d_beats1 & ~d_counter1) val d_mask = edge.mask(d_base, d_size) - val d_fifo = edge.manager.hasFifoId(d_flight.base) + val d_fifo = edge.manager.hasFifoIdFast(d_flight.base) // Grab the concurrency state we need val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi)) @@ -222,7 +222,8 @@ class TLRAMModel extends LazyModule // Check the response is correct assert (d_size === d_flight.size) - assert (edge.manager.findId(d_flight.base) === d.sink) + assert (edge.manager.findIdStartFast(d_flight.base) <= d.sink) + assert (edge.manager.findIdEndFast (d_flight.base) > d.sink) // addr_lo is allowed to differ when (d_flight.opcode === TLMessages.Hint) { diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/uncore/tilelink2/RegMapper.scala index b20937f8..6678e500 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/uncore/tilelink2/RegMapper.scala @@ -2,8 +2,8 @@ package uncore.tilelink2 -import Chisel._ -import chisel3.util.{Irrevocable, IrrevocableIO} +import chisel3._ +import chisel3.util._ // A bus agnostic register interface to a register-based device @@ -36,6 +36,23 @@ object RegMapper regmap.combinations(2).foreach { case Seq((reg1, _), (reg2, _)) => require (reg1 != reg2) } + // Don't be an asshole... + regmap.foreach { reg => require (reg._1 >= 0) } + // Make sure registers fit + val inParams = in.bits.params + val inBits = inParams.indexBits + assert (regmap.map(_._1).max < (1 << inBits)) + + val out = Wire(Irrevocable(new RegMapperOutput(inParams))) + val front = Wire(Irrevocable(new RegMapperInput(inParams))) + front.bits := in.bits + + // Must this device pipeline the control channel? + val pipelined = regmap.map(_._2.map(_.pipelined)).flatten.reduce(_ || _) + val depth = concurrency.getOrElse(if (pipelined) 1 else 0) + require (depth >= 0) + require (!pipelined || depth > 0) + val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front // Convert to and from Bits def toBits(x: Int, tail: List[Boolean] = List.empty): List[Boolean] = @@ -44,36 +61,33 @@ object RegMapper // Find the minimal mask that can decide the register map val mask = AddressDecoder(regmap.map(_._1)) + val maskMatch = ~UInt(mask, width = inBits) val maskFilter = toBits(mask) val maskBits = maskFilter.filter(x => x).size // Calculate size and indexes into the register map - val endIndex = 1 << log2Ceil(regmap.map(_._1).max+1) - val params = RegMapperParams(log2Up(endIndex), bytes, in.bits.params.extraBits) val regSize = 1 << maskBits def regIndexI(x: Int) = ofBits((maskFilter zip toBits(x)).filter(_._1).map(_._2)) def regIndexU(x: UInt) = if (maskBits == 0) UInt(0) else Cat((maskFilter zip x.toBools).filter(_._1).map(_._2).reverse) + // Protection flag for undefined registers + val iRightReg = Array.fill(regSize) { Bool(true) } + val oRightReg = Array.fill(regSize) { Bool(true) } + // Flatten the regmap into (RegIndex:Int, Offset:Int, field:RegField) val flat = regmap.map { case (reg, fields) => val offsets = fields.scanLeft(0)(_ + _.width).init val index = regIndexI(reg) + val uint = UInt(reg, width = inBits) + if (undefZero) { + iRightReg(index) = ((front.bits.index ^ uint) & maskMatch) === UInt(0) + oRightReg(index) = ((back .bits.index ^ uint) & maskMatch) === UInt(0) + } // println("mapping 0x%x -> 0x%x for 0x%x/%d".format(reg, index, mask, maskBits)) (offsets zip fields) map { case (o, f) => (index, o, f) } }.flatten - val out = Wire(Irrevocable(new RegMapperOutput(params))) - val front = Wire(Irrevocable(new RegMapperInput(params))) - front.bits := in.bits - - // Must this device pipeline the control channel? - val pipelined = flat.map(_._3.pipelined).reduce(_ || _) - val depth = concurrency.getOrElse(if (pipelined) 1 else 0) - require (depth >= 0) - require (!pipelined || depth > 0) - val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front - // Forward declaration of all flow control signals val rivalid = Wire(Vec(flat.size, Bool())) val wivalid = Wire(Vec(flat.size, Bool())) @@ -122,10 +136,10 @@ object RegMapper } // Is the selected register ready? - val rifireMux = Vec(rifire.map(_.reduce(_ && _))) - val wifireMux = Vec(wifire.map(_.reduce(_ && _))) - val rofireMux = Vec(rofire.map(_.reduce(_ && _))) - val wofireMux = Vec(wofire.map(_.reduce(_ && _))) + val rifireMux = Vec(rifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)}) + val wifireMux = Vec(wifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)}) + val rofireMux = Vec(rofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)}) + val wofireMux = Vec(wofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)}) val iindex = regIndexU(front.bits.index) val oindex = regIndexU(back .bits.index) val iready = Mux(front.bits.read, rifireMux(iindex), wifireMux(iindex)) @@ -138,8 +152,8 @@ object RegMapper out.valid := back.valid && oready // Which register is touched? - val frontSel = UIntToOH(iindex) - val backSel = UIntToOH(oindex) + val frontSel = UIntToOH(iindex) & Cat(iRightReg.reverse) + val backSel = UIntToOH(oindex) & Cat(oRightReg.reverse) // Include the per-register one-hot selected criteria for (reg <- 0 until regSize) { @@ -159,9 +173,9 @@ object RegMapper } out.bits.read := back.bits.read - out.bits.data := Vec(dataOut)(oindex) + out.bits.data := Mux(Vec(oRightReg)(oindex), Vec(dataOut)(oindex), UInt(0)) out.bits.extra := back.bits.extra - (endIndex, out) + out } } diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index 7cf6184d..7b6bdc21 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -12,7 +12,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB supportsPutFull = TransferSizes(1, beatBytes), fifoId = Some(0))) // requests are handled in order { - require (!address.strided) + require (address.contiguous) // Calling this method causes the matching TL2 bundle to be // configured to route all requests to the listed RegFields. @@ -27,7 +27,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB val (sourceEnd, sourceOff) = (edge.bundle.sourceBits + sizeEnd, sizeEnd) val (addrLoEnd, addrLoOff) = (log2Up(beatBytes) + sourceEnd, sourceEnd) - val params = RegMapperParams(log2Up(address.mask+1), beatBytes, addrLoEnd) + val params = RegMapperParams(log2Up((address.mask+1)/beatBytes), beatBytes, addrLoEnd) val in = Wire(Decoupled(new RegMapperInput(params))) in.bits.read := a.bits.opcode === TLMessages.Get in.bits.index := a.bits.addr_hi @@ -36,10 +36,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Option[Int] = None, beatB in.bits.extra := Cat(edge.addr_lo(a.bits), a.bits.source, a.bits.size) // Invoke the register map builder - val (endIndex, out) = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) - - // All registers must fit inside the device address space - require (address.mask >= (endIndex-1)*beatBytes) + val out = RegMapper(beatBytes, concurrency, undefZero, in, mapping:_*) // No flow control needed in.valid := a.valid @@ -78,7 +75,7 @@ object TLRegisterNode abstract class TLRegisterRouterBase(address: AddressSet, interrupts: Int, concurrency: Option[Int], beatBytes: Int, undefZero: Boolean) extends LazyModule { val node = TLRegisterNode(address, concurrency, beatBytes, undefZero) - val intnode = IntSourceNode(name + s" @ ${address.base}", interrupts) + val intnode = IntSourceNode(interrupts) } case class TLRegBundleArg(interrupts: Vec[Vec[Bool]], in: Vec[TLBundle]) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index 8c226f01..0c71428e 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -26,15 +26,15 @@ class RRTestCombinational(val bits: Int, rvalid: Bool => Bool, wready: Bool => B val wdata = UInt(INPUT, width = bits) } - val rfire = io.rvalid && io.rready - val wfire = io.wvalid && io.wready val reg = Reg(UInt(width = bits)) - io.rvalid := rvalid(rfire) - io.wready := wready(wfire) + val rvalid_s = rvalid(io.rready) + val wready_s = wready(io.wvalid) + io.rvalid := rvalid_s + io.wready := wready_s io.rdata := reg - when (wfire) { reg := io.wdata } + when (io.wvalid && wready_s) { reg := io.wdata } } object RRTestCombinational @@ -43,19 +43,19 @@ object RRTestCombinational def always: Bool => Bool = _ => Bool(true) - def random: Bool => Bool = { fire => + def random: Bool => Bool = { ready => seed = seed + 1 val lfsr = LFSR16Seed(seed) - val reg = RegInit(Bool(true)) - reg := Mux(reg, !fire, lfsr(0) && lfsr(1)) - reg + val valid = RegInit(Bool(true)) + valid := Mux(valid, !ready, lfsr(0) && lfsr(1)) + valid } - def delay(x: Int): Bool => Bool = { fire => + def delay(x: Int): Bool => Bool = { ready => val reg = RegInit(UInt(0, width = log2Ceil(x+1))) - val ready = reg === UInt(0) - reg := Mux(fire, UInt(x), Mux(ready, UInt(0), reg - UInt(1))) - ready + val valid = reg === UInt(0) + reg := Mux(ready && valid, UInt(x), Mux(valid, UInt(0), reg - UInt(1))) + valid } def combo(bits: Int, rvalid: Bool => Bool, wready: Bool => Bool): RegField = { diff --git a/src/main/scala/uncore/tilelink2/SRAM.scala b/src/main/scala/uncore/tilelink2/SRAM.scala index 2970eb78..84a775f2 100644 --- a/src/main/scala/uncore/tilelink2/SRAM.scala +++ b/src/main/scala/uncore/tilelink2/SRAM.scala @@ -4,12 +4,12 @@ package uncore.tilelink2 import Chisel._ -class TLRAM(address: AddressSet, beatBytes: Int = 4) extends LazyModule +class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4) extends LazyModule { val node = TLManagerNode(beatBytes, TLManagerParameters( address = List(address), regionType = RegionType.UNCACHED, - executable = true, + executable = executable, supportsGet = TransferSizes(1, beatBytes), supportsPutPartial = TransferSizes(1, beatBytes), supportsPutFull = TransferSizes(1, beatBytes), diff --git a/src/main/scala/uncore/tilelink2/TLNodes.scala b/src/main/scala/uncore/tilelink2/TLNodes.scala index ea0d2c77..7e1c79b5 100644 --- a/src/main/scala/uncore/tilelink2/TLNodes.scala +++ b/src/main/scala/uncore/tilelink2/TLNodes.scala @@ -24,6 +24,11 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL TLMonitor.legalize(bo, eo) bi <> bo } + + override def mixO(po: TLClientPortParameters, node: TLBaseNode): TLClientPortParameters = + po.copy(clients = po.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) + override def mixI(pi: TLManagerPortParameters, node: TLBaseNode): TLManagerPortParameters = + pi.copy(managers = pi.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) } case class TLIdentityNode() extends IdentityNode(TLImp) diff --git a/src/main/scala/uncore/tilelink2/Xbar.scala b/src/main/scala/uncore/tilelink2/Xbar.scala index 9302ea10..4a5bbf40 100644 --- a/src/main/scala/uncore/tilelink2/Xbar.scala +++ b/src/main/scala/uncore/tilelink2/Xbar.scala @@ -75,6 +75,23 @@ class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extend val inputIdRanges = mapInputIds(node.edgesIn.map(_.client)) val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager)) + // Find a good mask for address decoding + val port_addrs = node.edgesOut.map(_.manager.managers.map(_.address).flatten) + val routingMask = AddressDecoder(port_addrs) + val route_addrs = port_addrs.map(_.map(_.widen(~routingMask)).distinct) + val outputPorts = route_addrs.map(seq => (addr: UInt) => seq.map(_.contains(addr)).reduce(_ || _)) + + // Print the mapping + if (false) { + println("Xbar mapping:") + route_addrs.foreach { p => + print(" ") + p.foreach { a => print(s" ${a}") } + println("") + } + println("--") + } + // We need an intermediate size of bundle with the widest possible identifiers val wide_bundle = io.in(0).params.union(io.out(0).params) @@ -145,11 +162,15 @@ class TLXbar(policy: (Vec[Bool], Bool) => Seq[Bool] = TLXbar.lowestIndex) extend in(i).e.ready := Mux1C(grantedEIO(i), out.map(_.e.ready)) } - val requestAIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.a.valid && o.manager.contains(o.address(i.a.bits)) }) }) - val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.b.valid && i .contains(o.b.bits.source) }) }) - val requestCIO = Vec(in.map { i => Vec(node.edgesOut.map { o => i.c.valid && o.manager.contains(o.address(i.c.bits)) }) }) - val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.d.valid && i .contains(o.d.bits.source) }) }) - val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => i.e.valid && o .contains(i.e.bits.sink) }) }) + val addressA = (in zip node.edgesIn) map { case (i, e) => (i.a.valid, e.address(i.a.bits)) } + val addressC = (in zip node.edgesIn) map { case (i, e) => (i.c.valid, e.address(i.c.bits)) } + + val requestAIO = Vec(addressA.map { i => Vec(outputPorts.map { o => i._1 && o(i._2) }) }) + val requestCIO = Vec(addressC.map { i => Vec(outputPorts.map { o => i._1 && o(i._2) }) }) + + val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.b.valid && i.contains(o.b.bits.source) }) }) + val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => o.d.valid && i.contains(o.d.bits.source) }) }) + val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => i.e.valid && o.contains(i.e.bits.sink) }) }) val beatsA = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats(i.a.bits) }) val beatsB = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats(o.b.bits) })