Make table RAM, not CAM
This commit is contained in:
@@ -147,10 +147,10 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters)
|
|||||||
val tableEntry = Wire(inflightCoalReqTable.entryT)
|
val tableEntry = Wire(inflightCoalReqTable.entryT)
|
||||||
tableEntry.respSourceId := coalSourceId
|
tableEntry.respSourceId := coalSourceId
|
||||||
tableEntry.lanes.foreach { l =>
|
tableEntry.lanes.foreach { l =>
|
||||||
l.foreach { perLaneReq =>
|
l.foreach { singleLaneReq =>
|
||||||
// TODO: this part needs the actual coalescing logic to work
|
// TODO: this part needs the actual coalescing logic to work
|
||||||
perLaneReq.offset := 2.U
|
singleLaneReq.offset := 2.U
|
||||||
perLaneReq.size := 2.U
|
singleLaneReq.size := 2.U
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dontTouch(tableEntry)
|
dontTouch(tableEntry)
|
||||||
@@ -206,7 +206,8 @@ class InflightCoalReqTable(
|
|||||||
new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits)
|
new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits)
|
||||||
|
|
||||||
val io = IO(new Bundle {
|
val io = IO(new Bundle {
|
||||||
val enq = Flipped(EnqIO(entryT))
|
val enq = Flipped(Decoupled(entryT))
|
||||||
|
// TODO: return actual stuff
|
||||||
val lookup = Decoupled(UInt(sourceWidth.W))
|
val lookup = Decoupled(UInt(sourceWidth.W))
|
||||||
// TODO: put this inside decoupledIO
|
// TODO: put this inside decoupledIO
|
||||||
val lookupSourceId = Input(UInt(sourceWidth.W))
|
val lookupSourceId = Input(UInt(sourceWidth.W))
|
||||||
@@ -233,73 +234,38 @@ class InflightCoalReqTable(
|
|||||||
full := (0 until entries)
|
full := (0 until entries)
|
||||||
.map { i => table(i).valid }
|
.map { i => table(i).valid }
|
||||||
.reduce { (v0, v1) => v0 && v1 }
|
.reduce { (v0, v1) => v0 && v1 }
|
||||||
|
// Inflight table should never be full. It should have enough number of
|
||||||
val enqFire = io.enq.ready && io.enq.valid
|
// entries to keep track of all core-side requests for every lane, for every
|
||||||
val lookupFire = io.lookup.ready && io.lookup.valid
|
// per-lane srcId, for every new coalesced srcId.
|
||||||
|
assert(!full, "table is blocking coalescer")
|
||||||
|
dontTouch(full)
|
||||||
|
|
||||||
// Enqueue logic
|
// Enqueue logic
|
||||||
//
|
//
|
||||||
// Instantiate simple cascade of muxes that indicate what is the current
|
|
||||||
// minimum index that has an empty spot in the table.
|
|
||||||
val cascadeEmptyIndex = Seq.tabulate(entries) { i => WireInit(i.U) }
|
|
||||||
(0 until entries - 1).reverse.foreach { i =>
|
|
||||||
val empty = !table(i).valid
|
|
||||||
assert(i + 1 < entries)
|
|
||||||
// If entry with a lower index is empty, it always takes priority
|
|
||||||
cascadeEmptyIndex(i) := Mux(empty, i.U, cascadeEmptyIndex(i + 1))
|
|
||||||
}
|
|
||||||
val chosenEmptyIndex = cascadeEmptyIndex(0)
|
|
||||||
dontTouch(chosenEmptyIndex)
|
|
||||||
dontTouch(full)
|
|
||||||
|
|
||||||
io.enq.ready := !full
|
io.enq.ready := !full
|
||||||
// actual write will happen down below
|
val enqFire = io.enq.ready && io.enq.valid
|
||||||
|
|
||||||
// Currently, we assume coalescer never blocks generating coalesced requests.
|
|
||||||
// If this ever happens, it means the table is insufficiently large to keep
|
|
||||||
// track of the maximum number of in-flight requests and should be enlarged
|
|
||||||
// in size.
|
|
||||||
// assert(!full, "coalescer is blocking responses")
|
|
||||||
|
|
||||||
// Lookup logic
|
|
||||||
//
|
|
||||||
// Same deal as cascadeEmptyIndex, but for finding a respSourceId match
|
|
||||||
// FIXME: tree structure may be better. Any library for instantiating CAM?
|
|
||||||
val cascadeMatchIndex = Seq.tabulate(entries) { i => WireInit(i.U) }
|
|
||||||
(0 until entries - 1).reverse.foreach { i =>
|
|
||||||
val match_ = table(i).bits.respSourceId === io.lookupSourceId
|
|
||||||
assert(i + 1 < entries)
|
|
||||||
// If entry with a lower index is empty, it always takes priority
|
|
||||||
cascadeMatchIndex(i) := Mux(match_, i.U, cascadeMatchIndex(i + 1))
|
|
||||||
}
|
|
||||||
// width will be inferred after cascadeMatchIndex
|
|
||||||
val matchIndex = Wire(UInt())
|
|
||||||
matchIndex := cascadeMatchIndex(0)
|
|
||||||
val matchValid = table(matchIndex).valid &&
|
|
||||||
(table(matchIndex).bits.respSourceId === io.lookupSourceId)
|
|
||||||
io.lookup.valid := matchValid
|
|
||||||
// TODO: return something actually useful
|
|
||||||
io.lookup.bits := table(matchIndex).bits.respSourceId
|
|
||||||
|
|
||||||
when(lookupFire) {
|
|
||||||
// As soon as a lookup returns a match, dequeue that entry
|
|
||||||
table(matchIndex).valid := false.B
|
|
||||||
}
|
|
||||||
|
|
||||||
when(enqFire) {
|
when(enqFire) {
|
||||||
// When we're enqueueing and looking up at the same time, we want to reuse
|
// TODO: should we handle enqueueing and looking up the same entry in the same cycle?
|
||||||
// the current matching entry for writing the new incoming entry
|
val entryToWrite = table(io.enq.bits.respSourceId)
|
||||||
// to prevent having to write to two locations at the same cycle.
|
assert(
|
||||||
// TODO: This might or might not be an issue w.r.t. write ports of the
|
!entryToWrite.valid,
|
||||||
// registers, double-check
|
"tried to enqueue to an already occupied entry"
|
||||||
val indexToWrite = Mux(lookupFire, matchIndex, chosenEmptyIndex)
|
)
|
||||||
val entryToWrite = table(indexToWrite)
|
|
||||||
entryToWrite.valid := true.B
|
entryToWrite.valid := true.B
|
||||||
entryToWrite.bits := io.enq.bits
|
entryToWrite.bits := io.enq.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lookup logic
|
||||||
|
//
|
||||||
|
io.lookup.valid := table(io.lookupSourceId).valid
|
||||||
|
io.lookup.bits := table(io.lookupSourceId).bits.respSourceId
|
||||||
|
val lookupFire = io.lookup.ready && io.lookup.valid
|
||||||
|
// Dequeue as soon as lookup succeeds
|
||||||
|
when(lookupFire) {
|
||||||
|
table(io.lookupSourceId).valid := false.B
|
||||||
|
}
|
||||||
|
|
||||||
dontTouch(io.lookup)
|
dontTouch(io.lookup)
|
||||||
dontTouch(matchIndex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InflightCoalReqTableEntry(
|
class InflightCoalReqTableEntry(
|
||||||
@@ -308,6 +274,7 @@ class InflightCoalReqTableEntry(
|
|||||||
val offsetBits: Int,
|
val offsetBits: Int,
|
||||||
val sizeBits: Int
|
val sizeBits: Int
|
||||||
) extends Bundle {
|
) extends Bundle {
|
||||||
|
// srcId will be positionally encoded in a lane
|
||||||
class PerLaneRequest extends Bundle {
|
class PerLaneRequest extends Bundle {
|
||||||
val offset = UInt(offsetBits.W)
|
val offset = UInt(offsetBits.W)
|
||||||
val size = UInt(sizeBits.W)
|
val size = UInt(sizeBits.W)
|
||||||
|
|||||||
Reference in New Issue
Block a user