This commit is contained in:
Hansung Kim
2024-01-19 15:16:37 -08:00
parent fec3d61dd6
commit 737f24fd77

View File

@@ -14,13 +14,18 @@ import freechips.rocketchip.tilelink._
// Note: numNewSrcId is not a part of CoreParam, because the SIMT core should be agnostic to how inflight coalesced request can be genertated // Note: numNewSrcId is not a part of CoreParam, because the SIMT core should be agnostic to how inflight coalesced request can be genertated
case class SIMTCoreParams(nLanes: Int = 4, nSrcIds: Int = 8) case class SIMTCoreParams(nLanes: Int = 4, nSrcIds: Int = 8)
case class MemtraceCoreParams(tracefilename: String = "undefined", traceHasSource: Boolean = false) case class MemtraceCoreParams(
tracefilename: String = "undefined",
traceHasSource: Boolean = false
)
case class CoalXbarParam() case class CoalXbarParam()
case object SIMTCoreKey extends Field[Option[SIMTCoreParams]](None /*default*/) case object SIMTCoreKey extends Field[Option[SIMTCoreParams]](None /*default*/ )
case object MemtraceCoreKey extends Field[Option[MemtraceCoreParams]](None /*default*/) case object MemtraceCoreKey
case object CoalescerKey extends Field[Option[CoalescerConfig]](None /*default*/) extends Field[Option[MemtraceCoreParams]](None /*default*/ )
case object CoalXbarKey extends Field[Option[CoalXbarParam]](None /*default*/) case object CoalescerKey
extends Field[Option[CoalescerConfig]](None /*default*/ )
case object CoalXbarKey extends Field[Option[CoalXbarParam]](None /*default*/ )
trait InFlightTableSizeEnum extends ChiselEnum { trait InFlightTableSizeEnum extends ChiselEnum {
val INVALID: Type val INVALID: Type
@@ -81,19 +86,25 @@ case class CoalescerConfig(
) { ) {
// maximum coalesced size // maximum coalesced size
def maxCoalLogSize: Int = { def maxCoalLogSize: Int = {
require(coalLogSizes.max <= dataBusWidth, require(
"multi-beat coalesced reads/writes are currently not supported") coalLogSizes.max <= dataBusWidth,
"multi-beat coalesced reads/writes are currently not supported"
)
if (coalLogSizes.max < dataBusWidth) { if (coalLogSizes.max < dataBusWidth) {
println("======== Warning: coalescer's max coalescing size is set to " + println(
s"${coalLogSizes.max}, which is narrower than data bus width " + "======== Warning: coalescer's max coalescing size is set to " +
s"${dataBusWidth}. This might indicate misconfiguration.") s"${coalLogSizes.max}, which is narrower than data bus width " +
s"${dataBusWidth}. This might indicate misconfiguration."
)
} }
coalLogSizes.max coalLogSizes.max
} }
def wordSizeWidth: Int = { def wordSizeWidth: Int = {
val w = log2Ceil(wordSizeInBytes) val w = log2Ceil(wordSizeInBytes)
require(wordSizeInBytes == 1 << w, require(
s"wordSizeInBytes (${wordSizeInBytes}) is not power of two") wordSizeInBytes == 1 << w,
s"wordSizeInBytes (${wordSizeInBytes}) is not power of two"
)
w w
} }
} }
@@ -167,9 +178,16 @@ class CoalescingUnit(config: CoalescerConfig)(implicit p: Parameters) extends La
// Protocol-agnostic bundles that represent a request and a response to the // Protocol-agnostic bundles that represent a request and a response to the
// coalescer. // coalescer.
class Request(sourceWidth: Int, sizeWidth: Int, addressWidth: Int, dataWidth: Int) class Request(
extends Bundle { sourceWidth: Int,
require(dataWidth % 8 == 0, s"dataWidth (${dataWidth} bits) is not multiple of 8") sizeWidth: Int,
addressWidth: Int,
dataWidth: Int
) extends Bundle {
require(
dataWidth % 8 == 0,
s"dataWidth (${dataWidth} bits) is not multiple of 8"
)
val op = Bool() // 0=READ 1=WRITE val op = Bool() // 0=READ 1=WRITE
val address = UInt(addressWidth.W) val address = UInt(addressWidth.W)
val size = UInt(sizeWidth.W) val size = UInt(sizeWidth.W)
@@ -213,7 +231,10 @@ case class CoalescedRequest(config: CoalescerConfig)
class Response(sourceWidth: Int, sizeWidth: Int, dataWidth: Int) class Response(sourceWidth: Int, sizeWidth: Int, dataWidth: Int)
extends Bundle { extends Bundle {
require(dataWidth % 8 == 0, s"dataWidth (${dataWidth} bits) is not multiple of 8") require(
dataWidth % 8 == 0,
s"dataWidth (${dataWidth} bits) is not multiple of 8"
)
val op = UInt(1.W) // 0=READ 1=WRITE val op = UInt(1.W) // 0=READ 1=WRITE
val size = UInt(sizeWidth.W) val size = UInt(sizeWidth.W)
val source = UInt(sourceWidth.W) val source = UInt(sourceWidth.W)
@@ -262,13 +283,13 @@ case class CoalescedResponse(config: CoalescerConfig)
// If `ignoreInUse`, just keep giving out new IDs without any collision checking. // If `ignoreInUse`, just keep giving out new IDs without any collision checking.
// This might result in TL violation. // This might result in TL violation.
class SourceGenerator[T <: Data]( class SourceGenerator[T <: Data](
sourceWidth: Int, sourceWidth: Int,
metadata: Option[T] = None, metadata: Option[T] = None,
ignoreInUse: Boolean = false ignoreInUse: Boolean = false
) extends Module { ) extends Module {
def getMetadataType = metadata match { def getMetadataType = metadata match {
case Some(gen) => gen.cloneType case Some(gen) => gen.cloneType
case None => UInt(0.W) case None => UInt(0.W)
} }
val io = IO(new Bundle { val io = IO(new Bundle {
val gen = Input(Bool()) val gen = Input(Bool())
@@ -327,7 +348,7 @@ class SourceGenerator[T <: Data](
} }
when(io.gen && io.id.valid) { when(io.gen && io.id.valid) {
when (!io.reclaim.valid) { when(!io.reclaim.valid) {
assert(outstanding < (1 << sourceWidth).U) assert(outstanding < (1 << sourceWidth).U)
outstanding := outstanding + 1.U outstanding := outstanding + 1.U
} }
@@ -627,7 +648,7 @@ class MonoCoalescer(
class MultiCoalescer( class MultiCoalescer(
config: CoalescerConfig, config: CoalescerConfig,
queueT: CoalShiftQueue[NonCoalescedRequest], queueT: CoalShiftQueue[NonCoalescedRequest],
coalReqT: CoalescedRequest, coalReqT: CoalescedRequest
) extends Module { ) extends Module {
val invalidateT = Valid(Vec(config.numLanes, UInt(config.reqQueueDepth.W))) val invalidateT = Valid(Vec(config.numLanes, UInt(config.reqQueueDepth.W)))
val io = IO(new Bundle { val io = IO(new Bundle {
@@ -864,7 +885,9 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
reqQueues.io.coalescable := coalescer.io.coalescable reqQueues.io.coalescable := coalescer.io.coalescable
reqQueues.io.invalidate := coalescer.io.invalidate reqQueues.io.invalidate := coalescer.io.invalidate
val inflightTable = Module(new InFlightTable(config, nonCoalReqT, coalReqT, coalRespT)) val inflightTable = Module(
new InFlightTable(config, nonCoalReqT, coalReqT, coalRespT)
)
val uncoalescer = Module(new Uncoalescer(config, inflightTable.entryT)) val uncoalescer = Module(new Uncoalescer(config, inflightTable.entryT))
// =========================================================================== // ===========================================================================
@@ -903,27 +926,27 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
assert(legal, "unhandled illegal TL req gen") assert(legal, "unhandled illegal TL req gen")
} }
// debug // debug
// when (tlIn.a.valid) { // when (tlIn.a.valid) {
// TLPrintf(s"tlIn(${lane}).a", // TLPrintf(s"tlIn(${lane}).a",
// tlIn.a.bits.address, // tlIn.a.bits.address,
// tlIn.a.bits.size, // tlIn.a.bits.size,
// tlIn.a.bits.mask, // tlIn.a.bits.mask,
// TLUtils.AOpcodeIsStore(tlIn.a.bits.opcode), // TLUtils.AOpcodeIsStore(tlIn.a.bits.opcode),
// tlIn.a.bits.data, // tlIn.a.bits.data,
// 0.U // 0.U
// ) // )
// } // }
// when (tlOut.a.valid) { // when (tlOut.a.valid) {
// TLPrintf(s"tlOut(${lane}).a", // TLPrintf(s"tlOut(${lane}).a",
// tlOut.a.bits.address, // tlOut.a.bits.address,
// tlOut.a.bits.size, // tlOut.a.bits.size,
// tlOut.a.bits.mask, // tlOut.a.bits.mask,
// TLUtils.AOpcodeIsStore(tlOut.a.bits.opcode), // TLUtils.AOpcodeIsStore(tlOut.a.bits.opcode),
// tlOut.a.bits.data, // tlOut.a.bits.data,
// 0.U // 0.U
// ) // )
// } // }
} }
val (tlCoal, edgeCoal) = outer.coalescerNode.out.head val (tlCoal, edgeCoal) = outer.coalescerNode.out.head
@@ -942,7 +965,9 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
// │ RespQueues ├─┤ Uncoalescer ├─┤ CoalSourceGen(reclaim) ├─┤ InFlightTable ├── TileLink resp // │ RespQueues ├─┤ Uncoalescer ├─┤ CoalSourceGen(reclaim) ├─┤ InFlightTable ├── TileLink resp
// └────────────┘ └─────────────┘ └────────────────────────┘ └───────────────┘ // └────────────┘ └─────────────┘ └────────────────────────┘ └───────────────┘
// //
val coalSourceGen = Module(new CoalescerSourceGen(config, coalReqT, tlCoal.d.bits)) val coalSourceGen = Module(
new CoalescerSourceGen(config, coalReqT, tlCoal.d.bits)
)
coalSourceGen.io.inReq <> coalescer.io.coalReq coalSourceGen.io.inReq <> coalescer.io.coalReq
// InflightTable IO // InflightTable IO
@@ -1043,8 +1068,10 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
// MultiPortQueue, and eventually serialized. // MultiPortQueue, and eventually serialized.
respQueue.io.enq(respQueueNoncoalPort).valid := tlOut.d.valid respQueue.io.enq(respQueueNoncoalPort).valid := tlOut.d.valid
respQueue.io.enq(respQueueNoncoalPort).bits := resp respQueue.io.enq(respQueueNoncoalPort).bits := resp
assert(respQueue.io.deq.length == 1, assert(
"respQueue should have only one dequeue port to the upstream") respQueue.io.deq.length == 1,
"respQueue should have only one dequeue port to the upstream"
)
respQueue.io.deq.head.ready := tlIn.d.ready respQueue.io.deq.head.ready := tlIn.d.ready
tlIn.d.valid := respQueue.io.deq.head.valid tlIn.d.valid := respQueue.io.deq.head.valid
@@ -1074,7 +1101,8 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
// //
// Connect coalesced response // Connect coalesced response
uncoalescer.io.coalResp.valid := coalSourceGen.io.inResp.valid uncoalescer.io.coalResp.valid := coalSourceGen.io.inResp.valid
uncoalescer.io.coalResp.bits.fromTLD(coalSourceGen.io.inResp.bits, coalSourceGen.io.inResp.fire) uncoalescer.io.coalResp.bits
.fromTLD(coalSourceGen.io.inResp.bits, coalSourceGen.io.inResp.fire)
coalSourceGen.io.inResp.ready := uncoalescer.io.coalResp.ready coalSourceGen.io.inResp.ready := uncoalescer.io.coalResp.ready
// Connect lookup result from InflightTable // Connect lookup result from InflightTable
@@ -1086,24 +1114,27 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
inflightTable.io.lookupSourceId.bits := coalSourceGen.io.inResp.bits.source inflightTable.io.lookupSourceId.bits := coalSourceGen.io.inResp.bits.source
// Connect uncoalescer results back into response queue // Connect uncoalescer results back into response queue
(respQueues zip uncoalescer.io.respQueueIO).zipWithIndex.foreach (respQueues zip uncoalescer.io.respQueueIO).zipWithIndex.foreach {
{ case ((q, sameLaneUncoalResps), lane) => case ((q, sameLaneUncoalResps), lane) =>
// reqQueueDepth here is the maximum number of same-lane, different-time // reqQueueDepth here is the maximum number of same-lane, different-time
// requests that can go into a single coalesced response. We need to have // requests that can go into a single coalesced response. We need to have
// that many enq ports to not backpressure the uncoalescer. // that many enq ports to not backpressure the uncoalescer.
require(q.io.enq.length == config.reqQueueDepth + respQueueUncoalPortOffset, require(
s"wrong number of enq ports for MultiPort response queue") q.io.enq.length == config.reqQueueDepth + respQueueUncoalPortOffset,
// slice the ports reserved for uncoalesced response s"wrong number of enq ports for MultiPort response queue"
val sameLaneEnqPorts = q.io.enq.slice(respQueueUncoalPortOffset, q.io.enq.length) )
(sameLaneEnqPorts zip sameLaneUncoalResps).foreach { // slice the ports reserved for uncoalesced response
case (enqPort, uncoalResp) => { val sameLaneEnqPorts =
enqPort <> uncoalResp q.io.enq.slice(respQueueUncoalPortOffset, q.io.enq.length)
// assert( (sameLaneEnqPorts zip sameLaneUncoalResps).foreach {
// enqPort.ready, case (enqPort, uncoalResp) => {
// cf"respQueue: enq port for uncoalesced response is blocked on lane $lane" enqPort <> uncoalResp
// ) // assert(
// enqPort.ready,
// cf"respQueue: enq port for uncoalesced response is blocked on lane $lane"
// )
}
} }
}
} }
// coalSourceGen is the last module before going to downstream // coalSourceGen is the last module before going to downstream
@@ -1127,7 +1158,8 @@ class Uncoalescer(
val io = IO(new Bundle { val io = IO(new Bundle {
val inflightLookup = Flipped(Decoupled(inflightEntryT)) val inflightLookup = Flipped(Decoupled(inflightEntryT))
val coalResp = Flipped(Decoupled(new CoalescedResponse(config))) val coalResp = Flipped(Decoupled(new CoalescedResponse(config)))
val respQueueIO = Vec(config.numLanes, val respQueueIO = Vec(
config.numLanes,
// reqQueueDepth because if we're doing time-coalescing, that's the // reqQueueDepth because if we're doing time-coalescing, that's the
// maximum number of same-lane, different-time requests that can go into // maximum number of same-lane, different-time requests that can go into
// a single coalesced request. // a single coalesced request.
@@ -1137,7 +1169,12 @@ class Uncoalescer(
// Un-coalescing logic // Un-coalescing logic
// //
def getCoalescedDataChunk(data: UInt, dataWidth: Int, offset: UInt, logSize: UInt): UInt = { def getCoalescedDataChunk(
data: UInt,
dataWidth: Int,
offset: UInt,
logSize: UInt
): UInt = {
assert(logSize === 2.U, "currently only supporting 4-byte accesses. TODO") assert(logSize === 2.U, "currently only supporting 4-byte accesses. TODO")
// sizeInBits should be simulation-only construct // sizeInBits should be simulation-only construct
@@ -1167,46 +1204,52 @@ class Uncoalescer(
// ready. This is necessary because uncoalescing logic is a combinational // ready. This is necessary because uncoalescing logic is a combinational
// logic that produces all the split responses at the same cycle, so it needs // logic that produces all the split responses at the same cycle, so it needs
// to be guaranteed that all of them has somewhere to go. // to be guaranteed that all of them has somewhere to go.
val allRespQueueEnqReady = io.respQueueIO.map(_.map(_.ready).reduce(_ && _)).reduce(_ && _) val allRespQueueEnqReady =
io.respQueueIO.map(_.map(_.ready).reduce(_ && _)).reduce(_ && _)
tablePipeRegDeq.ready := allRespQueueEnqReady tablePipeRegDeq.ready := allRespQueueEnqReady
coalRespPipeRegDeq.ready := allRespQueueEnqReady coalRespPipeRegDeq.ready := allRespQueueEnqReady
assert(io.coalResp.fire === io.inflightLookup.fire, assert(
"enqueue timing for uncoalescer pipeline registers out-of-sync!") io.coalResp.fire === io.inflightLookup.fire,
assert(tablePipeRegDeq.fire === coalRespPipeRegDeq.fire, "enqueue timing for uncoalescer pipeline registers out-of-sync!"
"dequeue timing for uncoalescer pipeline registers out-of-sync!") )
assert(
tablePipeRegDeq.fire === coalRespPipeRegDeq.fire,
"dequeue timing for uncoalescer pipeline registers out-of-sync!"
)
// Un-coalesce responses back to individual lanes. Connect uncoalesced // Un-coalesce responses back to individual lanes. Connect uncoalesced
// results back into each lane's response queue. // results back into each lane's response queue.
val tableRow = tablePipeRegDeq val tableRow = tablePipeRegDeq
(io.respQueueIO zip tableRow.bits.lanes).zipWithIndex.foreach { case ((enqIOs, lane), laneNum) => (io.respQueueIO zip tableRow.bits.lanes).zipWithIndex.foreach {
lane.reqs.zipWithIndex.foreach { case (req, depth) => case ((enqIOs, lane), laneNum) =>
val enqIO = enqIOs(depth) lane.reqs.zipWithIndex.foreach { case (req, depth) =>
enqIO.valid := false.B val enqIO = enqIOs(depth)
enqIO.bits := DontCare enqIO.valid := false.B
// debug enqIO.bits := DontCare
// when (resp.valid) { // debug
// printf(s"${i}-th uncoalesced response came back from lane ${laneNum}\n") // when (resp.valid) {
// } // printf(s"${i}-th uncoalesced response came back from lane ${laneNum}\n")
// dontTouch(q.io.enq(respQueueCoalPortOffset)) // }
// dontTouch(q.io.enq(respQueueCoalPortOffset))
when(tableRow.valid && req.valid) { when(tableRow.valid && req.valid) {
enqIO.valid := tableRow.fire && req.valid enqIO.valid := tableRow.fire && req.valid
enqIO.bits.op := req.op enqIO.bits.op := req.op
enqIO.bits.source := req.source enqIO.bits.source := req.source
val logSize = tableRow.bits.sizeEnumT.enumToLogSize(req.sizeEnum) val logSize = tableRow.bits.sizeEnumT.enumToLogSize(req.sizeEnum)
enqIO.bits.size := logSize enqIO.bits.size := logSize
enqIO.bits.data := enqIO.bits.data :=
getCoalescedDataChunk( getCoalescedDataChunk(
coalRespPipeRegDeq.bits.data, coalRespPipeRegDeq.bits.data,
coalRespPipeRegDeq.bits.data.getWidth, coalRespPipeRegDeq.bits.data.getWidth,
req.offset, req.offset,
logSize logSize
) )
// is this necessary? // is this necessary?
enqIO.bits.error := DontCare enqIO.bits.error := DontCare
}
} }
}
} }
} }
@@ -1219,9 +1262,10 @@ class InFlightTable(
config: CoalescerConfig, config: CoalescerConfig,
nonCoalReqT: NonCoalescedRequest, nonCoalReqT: NonCoalescedRequest,
coalReqT: CoalescedRequest, coalReqT: CoalescedRequest,
coalRespT: CoalescedResponse, coalRespT: CoalescedResponse
) extends Module { ) extends Module {
val offsetBits = config.maxCoalLogSize - config.wordSizeWidth // assumes word offset val offsetBits =
config.maxCoalLogSize - config.wordSizeWidth // assumes word offset
val entryT = new InFlightTableEntry( val entryT = new InFlightTableEntry(
config.numLanes, config.numLanes,
config.reqQueueDepth, config.reqQueueDepth,
@@ -1243,11 +1287,13 @@ class InFlightTable(
val inCoalReq = Flipped(Decoupled(coalReqT)) val inCoalReq = Flipped(Decoupled(coalReqT))
// invalidate signal coming out of coalescer. Needed to generate new entry // invalidate signal coming out of coalescer. Needed to generate new entry
// for the table. // for the table.
val invalidate = Input(Valid(Vec(config.numLanes, UInt(config.reqQueueDepth.W)))) val invalidate =
Input(Valid(Vec(config.numLanes, UInt(config.reqQueueDepth.W))))
// coalescing window, connected to the contents of the request queues. // coalescing window, connected to the contents of the request queues.
// Need this to generate new entry for the table. // Need this to generate new entry for the table.
// TODO: duplicate type construction // TODO: duplicate type construction
val windowElts = Input(Vec(config.numLanes, Vec(config.reqQueueDepth, nonCoalReqT))) val windowElts =
Input(Vec(config.numLanes, Vec(config.reqQueueDepth, nonCoalReqT)))
// InflightTable simply passes through the inCoalReq to outCoalReq, only snooping // InflightTable simply passes through the inCoalReq to outCoalReq, only snooping
// on its data to record what's necessary. // on its data to record what's necessary.
val outCoalReq = Decoupled(coalReqT) val outCoalReq = Decoupled(coalReqT)
@@ -1319,7 +1365,7 @@ class InFlightTable(
reqEntry.source := req.source reqEntry.source := req.source
reqEntry.offset := ((req.address % (1 << config.maxCoalLogSize).U) >> config.wordSizeWidth) reqEntry.offset := ((req.address % (1 << config.maxCoalLogSize).U) >> config.wordSizeWidth)
reqEntry.sizeEnum := config.sizeEnum.logSizeToEnum(req.size) reqEntry.sizeEnum := config.sizeEnum.logSizeToEnum(req.size)
// TODO: load/store op // TODO: load/store op
} }
} }
dontTouch(newEntry) dontTouch(newEntry)
@@ -1347,23 +1393,27 @@ class InFlightTable(
} }
// Lookup logic // Lookup logic
io.lookupResult.valid := io.lookupSourceId.valid && table(io.lookupSourceId.bits).valid io.lookupResult.valid := io.lookupSourceId.valid && table(
io.lookupSourceId.bits
).valid
io.lookupResult.bits := table(io.lookupSourceId.bits).bits io.lookupResult.bits := table(io.lookupSourceId.bits).bits
// every lookup to the table should succeed as the request should have // every lookup to the table should succeed as the request should have
// gotten recorded earlier than the response // gotten recorded earlier than the response
when(io.lookupSourceId.valid) { when(io.lookupSourceId.valid) {
assert(table(io.lookupSourceId.bits).valid === true.B, assert(
"table lookup with a valid sourceId failed") table(io.lookupSourceId.bits).valid === true.B,
"table lookup with a valid sourceId failed"
)
assert(
!(enqFire && io.lookupResult.fire &&
(enqSource === io.lookupSourceId.bits)),
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
)
} }
// Dequeue as soon as lookup succeeds // Dequeue as soon as lookup succeeds
when(io.lookupResult.fire) { when(io.lookupResult.fire) {
table(io.lookupSourceId.bits).valid := false.B table(io.lookupSourceId.bits).valid := false.B
} }
assert(
!((enqFire === true.B) && (io.lookupResult.fire === true.B) &&
(enqSource === io.lookupSourceId.bits)),
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
)
dontTouch(io.lookupResult) dontTouch(io.lookupResult)
} }
@@ -1399,12 +1449,15 @@ object TLUtils {
when(checkOpcode) { when(checkOpcode) {
assert( assert(
opcode === TLMessages.PutFullData || opcode === TLMessages.PutPartialData || opcode === TLMessages.PutFullData || opcode === TLMessages.PutPartialData ||
opcode === TLMessages.Get, opcode === TLMessages.Get,
"unhandled TL A opcode found" "unhandled TL A opcode found"
) )
} }
Mux(opcode === TLMessages.PutFullData || opcode === TLMessages.PutPartialData, Mux(
true.B, false.B) opcode === TLMessages.PutFullData || opcode === TLMessages.PutPartialData,
true.B,
false.B
)
} }
def DOpcodeIsStore(opcode: UInt, checkOpcode: Bool): Bool = { def DOpcodeIsStore(opcode: UInt, checkOpcode: Bool): Bool = {
when(checkOpcode) { when(checkOpcode) {
@@ -1531,92 +1584,95 @@ class MemTraceDriverImp(
Cat(8.U(4.W), addr(27, 0)) Cat(8.U(4.W), addr(27, 0))
} }
val sourceGens = Seq.fill(config.numLanes)(Module( val sourceGens = Seq.fill(config.numLanes)(
new SourceGenerator( Module(
log2Ceil(config.numOldSrcIds), new SourceGenerator(
ignoreInUse = false log2Ceil(config.numOldSrcIds),
ignoreInUse = false
)
) )
)) )
// Advance source ID for all lanes in synchrony // Advance source ID for all lanes in synchrony
val syncedSourceGenValid = sourceGens.map(_.io.id.valid).reduce(_ && _) val syncedSourceGenValid = sourceGens.map(_.io.id.valid).reduce(_ && _)
// Take requests off of the queue and generate TL requests // Take requests off of the queue and generate TL requests
(outer.laneNodes zip reqQueues).zipWithIndex.foreach { case ((node, reqQ), lane) => (outer.laneNodes zip reqQueues).zipWithIndex.foreach {
val (tlOut, edge) = node.out(0) case ((node, reqQ), lane) =>
val (tlOut, edge) = node.out(0)
val req = reqQ.io.deq.bits val req = reqQ.io.deq.bits
// backpressure from downstream propagates into the queue // backpressure from downstream propagates into the queue
reqQ.io.deq.ready := tlOut.a.ready && syncedSourceGenValid reqQ.io.deq.ready := tlOut.a.ready && syncedSourceGenValid
// Core only makes accesses of granularity larger than a word, so we want // Core only makes accesses of granularity larger than a word, so we want
// the trace driver to act so as well. // the trace driver to act so as well.
// That means if req.size is smaller than word size, we need to pad data // That means if req.size is smaller than word size, we need to pad data
// with zeros to generate a word-size request, and set mask accordingly. // with zeros to generate a word-size request, and set mask accordingly.
val offsetInWord = req.address % config.wordSizeInBytes.U val offsetInWord = req.address % config.wordSizeInBytes.U
val subword = req.size < log2Ceil(config.wordSizeInBytes).U val subword = req.size < log2Ceil(config.wordSizeInBytes).U
// `mask` is currently unused // `mask` is currently unused
// val mask = Wire(UInt(config.wordSizeInBytes.W)) // val mask = Wire(UInt(config.wordSizeInBytes.W))
val wordData = Wire(UInt((config.wordSizeInBytes * 8 * 2).W)) val wordData = Wire(UInt((config.wordSizeInBytes * 8 * 2).W))
val sizeInBytes = Wire(UInt((sizeW + 1).W)) val sizeInBytes = Wire(UInt((sizeW + 1).W))
sizeInBytes := (1.U) << req.size sizeInBytes := (1.U) << req.size
// mask := Mux(subword, (~((~0.U(64.W)) << sizeInBytes)) << offsetInWord, ~0.U) // mask := Mux(subword, (~((~0.U(64.W)) << sizeInBytes)) << offsetInWord, ~0.U)
wordData := Mux(subword, req.data << (offsetInWord * 8.U), req.data) wordData := Mux(subword, req.data << (offsetInWord * 8.U), req.data)
val wordAlignedAddress = val wordAlignedAddress =
req.address & ~((1 << log2Ceil(config.wordSizeInBytes)) - 1).U(addrW.W) req.address & ~((1 << log2Ceil(config.wordSizeInBytes)) - 1).U(addrW.W)
val wordAlignedSize = Mux(subword, 2.U, req.size) val wordAlignedSize = Mux(subword, 2.U, req.size)
val sourceGen = sourceGens(lane) val sourceGen = sourceGens(lane)
sourceGen.io.gen := tlOut.a.fire sourceGen.io.gen := tlOut.a.fire
// assert(sourceGen.io.id.valid) // assert(sourceGen.io.id.valid)
sourceGen.io.reclaim.valid := tlOut.d.fire sourceGen.io.reclaim.valid := tlOut.d.fire
sourceGen.io.reclaim.bits := tlOut.d.bits.source sourceGen.io.reclaim.bits := tlOut.d.bits.source
sourceGen.io.meta := DontCare sourceGen.io.meta := DontCare
val (plegal, pbits) = edge.Put( val (plegal, pbits) = edge.Put(
fromSource = sourceGen.io.id.bits, fromSource = sourceGen.io.id.bits,
toAddress = hashToValidPhyAddr(wordAlignedAddress), toAddress = hashToValidPhyAddr(wordAlignedAddress),
lgSize = wordAlignedSize, // trace line already holds log2(size) lgSize = wordAlignedSize, // trace line already holds log2(size)
// data should be aligned to beatBytes // data should be aligned to beatBytes
data = data =
(wordData << (8.U * (wordAlignedAddress % edge.manager.beatBytes.U))).asUInt (wordData << (8.U * (wordAlignedAddress % edge.manager.beatBytes.U))).asUInt
)
val (glegal, gbits) = edge.Get(
fromSource = sourceGen.io.id.bits,
toAddress = hashToValidPhyAddr(wordAlignedAddress),
lgSize = wordAlignedSize
)
val legal = Mux(req.is_store, plegal, glegal)
val bits = Mux(req.is_store, pbits, gbits)
tlOut.a.valid := reqQ.io.deq.valid && syncedSourceGenValid
when(tlOut.a.fire) {
assert(legal, "illegal TL req gen")
}
tlOut.a.bits := bits
tlOut.b.ready := true.B
tlOut.c.valid := false.B
tlOut.d.ready := true.B
tlOut.e.valid := false.B
// debug
dontTouch(reqQ.io.enq)
dontTouch(reqQ.io.deq)
when(tlOut.a.valid) {
TLPrintf(
"MemTraceDriver",
tlOut.a.bits.source,
tlOut.a.bits.address,
tlOut.a.bits.size,
tlOut.a.bits.mask,
req.is_store,
tlOut.a.bits.data,
req.data
) )
} val (glegal, gbits) = edge.Get(
dontTouch(tlOut.a) fromSource = sourceGen.io.id.bits,
dontTouch(tlOut.d) toAddress = hashToValidPhyAddr(wordAlignedAddress),
lgSize = wordAlignedSize
)
val legal = Mux(req.is_store, plegal, glegal)
val bits = Mux(req.is_store, pbits, gbits)
tlOut.a.valid := reqQ.io.deq.valid && syncedSourceGenValid
when(tlOut.a.fire) {
assert(legal, "illegal TL req gen")
}
tlOut.a.bits := bits
tlOut.b.ready := true.B
tlOut.c.valid := false.B
tlOut.d.ready := true.B
tlOut.e.valid := false.B
// debug
dontTouch(reqQ.io.enq)
dontTouch(reqQ.io.deq)
when(tlOut.a.valid) {
TLPrintf(
"MemTraceDriver",
tlOut.a.bits.source,
tlOut.a.bits.address,
tlOut.a.bits.size,
tlOut.a.bits.mask,
req.is_store,
tlOut.a.bits.data,
req.data
)
}
dontTouch(tlOut.a)
dontTouch(tlOut.d)
} }
// Give some slack time after trace EOF to get some outstanding responses // Give some slack time after trace EOF to get some outstanding responses
@@ -1627,8 +1683,8 @@ class MemTraceDriverImp(
} }
io.finished := traceFinished io.finished := traceFinished
//currently the .cc file ouptuts finished=true while it still need to issue one more request // currently the .cc file ouptuts finished=true while it still need to issue one more request
val noValidReqs = sim.io.trace_read.valid === 0.U val noValidReqs = sim.io.trace_read.valid === 0.U
val allReqReclaimed = !(sourceGens.map(_.io.inflight).reduce(_ || _)) val allReqReclaimed = !(sourceGens.map(_.io.inflight).reduce(_ || _))
when(traceFinished && allReqReclaimed && noValidReqs) { when(traceFinished && allReqReclaimed && noValidReqs) {
@@ -1821,7 +1877,10 @@ class MemTraceLogger(
// and transaction happened. // and transaction happened.
resp.valid := tlOut.d.fire resp.valid := tlOut.d.fire
resp.size := tlOut.d.bits.size resp.size := tlOut.d.bits.size
resp.is_store := TLUtils.DOpcodeIsStore(tlOut.d.bits.opcode, tlOut.d.fire) resp.is_store := TLUtils.DOpcodeIsStore(
tlOut.d.bits.opcode,
tlOut.d.fire
)
resp.source := tlOut.d.bits.source resp.source := tlOut.d.bits.source
// NOTE: TL D channel doesn't carry address nor mask, so there's no easy // NOTE: TL D channel doesn't carry address nor mask, so there's no easy
// way to figure out which bytes the master actually use. Since we // way to figure out which bytes the master actually use. Since we
@@ -1911,7 +1970,7 @@ class MemTraceLogger(
// depending on this, e.g. response trace will not contain an address column. // depending on this, e.g. response trace will not contain an address column.
class SimMemTraceLogger( class SimMemTraceLogger(
isResponse: Boolean, isResponse: Boolean,
filenameBase: String, // usually the same as `filename` of SimMemTrace filenameBase: String, // usually the same as `filename` of SimMemTrace
filenameSuffix: String, // can be ".req", ".resp", .etc filenameSuffix: String, // can be ".req", ".resp", .etc
numLanes: Int numLanes: Int
) extends BlackBox( ) extends BlackBox(