Split IO for lookup and dealloc in InflightTable

The usage of `lookup` and `lookupSourceId` ports were being conflated in
terms of table lookup and dealloc.  Now `lookupSourceId` solely controls
lookup operation and vice versa.  This simplifies pipeline reg logic in
uncoalescer.
This commit is contained in:
Hansung Kim
2024-01-18 22:02:06 -08:00
parent 9ae1d9c392
commit 0fd4d0a76f

View File

@@ -858,12 +858,13 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
) )
val coalReqT = new CoalescedRequest(config) val coalReqT = new CoalescedRequest(config)
val coalRespT = new CoalescedResponse(config)
val coalescer = Module(new MultiCoalescer(config, reqQueues, coalReqT)) val coalescer = Module(new MultiCoalescer(config, reqQueues, coalReqT))
coalescer.io.window := reqQueues.io coalescer.io.window := reqQueues.io
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)) 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))
// =========================================================================== // ===========================================================================
@@ -1077,11 +1078,12 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig)
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
uncoalescer.io.inflightLookup <> inflightTable.io.lookup uncoalescer.io.inflightLookup <> inflightTable.io.lookupResult
// Look up the inflight table with incoming coalesced responses // Look up the inflight table with incoming coalesced responses
// @cleanup: would be cleaner if inflightTable lookup is contained inside // @cleanup: would be cleaner if inflightTable lookup is contained inside
// uncoalescer // uncoalescer
inflightTable.io.lookupSourceId := coalSourceGen.io.inResp.bits.source inflightTable.io.lookupSourceId.valid := coalSourceGen.io.inResp.valid
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
@@ -1158,34 +1160,25 @@ class Uncoalescer(
// Pipeline registers for the inflight table lookup result, and the coalesced // Pipeline registers for the inflight table lookup result, and the coalesced
// response itself. We cut timing here expecting that the table lookup // response itself. We cut timing here expecting that the table lookup
// will take up a long path. // will take up a long path.
val coalRespPipeReg = Module(new Queue(chiselTypeOf(io.coalResp.bits), 1, pipe = true)) val coalRespPipeRegDeq = Queue(io.coalResp, 1, pipe = true)
coalRespPipeReg.io.enq <> io.coalResp val tablePipeRegDeq = Queue(io.inflightLookup, 1, pipe = true)
val tablePipeReg = Module(new Queue(chiselTypeOf(io.inflightLookup.bits), 1, pipe = true))
tablePipeReg.io.enq <> io.inflightLookup
tablePipeReg.io.enq.valid := io.inflightLookup.fire
// inflightTable looks up as soon as ready signal goes up, assuming
// io.lookupSourceId is valid, so need to be careful when we assert ready by
// checking both if we have space in the pipeline register, and if there is a
// valid response on the channel
io.inflightLookup.ready := tablePipeReg.io.enq.ready && io.coalResp.fire
// Only proceed uncoalescing when all enq ports of the response queue are // Only proceed uncoalescing when all enq ports of the response queue are
// 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(_ && _)
tablePipeReg.io.deq.ready := allRespQueueEnqReady tablePipeRegDeq.ready := allRespQueueEnqReady
coalRespPipeReg.io.deq.ready := allRespQueueEnqReady coalRespPipeRegDeq.ready := allRespQueueEnqReady
assert(tablePipeReg.io.enq.fire === coalRespPipeReg.io.enq.fire, assert(io.coalResp.fire === io.inflightLookup.fire,
"enqueue timing for uncoalescer pipeline registers out-of-sync!") "enqueue timing for uncoalescer pipeline registers out-of-sync!")
assert(tablePipeReg.io.deq.fire === coalRespPipeReg.io.deq.fire, assert(tablePipeRegDeq.fire === coalRespPipeRegDeq.fire,
"dequeue timing for uncoalescer pipeline registers out-of-sync!") "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 = tablePipeReg.io.deq val tableRow = tablePipeRegDeq
(io.respQueueIO zip tableRow.bits.lanes).zipWithIndex.foreach { case ((enqIOs, lane), laneNum) => (io.respQueueIO zip tableRow.bits.lanes).zipWithIndex.foreach { case ((enqIOs, lane), laneNum) =>
lane.reqs.zipWithIndex.foreach { case (req, depth) => lane.reqs.zipWithIndex.foreach { case (req, depth) =>
val enqIO = enqIOs(depth) val enqIO = enqIOs(depth)
@@ -1205,8 +1198,8 @@ class Uncoalescer(
enqIO.bits.size := logSize enqIO.bits.size := logSize
enqIO.bits.data := enqIO.bits.data :=
getCoalescedDataChunk( getCoalescedDataChunk(
coalRespPipeReg.io.deq.bits.data, coalRespPipeRegDeq.bits.data,
coalRespPipeReg.io.deq.bits.data.getWidth, coalRespPipeRegDeq.bits.data.getWidth,
req.offset, req.offset,
logSize logSize
) )
@@ -1226,6 +1219,7 @@ class InFlightTable(
config: CoalescerConfig, config: CoalescerConfig,
nonCoalReqT: NonCoalescedRequest, nonCoalReqT: NonCoalescedRequest,
coalReqT: CoalescedRequest, coalReqT: CoalescedRequest,
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(
@@ -1240,7 +1234,7 @@ class InFlightTable(
val sourceWidth = log2Ceil(config.numOldSrcIds) val sourceWidth = log2Ceil(config.numOldSrcIds)
val io = IO(new Bundle { val io = IO(new Bundle {
// Enqueue IO // Enqueue/register IO
// //
// generated coalesced request, connected to the output of the coalescer. // generated coalesced request, connected to the output of the coalescer.
// val coalReq = Flipped(DecoupledIO(coalReqT.cloneType)) // val coalReq = Flipped(DecoupledIO(coalReqT.cloneType))
@@ -1258,14 +1252,14 @@ class InFlightTable(
// on its data to record what's necessary. // on its data to record what's necessary.
val outCoalReq = Decoupled(coalReqT) val outCoalReq = Decoupled(coalReqT)
// Lookup outputs // Dequeue/lookup IO
// //
// TODO: return actual stuff // Initiates table lookup via (valid, sourceId). The lookup result will be
val lookup = Decoupled(entryT) // placed on lookupResult.
// this should really be a part of lookup, but since lookup only accepts val lookupSourceId = Input(Valid(UInt(sourceWidth.W)))
// ready, we need another port for accepting sourceId. Could maybe have a // lookupResult.ready indicates when the user module consumed the table
// custom decoupledIO with ready+sourceId input. @cleanup // entry, so that the entry can be safely deallocated for later use.
val lookupSourceId = Input(UInt(sourceWidth.W)) val lookupResult = Decoupled(entryT)
}) })
println(s"CoalescingUnit InFlightTable config: {") println(s"CoalescingUnit InFlightTable config: {")
@@ -1353,25 +1347,25 @@ class InFlightTable(
} }
// Lookup logic // Lookup logic
io.lookup.valid := table(io.lookupSourceId).valid io.lookupResult.valid := io.lookupSourceId.valid && table(io.lookupSourceId.bits).valid
io.lookup.bits := table(io.lookupSourceId).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.lookup.ready) { when(io.lookupSourceId.valid) {
assert(table(io.lookupSourceId).valid === true.B, assert(table(io.lookupSourceId.bits).valid === true.B,
"table lookup with a valid sourceId failed") "table lookup with a valid sourceId failed")
} }
// Dequeue as soon as lookup succeeds // Dequeue as soon as lookup succeeds
when(io.lookup.fire) { when(io.lookupResult.fire) {
table(io.lookupSourceId).valid := false.B table(io.lookupSourceId.bits).valid := false.B
} }
assert( assert(
!((enqFire === true.B) && (io.lookup.fire === true.B) && !((enqFire === true.B) && (io.lookupResult.fire === true.B) &&
(enqSource === io.lookupSourceId)), (enqSource === io.lookupSourceId.bits)),
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled" "inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
) )
dontTouch(io.lookup) dontTouch(io.lookupResult)
} }
class InFlightTableEntry( class InFlightTableEntry(