From e958ede2779597ea444077682e7a57d322d78144 Mon Sep 17 00:00:00 2001 From: Vamber Yang Date: Mon, 6 Nov 2023 20:51:23 -0800 Subject: [PATCH] multi-bank working when nBanks=2, encountered a putPartial error, need to pull latest change --- src/main/scala/rocket/VortexFatBank.scala | 281 +++++++++++------- src/main/scala/tile/VortexTile.scala | 47 +-- .../scala/tilelink/CanHaveMemtraceCore.scala | 12 +- 3 files changed, 211 insertions(+), 129 deletions(-) diff --git a/src/main/scala/rocket/VortexFatBank.scala b/src/main/scala/rocket/VortexFatBank.scala index 8db4525..f54e087 100644 --- a/src/main/scala/rocket/VortexFatBank.scala +++ b/src/main/scala/rocket/VortexFatBank.scala @@ -11,119 +11,67 @@ import org.chipsalliance.cde.config.{Parameters, Field} - -// Delete the following NewSourceGenerator when merging with origin/graphics -//we should just use the one in coalescing.scala written by hansung - -class NewSourceGenerator[T <: Data]( - sourceWidth: Int, - metadata: Option[T] = None, - ignoreInUse: Boolean = false -) extends Module { - def getMetadataType = metadata match { - case Some(gen) => gen.cloneType - case None => UInt(0.W) - } - val io = IO(new Bundle { - val gen = Input(Bool()) - val reclaim = Input(Valid(UInt(sourceWidth.W))) - val id = Output(Valid(UInt(sourceWidth.W))) - // below are used only when metadata is not None - // `meta` is used as input when a request succeeds id generation to store - // its value to the table. - // `peek` is the retrieved metadata saved for the request when corresponding - // request has come back, setting `reclaim`. - // Although these do not use ValidIO, it is safe because any in-flight - // response coming back should have allocated a valid entry in the table - // when it went out. - val meta = Input(getMetadataType) - val peek = Output(getMetadataType) - // for debugging; indicates whether there is at least one inflight request - // that hasn't been reclaimed yet - val inflight = Output(Bool()) - }) - val head = RegInit(UInt(sourceWidth.W), 0.U) - head := Mux(io.gen, head + 1.U, head) - - val outstanding = RegInit(UInt((sourceWidth + 1).W), 0.U) - io.inflight := (outstanding > 0.U) || io.gen - - val numSourceId = 1 << sourceWidth - val row = new Bundle { - val meta = getMetadataType - val id = Valid(UInt(sourceWidth.W)) - } - // valid: in use, invalid: available - // val occupancyTable = Mem(numSourceId, Valid(UInt(sourceWidth.W))) - val occupancyTable = Mem(numSourceId, row) - when(reset.asBool) { - (0 until numSourceId).foreach { occupancyTable(_).id.valid := false.B } - } - val frees = (0 until numSourceId).map(!occupancyTable(_).id.valid) - val lowestFree = PriorityEncoder(frees) - val lowestFreeRow = occupancyTable(lowestFree) - - io.id.valid := (if (ignoreInUse) true.B else !lowestFreeRow.id.valid) - io.id.bits := lowestFree - when(io.gen && io.id.valid /* fire */ ) { - occupancyTable(io.id.bits).id.valid := true.B // mark in use - if (metadata.isDefined) { - occupancyTable(io.id.bits).meta := io.meta - } - } - when(io.reclaim.valid) { - // @perf: would this require multiple write ports? - assert(occupancyTable(io.reclaim.bits).id.valid === true.B, "tried to reclaim a non-used id") - occupancyTable(io.reclaim.bits).id.valid := false.B // mark freed - } - io.peek := { - if (metadata.isDefined) occupancyTable(io.reclaim.bits).meta else 0.U - } - - when(io.gen && io.id.valid) { - when (!io.reclaim.valid) { - assert(outstanding < (1 << sourceWidth).U) - outstanding := outstanding + 1.U - } - }.elsewhen(io.reclaim.valid) { - assert(outstanding > 0.U) - outstanding := outstanding - 1.U - } - dontTouch(outstanding) -} - - -// VortexTile has dmemNodes, imemNodes - //Param and Key are used during SoC Generation -case class VortexFatBankParam(wordSize: Int = 16, busWidthInBytes: Int = 8) -case object VortexFatBankKey extends Field[Option[VortexFatBankConfig]](None /*default*/) +case class L1SystemParam(wordSize: Int = 16, busWidthInBytes: Int = 8) +case object L1SystemKey extends Field[Option[L1SystemConfig]](None /*default*/) -case class VortexFatBankConfig( +case class L1SystemConfig( + numBanks: Int, wordSize: Int, //This is the read/write granularity of the L1 cache cacheLineSize: Int, coreTagWidth: Int, writeInfoReqQSize: Int, mshrSize: Int, - uncachedAddrSets: Seq[AddressSet] + uncachedAddrSets: Seq[AddressSet], + icacheInstAddrSets: Seq[AddressSet] ) { def coreTagPlusSizeWidth: Int = { log2Ceil(wordSize) + coreTagWidth } } -object defaultFatBankConfig extends VortexFatBankConfig( +object defaultL1SystemConfig extends L1SystemConfig( + numBanks = 4, wordSize = 16, cacheLineSize = 16, coreTagWidth = 8, writeInfoReqQSize = 16, mshrSize = 8, - uncachedAddrSets = Seq(AddressSet(0x2000000L, 0xFFL)) + uncachedAddrSets = Seq(AddressSet(0x2000000L, 0xFFL)), + icacheInstAddrSets = Seq(AddressSet(0x80000000L, 0xFFFFFFFL)) ) +class L1System (config:L1SystemConfig) (implicit p: Parameters) extends LazyModule { -class FatBankPassThrough(config:VortexFatBankConfig) (implicit p: Parameters) extends LazyModule { + //icache bank + val icache_bank = LazyModule(new VortexFatBank(config, 0, isICache=true)) + + //dcache banks + val dcache_banks = Seq.tabulate(config.numBanks) { bankId => + val bank = LazyModule(new VortexFatBank(config, bankId)) + bank + } + //passthrough + val passThrough = LazyModule(new FatBankPassThrough(config)) + + //L1System exposes to upstream as a dmemXbar + val dmemXbar = LazyModule(new TLXbar) + dcache_banks.foreach { _.coalToVxCacheNode :=* dmemXbar.node} + passThrough.coalToVxCacheNode :=* dmemXbar.node + + //L1System exposes to downstream as one tileLink Identity Node + val L1SystemToL2Node = TLIdentityNode() + dcache_banks.foreach{ L1SystemToL2Node := _.vxCacheToL2Node} + L1SystemToL2Node := passThrough.vxCacheToL2Node + L1SystemToL2Node := icache_bank.vxCacheToL2Node + + lazy val module = new LazyModuleImp(this) + +} + + +class FatBankPassThrough(config:L1SystemConfig) (implicit p: Parameters) extends LazyModule { val clientParam = Seq(TLMasterPortParameters.v1( clients = Seq( @@ -170,16 +118,32 @@ class FatBankPassThrough(config:VortexFatBankConfig) (implicit p: Parameters) ex } -class VortexFatBank (config: VortexFatBankConfig) (implicit p: Parameters) extends LazyModule { + + +class VortexFatBank (config: L1SystemConfig, bankId: Int, isICache: Boolean = false) + (implicit p: Parameters) extends LazyModule { //Generate AddressSet by excluding Addr we don't want - def generateAddressSets(excludeSets: Seq[AddressSet]): Seq[AddressSet] = { - var remainingSets: Seq[AddressSet] = Seq(AddressSet(0x00000000L, 0xFFFFFFFFL)) - for(excludeSet <- excludeSets) { - remainingSets = remainingSets.flatMap(_.subtract(excludeSet)) + def generateAddressSets(): Seq[AddressSet] = { + + if (isICache){ + //config.icacheInstAddrSets + Seq(AddressSet(0x00000000L, 0xFFFFFFFFL)) + } else { + //suppose have 4 bank + //base for bank 1: ...000000|01|0000 + //mask for bank 1; 111111|00|1111 + val mask = 0xFFFFFFFFL ^ ((config.numBanks-1)*config.wordSize) + val base = 0x00000000L | (bankId * config.wordSize) + + val excludeSets = (config.uncachedAddrSets ++ config.icacheInstAddrSets) + var remainingSets: Seq[AddressSet] = Seq(AddressSet(base, mask)) + for(excludeSet <- excludeSets) { + remainingSets = remainingSets.flatMap(_.subtract(excludeSet)) + } + remainingSets } - remainingSets } val clientParam = Seq(TLMasterPortParameters.v1( @@ -199,7 +163,7 @@ class VortexFatBank (config: VortexFatBankConfig) (implicit p: Parameters) exten beatBytes = config.wordSize, managers = Seq( TLSlaveParameters.v1( - address = generateAddressSets(config.uncachedAddrSets), + address = generateAddressSets(), regionType = RegionType.IDEMPOTENT, // idk what this does executable = false, supportsGet = TransferSizes(1, config.wordSize), @@ -223,7 +187,7 @@ class VortexFatBank (config: VortexFatBankConfig) (implicit p: Parameters) exten class VortexFatBankImp ( outer: VortexFatBank, - config: VortexFatBankConfig + config: L1SystemConfig ) extends LazyModuleImp(outer) { val vxCache = Module(new VX_cache( @@ -260,7 +224,7 @@ class VortexFatBankImp ( val size = UInt(32.W) } - class ReadReqInfo(config: VortexFatBankConfig) extends Bundle { + class ReadReqInfo(config: L1SystemConfig) extends Bundle { val size = UInt(log2Ceil(config.wordSize).W) val id = UInt(config.coreTagWidth.W) } @@ -423,7 +387,7 @@ class VortexFatBankImp ( class VX_cache ( CACHE_ID: Int = 0, - CACHE_SIZE: Int = 16384, + CACHE_SIZE: Int = 16384/4, // Delete the following NewSourceGenerator when merging with origin/graphics +//we should just use the one in coalescing.scala written by hansung + +class NewSourceGenerator[T <: Data]( + sourceWidth: Int, + metadata: Option[T] = None, + ignoreInUse: Boolean = false +) extends Module { + def getMetadataType = metadata match { + case Some(gen) => gen.cloneType + case None => UInt(0.W) + } + val io = IO(new Bundle { + val gen = Input(Bool()) + val reclaim = Input(Valid(UInt(sourceWidth.W))) + val id = Output(Valid(UInt(sourceWidth.W))) + // below are used only when metadata is not None + // `meta` is used as input when a request succeeds id generation to store + // its value to the table. + // `peek` is the retrieved metadata saved for the request when corresponding + // request has come back, setting `reclaim`. + // Although these do not use ValidIO, it is safe because any in-flight + // response coming back should have allocated a valid entry in the table + // when it went out. + val meta = Input(getMetadataType) + val peek = Output(getMetadataType) + // for debugging; indicates whether there is at least one inflight request + // that hasn't been reclaimed yet + val inflight = Output(Bool()) + }) + val head = RegInit(UInt(sourceWidth.W), 0.U) + head := Mux(io.gen, head + 1.U, head) + + val outstanding = RegInit(UInt((sourceWidth + 1).W), 0.U) + io.inflight := (outstanding > 0.U) || io.gen + + val numSourceId = 1 << sourceWidth + val row = new Bundle { + val meta = getMetadataType + val id = Valid(UInt(sourceWidth.W)) + val age = UInt(32.W) // New age field for debugging + } + // valid: in use, invalid: available + // val occupancyTable = Mem(numSourceId, Valid(UInt(sourceWidth.W))) + val occupancyTable = Mem(numSourceId, row) + when(reset.asBool) { + (0 until numSourceId).foreach { i => + occupancyTable(i).id.valid := false.B + occupancyTable(i).age := 0.U // Reset age during reset + } + } + val frees = (0 until numSourceId).map(!occupancyTable(_).id.valid) + val lowestFree = PriorityEncoder(frees) + val lowestFreeRow = occupancyTable(lowestFree) + + io.id.valid := (if (ignoreInUse) true.B else !lowestFreeRow.id.valid) + io.id.bits := lowestFree + when(io.gen && io.id.valid /* fire */ ) { + occupancyTable(io.id.bits).id.valid := true.B // mark in use + occupancyTable(io.id.bits).age := 0.U // reset age upon issuing, double safety + if (metadata.isDefined) { + occupancyTable(io.id.bits).meta := io.meta + } + } + + // Increase age of all inflight IDs by 1, except for the one being reclaimed + for(i <- 0 until numSourceId) { + when(occupancyTable(i).id.valid && (i.U =/= io.reclaim.bits || !io.reclaim.valid)) { + occupancyTable(i).age := occupancyTable(i).age + 1.U + } + } + + when(io.reclaim.valid) { + assert(occupancyTable(io.reclaim.bits).id.valid === true.B, "tried to reclaim a non-used id") + occupancyTable(io.reclaim.bits).id.valid := false.B // mark freed + occupancyTable(io.reclaim.bits).age := 0.U + } + + + io.peek := { + if (metadata.isDefined) occupancyTable(io.reclaim.bits).meta else 0.U + } + + when(io.gen && io.id.valid) { + when (!io.reclaim.valid) { + assert(outstanding < (1 << sourceWidth).U) + outstanding := outstanding + 1.U + } + }.elsewhen(io.reclaim.valid) { + assert(outstanding > 0.U) + outstanding := outstanding - 1.U + } + + // Debugging wires + val ages = VecInit((0 until numSourceId).map(i => occupancyTable(i).age)) + val oldestIndex = PriorityEncoder(ages.map(a => a === ages.reduce((x, y) => Mux(x > y, x, y)))) + val oldestIdInflight = Wire(UInt(sourceWidth.W)) + val oldestMetadata = Wire(getMetadataType) + val oldestAge = Wire(UInt(32.W)) + + oldestIdInflight := oldestIndex + oldestMetadata := occupancyTable(oldestIndex).meta + oldestAge := occupancyTable(oldestIndex).age + assert(oldestAge <= 2000.U, "One id in the SourceGen is not released for long time, potential bug !") + + dontTouch(oldestIdInflight) + dontTouch(oldestMetadata) + dontTouch(oldestAge) + dontTouch(outstanding) + +} \ No newline at end of file diff --git a/src/main/scala/tile/VortexTile.scala b/src/main/scala/tile/VortexTile.scala index 11b8804..5bbf8b5 100644 --- a/src/main/scala/tile/VortexTile.scala +++ b/src/main/scala/tile/VortexTile.scala @@ -218,31 +218,41 @@ class VortexTile private ( ))) // Conditionally instantiate memory coalescer - val coalescerNode = p(CoalescerKey) match { + val coalescerL1Node = p(CoalescerKey) match { case Some(coalescerParam) => { val coal = LazyModule(new CoalescingUnit(coalescerParam.copy(enable = true))) coal.cpuNode :=* dmemAggregateNode coal.aggregateNode // N+1 lanes //Conditionally instantiate fat-bank, we can only use fatbank in the presence of coalescer - val coalFatbankNode = p(VortexFatBankKey) match { - case Some(fatBankParam) =>{ - println(s"============ Using Vortex FatBank as L1 =================") - val vx_fatbank = LazyModule(new VortexFatBank(fatBankParam)) - val passThrough = LazyModule(new FatBankPassThrough(fatBankParam)) - val coalXbar = LazyModule(new TLXbar) - coalXbar.node :=* coal.aggregateNode - vx_fatbank.coalToVxCacheNode :=* coalXbar.node - passThrough.coalToVxCacheNode :=* coalXbar.node - //merge these two into one identity node - val fatBankSystem = TLIdentityNode() - fatBankSystem := vx_fatbank.vxCacheToL2Node - fatBankSystem := passThrough.vxCacheToL2Node - fatBankSystem + + val L1SystemNode = p(L1SystemKey) match { + case Some(l1SystemCfg) =>{ + println(s"============ Using Vortex FatBank as L1 System =================") + + + val L1System = LazyModule(new L1System(l1SystemCfg)) + //Currently we have an architectural deadlock + //we CAN NOT direcrly connect core's instruction fetch to TL-MasterXBar, that leads to a deadlock + + //Connect L1System with imem_fetch_interface without XBar + //coalToVxCacheNode is a bad naming, it really means up steam of vxBank in whihc it takes input + imemNodes.foreach { L1System.icache_bank.coalToVxCacheNode := TLWidthWidget(4) := _ } + + //connect L1System with dmem_req from coalescer + L1System.dmemXbar.node :=* coal.aggregateNode + + //L1System appears to downstream as one Identity Node + L1System.L1SystemToL2Node + + } + + case None => { + imemNodes.foreach { tlMasterXbar.node := TLWidthWidget(4) := _ } //need to bind imem directly if not using FatBank + coal.aggregateNode //if no fatbank, simply return coalescer.aggregateNode } - case None => coal.aggregateNode //if no fatbank, simply return coalescer.aggregateNode } - coalFatbankNode + L1SystemNode } case None => dmemAggregateNode @@ -251,8 +261,7 @@ class VortexTile private ( if (vortexParams.useVxCache) { tlMasterXbar.node := TLWidthWidget(16) := memNode } else { - imemNodes.foreach { tlMasterXbar.node := TLWidthWidget(4) := _ } - tlMasterXbar.node :=* coalescerNode + tlMasterXbar.node :=* coalescerL1Node } /* below are copied from rocket */ diff --git a/src/main/scala/tilelink/CanHaveMemtraceCore.scala b/src/main/scala/tilelink/CanHaveMemtraceCore.scala index e8c6e82..d9b1331 100644 --- a/src/main/scala/tilelink/CanHaveMemtraceCore.scala +++ b/src/main/scala/tilelink/CanHaveMemtraceCore.scala @@ -56,15 +56,9 @@ trait CanHaveMemtraceCore { this: BaseSubsystem => case None => coalescerNode } - val vortexBank = p(VortexFatBankKey) match { - case Some(fatBankParam) =>{ - val vx_fatbank = LazyModule(new VortexFatBank(fatBankParam)) - println(s"============ Using Vortex FatBank as L1 ") - vx_fatbank.coalToVxCacheNode :=* coalXbar - vx_fatbank.vxCacheToL2Node - } - case None => coalXbar - } + + val vortexBank = coalXbar + //If there is only 1 bank, the code below is useless