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