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:
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user