diff --git a/src/main/scala/diplomaticobjectmodel/logicaltree/LogicalTreeNode.scala b/src/main/scala/diplomaticobjectmodel/logicaltree/LogicalTreeNode.scala index f2f596c2a6a..71d701a9289 100644 --- a/src/main/scala/diplomaticobjectmodel/logicaltree/LogicalTreeNode.scala +++ b/src/main/scala/diplomaticobjectmodel/logicaltree/LogicalTreeNode.scala @@ -36,7 +36,7 @@ object LogicalModuleTree { def rootLogicalTreeNode: LogicalTreeNode = { val roots = tree.collect { case (k, _) if !tree.exists(_._2.contains(k)) => k } - assert(roots.size == 1, "Logical Tree contains more than one root.") + require(roots.size == 1, s"Logical Tree contains more than one root:\n$roots") roots.head } diff --git a/src/main/scala/groundtest/Configs.scala b/src/main/scala/groundtest/Configs.scala index 2f95e81e1dc..8288f2e9474 100644 --- a/src/main/scala/groundtest/Configs.scala +++ b/src/main/scala/groundtest/Configs.scala @@ -5,36 +5,67 @@ package freechips.rocketchip.groundtest import Chisel._ import freechips.rocketchip.config.Config +import freechips.rocketchip.devices.tilelink.{CLINTKey, PLICKey} +import freechips.rocketchip.devices.debug.{DebugModuleKey} import freechips.rocketchip.subsystem._ import freechips.rocketchip.system.BaseConfig import freechips.rocketchip.rocket.{DCacheParams} -import freechips.rocketchip.tile.{MaxHartIdBits, XLen} +import freechips.rocketchip.tile.{XLen} /** Actual testing target Configs */ -class TraceGenConfig extends Config(new WithTraceGen(List.fill(2){ DCacheParams(nSets = 16, nWays = 1) }) ++ new BaseConfig) +class TraceGenConfig extends Config( + new WithTraceGen(2)() ++ + new GroundTestBaseConfig +) -class TraceGenBufferlessConfig extends Config(new WithBufferlessBroadcastHub ++ new TraceGenConfig) +class TraceGenBufferlessConfig extends Config( + new WithBufferlessBroadcastHub ++ + new TraceGenConfig +) /* Composable Configs to set individual parameters */ -class WithTraceGen(params: Seq[DCacheParams], nReqs: Int = 8192) extends Config((site, here, up) => { - case GroundTestTilesKey => params.map { dcp => TraceGenParams( - dcache = Some(dcp), - wordBits = site(XLen), - addrBits = 32, - addrBag = { - val nSets = dcp.nSets - val nWays = dcp.nWays - val blockOffset = site(SystemBusKey).blockOffset - val nBeats = site(SystemBusKey).blockBeats - List.tabulate(nWays) { i => - Seq.tabulate(nBeats) { j => BigInt((j * 8) + ((i * nSets) << blockOffset)) } - }.flatten - }, - maxRequests = nReqs, - memStart = site(ExtMem).get.master.base, - numGens = params.size) +class GroundTestBaseConfig extends Config( + new BaseConfig().alter((site,here,up) => { + case DebugModuleKey => None + case CLINTKey => None + case PLICKey => None + }) +) + +class WithTraceGen( + n: Int = 2, + overrideIdOffset: Option[Int] = None, + overrideMemOffset: Option[BigInt] = None)( + params: Seq[DCacheParams] = List.fill(n){ DCacheParams(nSets = 16, nWays = 1) }, + nReqs: Int = 8192 +) extends Config((site, here, up) => { + case TilesLocated(InSubsystem) => { + val prev = up(TilesLocated(InSubsystem), site) + val idOffset = overrideIdOffset.getOrElse(prev.size) + val memOffset: BigInt = overrideMemOffset.orElse(site(ExtMem).map(_.master.base)).getOrElse(0x0L) + params.zipWithIndex.map { case (dcp, i) => + TraceGenTileAttachParams( + tileParams = TraceGenParams( + hartId = i + idOffset, + dcache = Some(dcp), + wordBits = site(XLen), + addrBits = 32, + addrBag = { + val nSets = dcp.nSets + val nWays = dcp.nWays + val blockOffset = site(SystemBusKey).blockOffset + val nBeats = site(SystemBusKey).blockBeats + List.tabulate(nWays) { i => + Seq.tabulate(nBeats) { j => BigInt((j * 8) + ((i * nSets) << blockOffset)) } + }.flatten + }, + maxRequests = nReqs, + memStart = memOffset, + numGens = params.size), + crossingParams = RocketCrossingParams() + ) + } ++ prev } - case MaxHartIdBits => log2Up(params.size) }) diff --git a/src/main/scala/groundtest/GroundTestSubsystem.scala b/src/main/scala/groundtest/GroundTestSubsystem.scala index f5d3881eeeb..bb330031357 100644 --- a/src/main/scala/groundtest/GroundTestSubsystem.scala +++ b/src/main/scala/groundtest/GroundTestSubsystem.scala @@ -3,30 +3,24 @@ package freechips.rocketchip.groundtest import Chisel._ -import freechips.rocketchip.config.{Field, Parameters} -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.diplomaticobjectmodel.model.OMInterrupt -import freechips.rocketchip.interrupts._ -import freechips.rocketchip.subsystem._ -import freechips.rocketchip.tilelink._ -import freechips.rocketchip.tile._ - -import scala.math.max - -case object TileId extends Field[Int] - -class GroundTestSubsystem(implicit p: Parameters) extends BaseSubsystem - with CanHaveMasterAXI4MemPort { - val tileParams = p(GroundTestTilesKey) - val tiles = tileParams.zipWithIndex.map { case(c, i) => LazyModule(c.build(i, p)) } - - tiles.map(_.masterNode).foreach { m => - sbus.fromTile(None, buffer = BufferParams.default){ m } - } - +import chisel3.dontTouch +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy.{AddressSet, LazyModule} +import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple} +import freechips.rocketchip.subsystem.{BaseSubsystem, BaseSubsystemModuleImp, HasTiles, CanHaveMasterAXI4MemPort} +import freechips.rocketchip.tilelink.{TLRAM, TLFragmenter} + +class GroundTestSubsystem(implicit p: Parameters) + extends BaseSubsystem + with HasTiles + with CanHaveMasterAXI4MemPort +{ val testram = LazyModule(new TLRAM(AddressSet(0x52000000, 0xfff), beatBytes=pbus.beatBytes)) pbus.coupleTo("TestRAM") { testram.node := TLFragmenter(pbus) := _ } + // No cores to monitor + def coreMonitorBundles = Nil + // No PLIC in ground test; so just sink the interrupts to nowhere IntSinkNode(IntSinkPortSimple()) :=* ibus.toPLIC @@ -38,6 +32,6 @@ class GroundTestSubsystemModuleImp[+L <: GroundTestSubsystem](_outer: L) extends outer.tiles.zipWithIndex.map { case(t, i) => t.module.constants.hartid := UInt(i) } - val status = DebugCombiner(outer.tiles.map(_.module.status)) - success := status.finished + val status = dontTouch(DebugCombiner(outer.tiles.collect { case t: GroundTestTile => t.module.status })) + success := outer.tileCeaseSinkNode.in.head._1.asUInt.andR } diff --git a/src/main/scala/groundtest/Status.scala b/src/main/scala/groundtest/Status.scala index 4601593ada8..c1059f06c22 100644 --- a/src/main/scala/groundtest/Status.scala +++ b/src/main/scala/groundtest/Status.scala @@ -6,7 +6,6 @@ import Chisel._ import freechips.rocketchip.util.ValidMux class GroundTestStatus extends Bundle { - val finished = Bool(OUTPUT) val timeout = Valid(UInt(width = 4)) val error = Valid(UInt(width = 4)) } @@ -14,10 +13,8 @@ class GroundTestStatus extends Bundle { object DebugCombiner { def apply(debugs: Seq[GroundTestStatus]): GroundTestStatus = { val out = Wire(new GroundTestStatus) - out.finished := debugs.map(_.finished).reduce(_ && _) out.timeout := ValidMux(debugs.map(_.timeout)) out.error := ValidMux(debugs.map(_.error)) out } } - diff --git a/src/main/scala/groundtest/Tile.scala b/src/main/scala/groundtest/Tile.scala index b4f25ba3afc..230bf58552b 100644 --- a/src/main/scala/groundtest/Tile.scala +++ b/src/main/scala/groundtest/Tile.scala @@ -17,8 +17,6 @@ trait GroundTestTileParams extends TileParams { val memStart: BigInt val maxRequests: Int val numGens: Int - - def build(i: Int, p: Parameters): GroundTestTile val icache = Some(ICacheParams()) val btb = None @@ -28,18 +26,18 @@ trait GroundTestTileParams extends TileParams { val dataScratchpadBytes = 0 } -case object GroundTestTilesKey extends Field[Seq[GroundTestTileParams]] - -abstract class GroundTestTile private (params: GroundTestTileParams, x: ClockCrossingType, q: Parameters) - extends BaseTile(params, x, HartsWontDeduplicate(params), q) +abstract class GroundTestTile( + params: GroundTestTileParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters +) extends BaseTile(params, crossing, lookup, q) + with SinksExternalInterrupts + with SourcesExternalNotifications { - def this(params: GroundTestTileParams)(implicit p: Parameters) = this(params, SynchronousCrossing(), p) - val intInwardNode: IntInwardNode = IntIdentityNode() + val cpuDevice: SimpleDevice = new SimpleDevice("groundtest", Nil) val intOutwardNode: IntOutwardNode = IntIdentityNode() val slaveNode: TLInwardNode = TLIdentityNode() - val ceaseNode: IntOutwardNode = IntIdentityNode() - val haltNode: IntOutwardNode = IntIdentityNode() - val wfiNode: IntOutwardNode = IntIdentityNode() val dcacheOpt = params.dcache.map { dc => LazyModule( if (dc.nMSHRs == 0) new DCache(hartId, crossing) diff --git a/src/main/scala/groundtest/TraceGen.scala b/src/main/scala/groundtest/TraceGen.scala index 4cefaca2f0c..27d295fa8fc 100644 --- a/src/main/scala/groundtest/TraceGen.scala +++ b/src/main/scala/groundtest/TraceGen.scala @@ -20,10 +20,12 @@ package freechips.rocketchip.groundtest import Chisel._ -import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy.{ClockCrossingType} import freechips.rocketchip.rocket._ import freechips.rocketchip.tile._ import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem.{TileCrossingParamsLike, CanAttachTile} import freechips.rocketchip.util._ // ======= @@ -57,15 +59,19 @@ import freechips.rocketchip.util._ // to repeatedly recompile with a different address bag.) case class TraceGenParams( - dcache: Option[DCacheParams] = Some(DCacheParams()), wordBits: Int, // p(XLen) addrBits: Int, // p(PAddrBits) addrBag: List[BigInt], // p(AddressBag) maxRequests: Int, memStart: BigInt, //p(ExtMem).base - numGens: Int) extends GroundTestTileParams { - def build(i: Int, p: Parameters): GroundTestTile = new TraceGenTile(i, this)(p) - val hartId = 0 + numGens: Int, + dcache: Option[DCacheParams] = Some(DCacheParams()), + hartId: Int = 0 +) extends InstantiableTileParams[TraceGenTile] with GroundTestTileParams +{ + def instantiate(crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters): TraceGenTile = { + new TraceGenTile(this, crossing, lookup) + } val beuAddr = None val blockerCtrlAddr = None val name = None @@ -94,6 +100,15 @@ trait HasTraceGenParams { require((1 << logAddressBagLen) == addressBagLen) } +case class TraceGenTileAttachParams( + tileParams: TraceGenParams, + crossingParams: TileCrossingParamsLike +) extends CanAttachTile { + type TileType = TraceGenTile + val lookup: LookupByHartIdImpl = HartsWontDeduplicate(tileParams) +} + + // ============ // Trace format // ============ @@ -578,9 +593,18 @@ class TraceGenerator(val params: TraceGenParams)(implicit val p: Parameters) ext // Trace-generator wrapper // ======================= -class TraceGenTile(hack: Int, val id: Int, val params: TraceGenParams, q: Parameters) extends GroundTestTile(params)(q) { - def this(id: Int, params: TraceGenParams)(implicit p: Parameters) = this(0, id, params, p) - val masterNode: TLOutwardNode = TLIdentityNode() := visibilityNode := dcacheOpt.map(_.node).getOrElse(TLIdentityNode()) +class TraceGenTile private( + val params: TraceGenParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters +) extends GroundTestTile(params, crossing, lookup, q) +{ + def this(params: TraceGenParams, crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters) = + this(params, crossing.crossingType, lookup, p) + + val masterNode: TLOutwardNode = TLIdentityNode() := visibilityNode := dcacheOpt.map(_.node).getOrElse(TLTempNode()) + override lazy val module = new TraceGenTileModuleImp(this) } @@ -595,10 +619,12 @@ class TraceGenTileModuleImp(outer: TraceGenTile) extends GroundTestTileModuleImp dcache.module.io.cpu <> dcacheIF.io.cache } - status.finished := tracegen.io.finished + outer.reportCease(Some(tracegen.io.finished)) + outer.reportHalt(Some(tracegen.io.timeout)) + outer.reportWFI(None) status.timeout.valid := tracegen.io.timeout status.timeout.bits := UInt(0) status.error.valid := Bool(false) - assert(!tracegen.io.timeout, s"TraceGen tile ${outer.id}: request timed out") + assert(!tracegen.io.timeout, s"TraceGen tile ${outer.tileParams.hartId}: request timed out") } diff --git a/src/main/scala/subsystem/Configs.scala b/src/main/scala/subsystem/Configs.scala index 62764d6bc8b..78ed31dcf18 100644 --- a/src/main/scala/subsystem/Configs.scala +++ b/src/main/scala/subsystem/Configs.scala @@ -17,7 +17,7 @@ class BaseSubsystemConfig extends Config ((site, here, up) => { // Tile parameters case PgLevels => if (site(XLen) == 64) 3 /* Sv39 */ else 2 /* Sv32 */ case XLen => 64 // Applies to all cores - case MaxHartIdBits => log2Up(site(RocketTilesKey).size) + case MaxHartIdBits => log2Up(site(TilesLocated(InSubsystem)).map(_.tileParams.hartId).max+1) // Interconnect parameters case SystemBusKey => SystemBusParams( beatBytes = site(XLen)/8, @@ -41,6 +41,8 @@ class BaseSubsystemConfig extends Config ((site, here, up) => { case DebugModuleKey => Some(DefaultDebugModuleParams(site(XLen))) case CLINTKey => Some(CLINTParams()) case PLICKey => Some(PLICParams()) + case TilesLocated(InSubsystem) => + LegacyTileFieldHelper(site(RocketTilesKey), site(RocketCrossingKey), RocketTileAttachParams.apply _) }) /* Composable partial function Configs to set individual parameters */ @@ -75,8 +77,10 @@ class WithCoherentBusTopology extends Config((site, here, up) => { l2 = site(BankedL2Key))) }) -class WithNBigCores(n: Int) extends Config((site, here, up) => { +class WithNBigCores(n: Int, overrideIdOffset: Option[Int] = None) extends Config((site, here, up) => { case RocketTilesKey => { + val prev = up(RocketTilesKey, site) + val idOffset = overrideIdOffset.getOrElse(prev.size) val big = RocketTileParams( core = RocketCoreParams(mulDiv = Some(MulDivParams( mulUnroll = 8, @@ -89,12 +93,14 @@ class WithNBigCores(n: Int) extends Config((site, here, up) => { icache = Some(ICacheParams( rowBits = site(SystemBusKey).beatBits, blockBytes = site(CacheBlockBytes)))) - List.tabulate(n)(i => big.copy(hartId = i)) + List.tabulate(n)(i => big.copy(hartId = i + idOffset)) ++ prev } }) -class WithNMedCores(n: Int) extends Config((site, here, up) => { +class WithNMedCores(n: Int, overrideIdOffset: Option[Int] = None) extends Config((site, here, up) => { case RocketTilesKey => { + val prev = up(RocketTilesKey, site) + val idOffset = overrideIdOffset.getOrElse(prev.size) val med = RocketTileParams( core = RocketCoreParams(fpu = None), btb = None, @@ -111,12 +117,14 @@ class WithNMedCores(n: Int) extends Config((site, here, up) => { nWays = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes)))) - List.tabulate(n)(i => med.copy(hartId = i)) + List.tabulate(n)(i => med.copy(hartId = i + idOffset)) ++ prev } }) -class WithNSmallCores(n: Int) extends Config((site, here, up) => { +class WithNSmallCores(n: Int, overrideIdOffset: Option[Int] = None) extends Config((site, here, up) => { case RocketTilesKey => { + val prev = up(RocketTilesKey, site) + val idOffset = overrideIdOffset.getOrElse(prev.size) val small = RocketTileParams( core = RocketCoreParams(useVM = false, fpu = None), btb = None, @@ -133,7 +141,7 @@ class WithNSmallCores(n: Int) extends Config((site, here, up) => { nWays = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes)))) - List.tabulate(n)(i => small.copy(hartId = i)) + List.tabulate(n)(i => small.copy(hartId = i + idOffset)) ++ prev } }) @@ -410,3 +418,17 @@ class WithScratchpadsOnly extends Config((site, here, up) => { scratch = Some(0x80000000L)))) } }) + +/** Boilerplate code for translating between the old XTilesParamsKey/XTilesCrossingKey pattern and new TilesLocated pattern */ +object LegacyTileFieldHelper { + def apply[TPT <: InstantiableTileParams[_], TCT <: TileCrossingParamsLike, TAP <: CanAttachTile]( + tileParams: Seq[TPT], + tcp: Seq[TCT], + apply: (TPT, TCT, LookupByHartIdImpl) => TAP): Seq[TAP] = + { + val crossingParams = heterogeneousOrGlobalSetting(tcp, tileParams.size) + tileParams.zip(crossingParams).map { case (t, c) => + apply(t, c, PriorityMuxHartIdFromSeq(tileParams)) + } + } +} diff --git a/src/main/scala/subsystem/HasTiles.scala b/src/main/scala/subsystem/HasTiles.scala index 399933c81aa..22ba7eae6be 100644 --- a/src/main/scala/subsystem/HasTiles.scala +++ b/src/main/scala/subsystem/HasTiles.scala @@ -4,24 +4,78 @@ package freechips.rocketchip.subsystem import Chisel._ import chisel3.dontTouch -import freechips.rocketchip.config.Parameters -import freechips.rocketchip.devices.debug.TLDebugModule -import freechips.rocketchip.devices.tilelink.{BasicBusBlocker, BasicBusBlockerParams, CLINT, CLINTConsts, TLPLIC, PLICKey} +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.devices.debug.{HasPeripheryDebug, HasPeripheryDebugModuleImp} +import freechips.rocketchip.devices.tilelink.{BasicBusBlocker, BasicBusBlockerParams, CLINTConsts, PLICKey, CanHavePeripheryPLIC, CanHavePeripheryCLINT} import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.diplomaticobjectmodel.logicaltree.{LogicalModuleTree} import freechips.rocketchip.interrupts._ -import freechips.rocketchip.tile.{BaseTile, LookupByHartId, LookupByHartIdImpl, TileParams, HasExternallyDrivenTileConstants} +import freechips.rocketchip.tile.{BaseTile, LookupByHartIdImpl, TileParams, HasExternallyDrivenTileConstants, InstantiableTileParams, PriorityMuxHartIdFromSeq} import freechips.rocketchip.tilelink._ import freechips.rocketchip.util._ -trait HasTiles extends HasCoreMonitorBundles { this: BaseSubsystem => - implicit val p: Parameters - val tiles: Seq[BaseTile] - protected def tileParams: Seq[TileParams] = tiles.map(_.tileParams) - def nTiles: Int = tileParams.size - def hartIdList: Seq[Int] = tileParams.map(_.hartId) - def localIntCounts: Seq[Int] = tileParams.map(_.core.nLocalInterrupts) +/** Entry point for Config-uring the presence of Tiles */ +case class TilesLocated(loc: HierarchicalLocation) extends Field[Seq[CanAttachTile]](Nil) + +/** An interface for describing the parameteization of how Tiles are connected to interconnects */ +trait TileCrossingParamsLike { + val crossingType: ClockCrossingType + val master: TilePortParamsLike + val slave: TilePortParamsLike +} - // define some nodes that are useful for collecting or driving tile interrupts +/** An interface for describing the parameterization of how a particular tile port is connected to an interconnect */ +trait TilePortParamsLike { + // the subnetwork location of the interconnect to which this tile port should be connected + def where: TLBusWrapperLocation + // allows port-specific adapters to be injected into the interconnect side of the attachment point + def injectNode(context: Attachable)(implicit p: Parameters): TLNode +} + +/** A default implementation of parameterizing the connectivity of the port where the tile is the master. + * Optional timing buffers and/or an optional CacheCork can be inserted in the interconnect's clock domain. + */ +case class TileMasterPortParams( + buffers: Int = 0, + cork: Option[Boolean] = None, + where: TLBusWrapperLocation = SBUS +) extends TilePortParamsLike { + def injectNode(context: Attachable)(implicit p: Parameters): TLNode = { + (TLBuffer(buffers) :=* cork.map { u => TLCacheCork(unsafe = u) } .getOrElse { TLTempNode() }) + } +} + +/** A default implementation of parameterizing the connectivity of the port giving access to slaves inside the tile. + * Optional timing buffers and/or an optional BusBlocker adapter can be inserted in the interconnect's clock domain. + */ +case class TileSlavePortParams( + buffers: Int = 0, + blockerCtrlAddr: Option[BigInt] = None, + blockerCtrlWhere: TLBusWrapperLocation = CBUS, + where: TLBusWrapperLocation = CBUS +) extends TilePortParamsLike { + def injectNode(context: Attachable)(implicit p: Parameters): TLNode = { + val controlBus = context.locateTLBusWrapper(where) + val blockerBus = context.locateTLBusWrapper(blockerCtrlWhere) + blockerCtrlAddr + .map { BasicBusBlockerParams(_, blockerBus.beatBytes, controlBus.beatBytes) } + .map { bbbp => + val blocker = LazyModule(new BasicBusBlocker(bbbp)) + blockerBus.coupleTo("tile_slave_port_bus_blocker") { blocker.controlNode := TLFragmenter(blockerBus) := _ } + blocker.node :*= TLBuffer(buffers) + } .getOrElse { TLBuffer(buffers) } + } +} + +/** These are sources of interrupts that are driven into the tile. + * They need to be instantiated before tiles are attached to the subsystem containing them. + */ +trait HasTileInterruptSources + extends CanHavePeripheryPLIC + with CanHavePeripheryCLINT + with HasPeripheryDebug +{ this: BaseSubsystem => // TODO ideally this bound would be softened to LazyModule + // meipNode is used to create a single bit subsystem input in Configs without a PLIC val meipNode = p(PLICKey) match { case Some(_) => None case None => Some(IntNexusNode( @@ -30,7 +84,12 @@ trait HasTiles extends HasCoreMonitorBundles { this: BaseSubsystem => outputRequiresInput = false, inputRequiresOutput = false)) } +} +/** These are sinks of notifications that are driven out from the tile. + * They need to be instantiated before tiles are attached to the subsystem containing them. + */ +trait HasTileNotificationSinks { this: LazyModule => val tileHaltXbarNode = IntXbar(p) val tileHaltSinkNode = IntSinkNode(IntSinkPortSimple()) tileHaltSinkNode := tileHaltXbarNode @@ -42,41 +101,100 @@ trait HasTiles extends HasCoreMonitorBundles { this: BaseSubsystem => val tileCeaseXbarNode = IntXbar(p) val tileCeaseSinkNode = IntSinkNode(IntSinkPortSimple()) tileCeaseSinkNode := tileCeaseXbarNode +} + +/** HasTiles adds a Config-urable sequence of tiles of any type + * to the subsystem class into which it is mixed. + */ +trait HasTiles extends HasCoreMonitorBundles with DefaultTileContextType +{ this: BaseSubsystem => // TODO: ideally this bound would be softened to Attachable + implicit val p: Parameters - protected def connectMasterPortsToSBus(tile: BaseTile, crossing: RocketCrossingParams) { - locateTLBusWrapper(crossing.master.where).coupleFrom(tile.tileParams.name.getOrElse("tile")) { bus => - (bus :=* - TLBuffer(crossing.master.buffers) :=* - crossing.master.cork - .map { u => TLCacheCork(unsafe = u) } - .map { _ :=* tile.crossMasterPort() } - .getOrElse { tile.crossMasterPort() }) + // Actually instantiate all tiles, in order based on statically-assigned hartids + val tileAttachParams: Seq[CanAttachTile] = p(TilesLocated(location)).sortBy(_.tileParams.hartId) + val tiles: Seq[BaseTile] = tileAttachParams.map(_.instantiate(p)) + + // Helper functions for accessing certain parameters the are popular to refer to in subsystem code + val tileParams: Seq[TileParams] = tileAttachParams.map(_.tileParams) + val tileCrossingTypes = tileAttachParams.map(_.crossingParams.crossingType) + def nTiles: Int = tileAttachParams.size + def hartIdList: Seq[Int] = tileParams.map(_.hartId) + def localIntCounts: Seq[Int] = tileParams.map(_.core.nLocalInterrupts) + + require(hartIdList.distinct.size == tiles.size, s"Every tile must be statically assigned a unique id, but got:\n${hartIdList}") + + // connect all the tiles to interconnect attachment points made available in this subsystem context + tileAttachParams.zip(tiles).foreach { case (params, t) => + params.connect(t.asInstanceOf[params.TileType], this.asInstanceOf[params.TileContextType]) + } +} + +/** Most tile types require only these traits in order for their standardized connect functions to apply. + * BaseTiles subtypes with different needs can extend this trait to provide themselves with + * additional external connection points. + */ +trait DefaultTileContextType + extends Attachable + with HasTileInterruptSources + with HasTileNotificationSinks +{ this: BaseSubsystem => } // TODO: ideally this bound would be softened to LazyModule + +/** Standardized interface by which parameterized tiles can be attached to contexts containing interconnect resources. + * Sub-classes of this trait can optionally override the individual connect functions in order to specialize + * their attachment behaviors, but most use cases should be be handled simply by changing the implementation + * of the injectNode functions in crossingParams. + */ +trait CanAttachTile { + type TileType <: BaseTile + type TileContextType <: DefaultTileContextType + def tileParams: InstantiableTileParams[TileType] + def crossingParams: TileCrossingParamsLike + def lookup: LookupByHartIdImpl + + // narrow waist through which all tiles are intended to pass while being instantiated + def instantiate(implicit p: Parameters): TileType = { + val tile = LazyModule(tileParams.instantiate(crossingParams, lookup)) + tile + } + + // a default set of connections that need to occur for most tile types + def connect(tile: TileType, context: TileContextType): Unit = { + connectMasterPorts(tile, context) + connectSlavePorts(tile, context) + connectInterrupts(tile, context) + LogicalModuleTree.add(context.logicalTreeNode, tile.logicalTreeNode) + } + + // connect the port where the tile is the master to a TileLink interconnect + def connectMasterPorts(tile: TileType, context: Attachable): Unit = { + implicit val p = context.p + val dataBus = context.locateTLBusWrapper(crossingParams.master.where) + dataBus.coupleFrom(tileParams.name.getOrElse("tile")) { bus => + bus :=* crossingParams.master.injectNode(context) :=* tile.crossMasterPort() } } - protected def connectSlavePortsToCBus(tile: BaseTile, crossing: RocketCrossingParams)(implicit valName: ValName) { + // connect the port where the tile is the slave to a TileLink interconnect + def connectSlavePorts(tile: TileType, context: Attachable): Unit = { + implicit val p = context.p DisableMonitors { implicit p => - locateTLBusWrapper(crossing.slave.where).coupleTo(tile.tileParams.name.getOrElse("tile")) { bus => - crossing.slave.blockerCtrlAddr - .map { BasicBusBlockerParams(_, pbus.beatBytes, sbus.beatBytes) } - .map { bbbp => LazyModule(new BasicBusBlocker(bbbp)) } - .map { bbb => - cbus.coupleTo("bus_blocker") { bbb.controlNode := TLFragmenter(cbus) := _ } - tile.crossSlavePort() :*= bbb.node - } .getOrElse { tile.crossSlavePort() } :*= bus + val controlBus = context.locateTLBusWrapper(crossingParams.slave.where) + controlBus.coupleTo(tileParams.name.getOrElse("tile")) { bus => + tile.crossSlavePort() :*= crossingParams.slave.injectNode(context) :*= TLWidthWidget(controlBus.beatBytes) :*= bus } } } - protected def connectInterrupts(tile: BaseTile, debugOpt: Option[TLDebugModule], clintOpt: Option[CLINT], plicOpt: Option[TLPLIC]) { - // Handle all the different types of interrupts crossing to or from the tile: + // connect the various interrupt and notification wires going to and from the tile + def connectInterrupts(tile: TileType, context: TileContextType): Unit = { + implicit val p = context.p // NOTE: The order of calls to := matters! They must match how interrupts // are decoded from tile.intInwardNode inside the tile. For this reason, // we stub out missing interrupts with constant sources here. // 1. Debug interrupt is definitely asynchronous in all cases. tile.intInwardNode := - debugOpt + context.debugOpt .map { tile { IntSyncAsyncCrossingSink(3) } := _.intnode } .getOrElse { NullIntSource() } @@ -85,18 +203,18 @@ trait HasTiles extends HasCoreMonitorBundles { this: BaseSubsystem => // From CLINT: "msip" and "mtip" tile.crossIntIn() := - clintOpt.map { _.intnode } + context.clintOpt.map { _.intnode } .getOrElse { NullIntSource(sources = CLINTConsts.ints) } // From PLIC: "meip" tile.crossIntIn() := - plicOpt .map { _.intnode } - .getOrElse { meipNode.get } + context.plicOpt .map { _.intnode } + .getOrElse { context.meipNode.get } // From PLIC: "seip" (only if supervisor mode is enabled) if (tile.tileParams.core.hasSupervisorMode) { tile.crossIntIn() := - plicOpt .map { _.intnode } + context.plicOpt .map { _.intnode } .getOrElse { NullIntSource() } } @@ -105,27 +223,22 @@ trait HasTiles extends HasCoreMonitorBundles { this: BaseSubsystem => // 4. Interrupts coming out of the tile are sent to the PLIC, // so might need to be synchronized depending on the Tile's crossing type. - plicOpt.foreach { plic => + context.plicOpt.foreach { plic => FlipRendering { implicit p => plic.intnode :=* tile.crossIntOut() } } - // 5. Reports of tile status are collected without needing to be clock-crossed - tileHaltXbarNode := tile.haltNode - tileWFIXbarNode := tile.wfiNode - tileCeaseXbarNode := tile.ceaseNode - } - - protected def perTileOrGlobalSetting[T](in: Seq[T], n: Int): Seq[T] = in.size match { - case 1 => List.fill(n)(in.head) - case x if x == n => in - case _ => throw new Exception("must provide exactly 1 or #tiles of this key") + // 5. Notifications of tile status are collected without needing to be clock-crossed + context.tileHaltXbarNode :=* tile.haltNode + context.tileWFIXbarNode :=* tile.wfiNode + context.tileCeaseXbarNode :=* tile.ceaseNode } } -trait HasTilesModuleImp extends LazyModuleImp { - val outer: HasTiles +/** Provides some Chisel connectivity to certain tile IOs */ +trait HasTilesModuleImp extends LazyModuleImp with HasPeripheryDebugModuleImp { + val outer: HasTiles with HasTileInterruptSources def resetVectorBits: Int = { // Consider using the minimum over all widths, rather than enforcing homogeneity diff --git a/src/main/scala/subsystem/RocketSubsystem.scala b/src/main/scala/subsystem/RocketSubsystem.scala index 789a883ae7e..e26ea2361c9 100644 --- a/src/main/scala/subsystem/RocketSubsystem.scala +++ b/src/main/scala/subsystem/RocketSubsystem.scala @@ -14,70 +14,34 @@ import freechips.rocketchip.tile._ case object HartPrefixKey extends Field[Boolean](false) -// TODO: how specific are these to RocketTiles? -case class TileMasterPortParams( - buffers: Int = 0, - cork: Option[Boolean] = None, - where: TLBusWrapperLocation = SBUS) - -case class TileSlavePortParams( - buffers: Int = 0, - blockerCtrlAddr: Option[BigInt] = None, - where: TLBusWrapperLocation = CBUS) - case class RocketCrossingParams( crossingType: ClockCrossingType = SynchronousCrossing(), master: TileMasterPortParams = TileMasterPortParams(), - slave: TileSlavePortParams = TileSlavePortParams()) + slave: TileSlavePortParams = TileSlavePortParams() +) extends TileCrossingParamsLike + +case class RocketTileAttachParams( + tileParams: RocketTileParams, + crossingParams: RocketCrossingParams, + lookup: LookupByHartIdImpl +) extends CanAttachTile { type TileType = RocketTile } case object RocketTilesKey extends Field[Seq[RocketTileParams]](Nil) case object RocketCrossingKey extends Field[Seq[RocketCrossingParams]](List(RocketCrossingParams())) -trait HasRocketTiles extends HasTiles - with CanHavePeripheryPLIC - with CanHavePeripheryCLINT - with HasPeripheryDebug { this: BaseSubsystem => - val module: HasRocketTilesModuleImp - - protected val rocketTileParams = p(RocketTilesKey) - private val crossings = perTileOrGlobalSetting(p(RocketCrossingKey), rocketTileParams.size) - - // Make a tile and wire its nodes into the system, - // according to the specified type of clock crossing. - // Note that we also inject new nodes into the tile itself, - // also based on the crossing type. - val rocketTiles = rocketTileParams.zip(crossings).map { case (tp, crossing) => - val rocket = LazyModule(new RocketTile(tp, crossing, PriorityMuxHartIdFromSeq(rocketTileParams), logicalTreeNode)) - - connectMasterPortsToSBus(rocket, crossing) - connectSlavePortsToCBus(rocket, crossing) - connectInterrupts(rocket, debugOpt, clintOpt, plicOpt) - - rocket - } - - rocketTiles.map { - r => - def treeNode: RocketTileLogicalTreeNode = new RocketTileLogicalTreeNode(r.rocketLogicalTree.getOMInterruptTargets) - LogicalModuleTree.add(logicalTreeNode, r.rocketLogicalTree) - } +trait HasRocketTiles extends HasTiles { this: BaseSubsystem => + val rocketTiles = tiles.collect { case r: RocketTile => r } def coreMonitorBundles = (rocketTiles map { t => t.module.core.rocketImpl.coreMonitorBundle }).toList } -trait HasRocketTilesModuleImp extends HasTilesModuleImp - with HasPeripheryDebugModuleImp { - val outer: HasRocketTiles -} - // Field for specifying MaskROM addition to subsystem case object PeripheryMaskROMKey extends Field[Seq[MaskROMParams]](Nil) class RocketSubsystem(implicit p: Parameters) extends BaseSubsystem with HasRocketTiles { - val tiles = rocketTiles // add Mask ROM devices val maskROMs = p(PeripheryMaskROMKey).map { MaskROM.attach(_, cbus) } @@ -99,7 +63,7 @@ class RocketSubsystem(implicit p: Parameters) extends BaseSubsystem class RocketSubsystemModuleImp[+L <: RocketSubsystem](_outer: L) extends BaseSubsystemModuleImp(_outer) with HasResetVectorWire - with HasRocketTilesModuleImp { + with HasTilesModuleImp { for (i <- 0 until outer.tiles.size) { val wire = tile_inputs(i) diff --git a/src/main/scala/system/Configs.scala b/src/main/scala/system/Configs.scala index b76507bb07a..543a064c508 100644 --- a/src/main/scala/system/Configs.scala +++ b/src/main/scala/system/Configs.scala @@ -6,7 +6,7 @@ package freechips.rocketchip.system import Chisel._ import freechips.rocketchip.config.Config import freechips.rocketchip.subsystem._ -import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.groundtest.WithTraceGen class WithJtagDTMSystem extends freechips.rocketchip.subsystem.WithJtagDTM class WithDebugSBASystem extends freechips.rocketchip.subsystem.WithDebugSBA @@ -39,6 +39,15 @@ class DualChannelDualBankConfig extends Config( class RoccExampleConfig extends Config(new WithRoccExample ++ new DefaultConfig) +class HeterogeneousTileExampleConfig extends Config( + new WithTraceGen (n = 2, overrideMemOffset = Some(0x90000000L))() ++ + new WithNBigCores(n = 1) ++ + new WithNMedCores(n = 1) ++ + new WithNSmallCores(n = 1) ++ + new WithCoherentBusTopology ++ + new BaseConfig +) + class Edge128BitConfig extends Config( new WithEdgeDataBits(128) ++ new DefaultConfig) class Edge32BitConfig extends Config( diff --git a/src/main/scala/tile/BaseTile.scala b/src/main/scala/tile/BaseTile.scala index df658ddcd8c..5bb6b2582b4 100644 --- a/src/main/scala/tile/BaseTile.scala +++ b/src/main/scala/tile/BaseTile.scala @@ -7,6 +7,9 @@ import Chisel._ import freechips.rocketchip.config._ import freechips.rocketchip.subsystem._ import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.diplomaticobjectmodel.{HasLogicalTreeNode} +import freechips.rocketchip.diplomaticobjectmodel.logicaltree.{GenericLogicalTreeNode, LogicalTreeNode} + import freechips.rocketchip.interrupts._ import freechips.rocketchip.rocket._ import freechips.rocketchip.tilelink._ @@ -29,6 +32,11 @@ trait TileParams { val name: Option[String] } +abstract class InstantiableTileParams[TileType <: BaseTile] extends TileParams { + def instantiate(crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl) + (implicit p: Parameters): TileType +} + /** These parameters values are not computed based on diplomacy negotiation * and so are safe to use while diplomacy itself is running. */ @@ -148,6 +156,7 @@ abstract class BaseTile private (val crossing: ClockCrossingType, q: Parameters) extends LazyModule()(q) with CrossesToOnlyOneClockDomain with HasNonDiplomaticTileParameters + with HasLogicalTreeNode { // Public constructor alters Parameters to supply some legacy compatibility keys def this(tileParams: TileParams, crossing: ClockCrossingType, lookup: LookupByHartIdImpl, p: Parameters) = { @@ -251,6 +260,8 @@ abstract class BaseTile private (val crossing: ClockCrossingType, q: Parameters) def crossIntIn(): IntInwardNode = crossIntIn(intInwardNode) def crossIntOut(): IntOutwardNode = crossIntOut(intOutwardNode) + val logicalTreeNode: LogicalTreeNode = new GenericLogicalTreeNode + this.suggestName(tileParams.name) } diff --git a/src/main/scala/tile/RocketTile.scala b/src/main/scala/tile/RocketTile.scala index c743c2df000..e5a713445df 100644 --- a/src/main/scala/tile/RocketTile.scala +++ b/src/main/scala/tile/RocketTile.scala @@ -11,7 +11,7 @@ import freechips.rocketchip.diplomaticobjectmodel.logicaltree.{DCacheLogicalTree import freechips.rocketchip.interrupts._ import freechips.rocketchip.tilelink._ import freechips.rocketchip.rocket._ -import freechips.rocketchip.subsystem.{SubsystemResetSchemeKey, ResetSynchronous, RocketCrossingParams, HartPrefixKey} +import freechips.rocketchip.subsystem.{SubsystemResetSchemeKey, ResetSynchronous, HartPrefixKey, TileCrossingParamsLike} import freechips.rocketchip.util._ case class RocketTileParams( @@ -25,17 +25,19 @@ case class RocketTileParams( beuAddr: Option[BigInt] = None, blockerCtrlAddr: Option[BigInt] = None, boundaryBuffers: Boolean = false // if synthesized with hierarchical PnR, cut feed-throughs? - ) extends TileParams { + ) extends InstantiableTileParams[RocketTile] { require(icache.isDefined) require(dcache.isDefined) + def instantiate(crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters): RocketTile = { + new RocketTile(this, crossing, lookup) + } } class RocketTile private( val rocketParams: RocketTileParams, crossing: ClockCrossingType, lookup: LookupByHartIdImpl, - q: Parameters, - logicalTreeNode: LogicalTreeNode) + q: Parameters) extends BaseTile(rocketParams, crossing, lookup, q) with SinksExternalInterrupts with SourcesExternalNotifications @@ -44,14 +46,14 @@ class RocketTile private( with HasICacheFrontend { // Private constructor ensures altered LazyModule.p is used implicitly - def this(params: RocketTileParams, crossing: RocketCrossingParams, lookup: LookupByHartIdImpl, logicalTreeNode: LogicalTreeNode)(implicit p: Parameters) = - this(params, crossing.crossingType, lookup, p, logicalTreeNode) + def this(params: RocketTileParams, crossing: TileCrossingParamsLike, lookup: LookupByHartIdImpl)(implicit p: Parameters) = + this(params, crossing.crossingType, lookup, p) val intOutwardNode = IntIdentityNode() val slaveNode = TLIdentityNode() val masterNode = visibilityNode - val rocketLogicalTree = new RocketLogicalTreeNode(this, p(XLen), pgLevels) + override val logicalTreeNode = new RocketLogicalTreeNode(this, p(XLen), pgLevels) val dtim_adapter = tileParams.dcache.flatMap { d => d.scratch.map { s => val coreParams = { @@ -63,7 +65,7 @@ class RocketTile private( dtim_adapter.foreach(lm => connectTLSlave(lm.node, lm.node.portParams.head.beatBytes)) val bus_error_unit = rocketParams.beuAddr map { a => - val beu = LazyModule(new BusErrorUnit(new L1BusErrors, BusErrorUnitParams(a), rocketLogicalTree)) + val beu = LazyModule(new BusErrorUnit(new L1BusErrors, BusErrorUnitParams(a), logicalTreeNode)) intOutwardNode := beu.intNode connectTLSlave(beu.node, xBytes) beu @@ -117,8 +119,8 @@ class RocketTile private( } val dCacheLogicalTreeNode = new DCacheLogicalTreeNode(dcache, dtim_adapter.map(_.device), rocketParams.dcache.get) - LogicalModuleTree.add(rocketLogicalTree, iCacheLogicalTreeNode) - LogicalModuleTree.add(rocketLogicalTree, dCacheLogicalTreeNode) + LogicalModuleTree.add(logicalTreeNode, iCacheLogicalTreeNode) + LogicalModuleTree.add(logicalTreeNode, dCacheLogicalTreeNode) } class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) diff --git a/src/main/scala/util/package.scala b/src/main/scala/util/package.scala index 5aec9fcfa51..aabee6525e9 100644 --- a/src/main/scala/util/package.scala +++ b/src/main/scala/util/package.scala @@ -253,6 +253,12 @@ package object util { map.view.map({ case (k, vs) => k -> vs.toList }).toList } + def heterogeneousOrGlobalSetting[T](in: Seq[T], n: Int): Seq[T] = in.size match { + case 1 => List.fill(n)(in.head) + case x if x == n => in + case _ => throw new Exception(s"must provide exactly 1 or $n of some field, but got:\n$in") + } + /** provides operators useful for working with bidirectional [[Bundle]]s * * In terms of [[Flipped]] with a producer 'p' and 'consumer' c: