Put pipereg between uncoalescer output and respQueue

... to break the combinational loop resulting from MultiPortQueue's
enq.ready port being dependent on enq.valid.  This seems to manifest
only when time-coalescing is enabled.
This commit is contained in:
Hansung Kim
2024-01-22 16:51:35 -08:00
parent 75d51e3d1d
commit f26c9dfb11

View File

@@ -1185,8 +1185,6 @@ class Uncoalescer(
offset: UInt, offset: UInt,
logSize: UInt logSize: UInt
): UInt = { ): UInt = {
assert(logSize === 2.U, "currently only supporting 4-byte accesses. TODO")
// sizeInBits should be simulation-only construct // sizeInBits should be simulation-only construct
val sizeInBits = ((1.U << logSize) << 3.U).asUInt val sizeInBits = ((1.U << logSize) << 3.U).asUInt
assert( assert(
@@ -1210,14 +1208,21 @@ class Uncoalescer(
val coalRespPipeRegDeq = Queue(io.coalResp, 1, pipe = true) val coalRespPipeRegDeq = Queue(io.coalResp, 1, pipe = true)
val tablePipeRegDeq = Queue(io.inflightLookup, 1, pipe = true) val tablePipeRegDeq = Queue(io.inflightLookup, 1, pipe = true)
// Only proceed uncoalescing when all enq ports of the response queue are // Pipeline registers staging the uncoalesced requests before it goes into
// ready. This is necessary because uncoalescing logic is a combinational // respQueues.
// logic that produces all the split responses at the same cycle, so it needs val uncoalPipeRegs = Seq.fill(config.numLanes)(
// to be guaranteed that all of them has somewhere to go. Seq.fill(config.timeCoalWindowSize)(
val allRespQueueEnqReady = Module(new Queue(new NonCoalescedResponse(config), 1, pipe = true)))
io.respQueueIO.map(_.map(_.ready).reduce(_ && _)).reduce(_ && _) )
tablePipeRegDeq.ready := allRespQueueEnqReady val allUncoalPipelineRegsReady =
coalRespPipeRegDeq.ready := allRespQueueEnqReady uncoalPipeRegs.map(_.map(_.io.enq.ready).reduce(_ && _)).reduce(_ && _)
// Only proceed uncoalescing when all enq ports of the next pipeline
// registers are ready. This is necessary because uncoalescing logic is a
// combinational 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.
tablePipeRegDeq.ready := allUncoalPipelineRegsReady
coalRespPipeRegDeq.ready := allUncoalPipelineRegsReady
assert( assert(
io.coalResp.fire === io.inflightLookup.fire, io.coalResp.fire === io.inflightLookup.fire,
@@ -1231,35 +1236,42 @@ class Uncoalescer(
// 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 { (uncoalPipeRegs zip tableRow.bits.lanes).zipWithIndex.foreach {
case ((enqIOs, lane), laneNum) => case ((laneRegs, tableLane), laneNum) =>
lane.reqs.zipWithIndex.foreach { case (req, depth) => (laneRegs zip tableLane.reqs).foreach { case (pipeReg, tableReq) =>
val enqIO = enqIOs(depth) val enqIO = pipeReg.io.enq
enqIO.valid := false.B enqIO.valid := tableRow.fire && tableReq.valid
enqIO.bits := DontCare enqIO.bits.op := tableReq.op
enqIO.bits.source := tableReq.source
val logSize = tableRow.bits.sizeEnumT.enumToLogSize(tableReq.sizeEnum)
enqIO.bits.size := logSize
enqIO.bits.data :=
getCoalescedDataChunk(
coalRespPipeRegDeq.bits.data,
coalRespPipeRegDeq.bits.data.getWidth,
tableReq.offset,
logSize
)
// is this necessary?
enqIO.bits.error := DontCare
// debug // debug
// when (resp.valid) { // when (resp.valid) {
// printf(s"${i}-th uncoalesced response came back from lane ${laneNum}\n") // 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) { // connect pipeline reg output to respQueueIO
enqIO.valid := tableRow.fire && req.valid (io.respQueueIO zip uncoalPipeRegs).foreach {
enqIO.bits.op := req.op case (laneQueue, laneRegs) => {
enqIO.bits.source := req.source (laneQueue zip laneRegs).foreach {
val logSize = tableRow.bits.sizeEnumT.enumToLogSize(req.sizeEnum) case (respQIO, reg) => {
enqIO.bits.size := logSize respQIO <> reg.io.deq
enqIO.bits.data :=
getCoalescedDataChunk(
coalRespPipeRegDeq.bits.data,
coalRespPipeRegDeq.bits.data.getWidth,
req.offset,
logSize
)
// is this necessary?
enqIO.bits.error := DontCare
} }
} }
}
} }
} }