Fix misleading maxSize param in Req/RespQueueEntry

This commit is contained in:
Hansung Kim
2023-05-10 19:55:01 -07:00
parent 4f31cad513
commit 300eff4f9a
2 changed files with 43 additions and 31 deletions

View File

@@ -53,7 +53,6 @@ case class CoalescerConfig(
// the coalescer to perform well // the coalescer to perform well
// watermark = 2, // minimum buffer occupancy to start coalescing // watermark = 2, // minimum buffer occupancy to start coalescing
wordSizeInBytes: Int, // 32-bit system wordSizeInBytes: Int, // 32-bit system
wordWidth: Int, // log(WORD_SIZE)
numOldSrcIds: Int, // num of outstanding requests per lane, from processor numOldSrcIds: Int, // num of outstanding requests per lane, from processor
numNewSrcIds: Int, // num of outstanding coalesced requests numNewSrcIds: Int, // num of outstanding coalesced requests
respQueueDepth: Int, // depth of the response fifo queues respQueueDepth: Int, // depth of the response fifo queues
@@ -67,6 +66,12 @@ case class CoalescerConfig(
) { ) {
// maximum coalesced size // maximum coalesced size
def maxCoalLogSize: Int = coalLogSizes.max def maxCoalLogSize: Int = coalLogSizes.max
def wordSizeWidth: Int = {
val w = log2Ceil(wordSizeInBytes)
require(wordSizeInBytes == 1 << w,
s"wordSizeInBytes (${wordSizeInBytes}) is not power of two")
w
}
} }
@@ -79,7 +84,6 @@ object defaultConfig extends CoalescerConfig(
dataBusWidth = 3, // 2^3=8 bytes, 64 bit bus dataBusWidth = 3, // 2^3=8 bytes, 64 bit bus
// watermark = 2, // watermark = 2,
wordSizeInBytes = 4, wordSizeInBytes = 4,
wordWidth = 2,
// when attaching to SoC, 16 source IDs are not enough due to longer latency // when attaching to SoC, 16 source IDs are not enough due to longer latency
numOldSrcIds = 16, numOldSrcIds = 16,
numNewSrcIds = 4, numNewSrcIds = 4,
@@ -131,13 +135,14 @@ class CoalescingUnit(config: CoalescerConfig)(implicit p: Parameters) extends La
lazy val module = new CoalescingUnitImp(this, config) lazy val module = new CoalescingUnitImp(this, config)
} }
class ReqQueueEntry(sourceWidth: Int, sizeWidth: Int, addressWidth: Int, maxSize: Int) extends Bundle { class ReqQueueEntry(sourceWidth: Int, sizeWidth: Int, addressWidth: Int, dataWidth: Int) extends Bundle {
require(dataWidth % 8 == 0, s"dataWidth (${dataWidth} bits) is not multiple of 8")
val op = UInt(1.W) // 0=READ 1=WRITE val op = UInt(1.W) // 0=READ 1=WRITE
val address = UInt(addressWidth.W) val address = UInt(addressWidth.W)
val size = UInt(sizeWidth.W) val size = UInt(sizeWidth.W)
val source = UInt(sourceWidth.W) val source = UInt(sourceWidth.W)
val mask = UInt((1 << maxSize).W) // write only val mask = UInt((dataWidth / 8).W) // write only
val data = UInt((8 * (1 << maxSize)).W) // write only val data = UInt(dataWidth.W) // write only
def toTLA(edgeOut: TLEdgeOut): TLBundleA = { def toTLA(edgeOut: TLEdgeOut): TLBundleA = {
val (plegal, pbits) = edgeOut.Put( val (plegal, pbits) = edgeOut.Put(
@@ -158,11 +163,11 @@ class ReqQueueEntry(sourceWidth: Int, sizeWidth: Int, addressWidth: Int, maxSize
} }
} }
class RespQueueEntry(sourceWidth: Int, sizeWidth: Int, maxSize: Int) extends Bundle { class RespQueueEntry(sourceWidth: Int, sizeWidth: Int, dataWidth: Int) extends Bundle {
val op = UInt(1.W) // 0=READ 1=WRITE val op = UInt(1.W) // 0=READ 1=WRITE
val size = UInt(sizeWidth.W) val size = UInt(sizeWidth.W)
val source = UInt(sourceWidth.W) val source = UInt(sourceWidth.W)
val data = UInt((8 * (1 << maxSize)).W) // read only val data = UInt(dataWidth.W) // read only
val error = Bool() val error = Bool()
def toTLD(edgeIn: TLEdgeIn): TLBundleD = { def toTLD(edgeIn: TLEdgeIn): TLBundleD = {
@@ -254,6 +259,11 @@ class CoalShiftQueue[T <: Data](gen: T, entries: Int, config: CoalescerConfig) e
val empty = Bool() val empty = Bool()
})) }))
// io.coalescable will first turn on for all coalescable chunks, and turn off
// incrementally as time goes on. Therefore, when io.coalescable is all
// turned off, that means we have processed all coalescable chunks at the
// current cycle.
//
// shift hint is when the heads have no more coalescable left this or next cycle // shift hint is when the heads have no more coalescable left this or next cycle
val shiftHint = !(io.coalescable zip io.invalidate.bits.map(_(0))).map { case (c, inv) => val shiftHint = !(io.coalescable zip io.invalidate.bits.map(_(0))).map { case (c, inv) =>
c && !(io.invalidate.valid && inv) c && !(io.invalidate.valid && inv)
@@ -341,7 +351,7 @@ class MonoCoalescer(coalLogSize: Int, windowT: CoalShiftQueue[ReqQueueEntry],
// number of entries matched with this leader lane's head. // number of entries matched with this leader lane's head.
// maximum is numLanes * queueDepth // maximum is numLanes * queueDepth
val matchCount = Output(UInt(log2Ceil(config.numLanes * config.queueDepth + 1).W)) val matchCount = Output(UInt(log2Ceil(config.numLanes * config.queueDepth + 1).W))
val coverageHits = Output(UInt((config.maxCoalLogSize - config.wordWidth + 1).W)) val coverageHits = Output(UInt((config.maxCoalLogSize - config.wordSizeWidth + 1).W))
val canCoalesce = Output(Vec(config.numLanes, Bool())) val canCoalesce = Output(Vec(config.numLanes, Bool()))
}) })
}) })
@@ -419,7 +429,7 @@ class MonoCoalescer(coalLogSize: Int, windowT: CoalShiftQueue[ReqQueueEntry],
val chosenMatchCount = VecInit(matchCounts)(chosenLeaderIdx) val chosenMatchCount = VecInit(matchCounts)(chosenLeaderIdx)
// coverage calculation // coverage calculation
def getOffsetSlice(addr: UInt) = addr(size - 1, config.wordWidth) def getOffsetSlice(addr: UInt) = addr(size - 1, config.wordSizeWidth)
// 2-D table flattened to 1-D // 2-D table flattened to 1-D
val offsets = io.window.elts.flatMap(_.map(req => getOffsetSlice(req.address))) val offsets = io.window.elts.flatMap(_.map(req => getOffsetSlice(req.address)))
val valids = chosenMatches.flatMap(_.asBools) val valids = chosenMatches.flatMap(_.asBools)
@@ -427,7 +437,7 @@ class MonoCoalescer(coalLogSize: Int, windowT: CoalShiftQueue[ReqQueueEntry],
// any of the requests in the queue. e.g. if [ 1 1 1 1 ], all of the four // any of the requests in the queue. e.g. if [ 1 1 1 1 ], all of the four
// words in the coalesced data coming back will be accessed by some request // words in the coalesced data coming back will be accessed by some request
// and we've reached 100% bandwidth utilization. // and we've reached 100% bandwidth utilization.
val hits = Seq.tabulate(1 << (size - config.wordWidth)) { target => val hits = Seq.tabulate(1 << (size - config.wordSizeWidth)) { target =>
(offsets zip valids).map { case (offset, valid) => valid && (offset === target.U) }.reduce(_ || _) (offsets zip valids).map { case (offset, valid) => valid && (offset === target.U) }.reduce(_ || _)
} }
@@ -499,7 +509,7 @@ class MultiCoalescer(windowT: CoalShiftQueue[ReqQueueEntry], coalReqT: ReqQueueE
val chosenSizeIdx = Wire(UInt(log2Ceil(config.coalLogSizes.size).W)) val chosenSizeIdx = Wire(UInt(log2Ceil(config.coalLogSizes.size).W))
val chosenValid = Wire(Bool()) val chosenValid = Wire(Bool())
// minimum 25% coverage // minimum 25% coverage
val minCoverage = 1.max(1 << ((config.maxCoalLogSize - config.wordWidth) - 2)) val minCoverage = 1.max(1 << ((config.maxCoalLogSize - config.wordSizeWidth) - 2))
when (normalizedHits.map(_ > minCoverage.U).reduce(_ || _)) { when (normalizedHits.map(_ > minCoverage.U).reduce(_ || _)) {
chosenSizeIdx := argMax(normalizedHits) chosenSizeIdx := argMax(normalizedHits)
@@ -531,13 +541,13 @@ class MultiCoalescer(windowT: CoalShiftQueue[ReqQueueEntry], coalReqT: ReqQueueE
val flatMatches = chosenBundle.matchOH.flatMap(_.asBools) val flatMatches = chosenBundle.matchOH.flatMap(_.asBools)
// check for word alignment in addresses // check for word alignment in addresses
assert(io.window.elts.flatMap(_.map(req => req.address(config.wordWidth - 1, 0) === 0.U)).zip( assert(io.window.elts.flatMap(_.map(req => req.address(config.wordSizeWidth - 1, 0) === 0.U)).zip(
io.window.mask.flatMap(_.asBools)).map { case (aligned, valid) => (!valid) || aligned }.reduce(_ || _), io.window.mask.flatMap(_.asBools)).map { case (aligned, valid) => (!valid) || aligned }.reduce(_ || _),
"one or more addresses used for coalescing is not word-aligned") "one or more addresses used for coalescing is not word-aligned")
// note: this is word-level coalescing. if finer granularity is needed, need to modify code // note: this is word-level coalescing. if finer granularity is needed, need to modify code
val numWords = (1.U << (chosenSize - config.wordWidth.U)).asUInt val numWords = (1.U << (chosenSize - config.wordSizeWidth.U)).asUInt
val maxWords = 1 << (config.maxCoalLogSize - config.wordWidth) val maxWords = 1 << (config.maxCoalLogSize - config.wordSizeWidth)
val addrMask = Wire(UInt(config.maxCoalLogSize.W)) val addrMask = Wire(UInt(config.maxCoalLogSize.W))
addrMask := (1.U << chosenSize).asUInt - 1.U addrMask := (1.U << chosenSize).asUInt - 1.U
@@ -548,7 +558,7 @@ class MultiCoalescer(windowT: CoalShiftQueue[ReqQueueEntry], coalReqT: ReqQueueE
val sel = flatReqs.zip(flatMatches).map { case (req, m) => val sel = flatReqs.zip(flatMatches).map { case (req, m) =>
// note: ANDing against addrMask is to conform to active byte lanes requirements // note: ANDing against addrMask is to conform to active byte lanes requirements
// if aligning to LSB suffices, we should add the bitwise AND back // if aligning to LSB suffices, we should add the bitwise AND back
m && ((req.address(config.maxCoalLogSize - 1, config.wordWidth)/* & addrMask*/) === i.U) m && ((req.address(config.maxCoalLogSize - 1, config.wordSizeWidth)/* & addrMask*/) === i.U)
} }
// TODO: SW uses priority encoder, not sure about behavior of MuxCase // TODO: SW uses priority encoder, not sure about behavior of MuxCase
data(i) := MuxCase(DontCare, flatReqs.zip(sel).map { case (req, s) => data(i) := MuxCase(DontCare, flatReqs.zip(sel).map { case (req, s) =>
@@ -602,11 +612,12 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig) extends
val sourceWidth = outer.cpuNode.in.head._1.params.sourceBits val sourceWidth = outer.cpuNode.in.head._1.params.sourceBits
// note we are using word size. assuming all coalescer inputs are word sized // note we are using word size. assuming all coalescer inputs are word sized
val reqQueueEntryT = new ReqQueueEntry(sourceWidth, config.wordWidth, config.addressWidth, config.wordSizeInBytes) val reqQueueEntryT = new ReqQueueEntry(sourceWidth, config.wordSizeWidth,
config.addressWidth, (config.wordSizeInBytes * 8))
val reqQueues = Module(new CoalShiftQueue(reqQueueEntryT, config.queueDepth, config)) val reqQueues = Module(new CoalShiftQueue(reqQueueEntryT, config.queueDepth, config))
val coalReqT = new ReqQueueEntry(log2Ceil(config.numNewSrcIds), log2Ceil(config.maxCoalLogSize), val coalReqT = new ReqQueueEntry(log2Ceil(config.numNewSrcIds), log2Ceil(config.maxCoalLogSize),
config.addressWidth, config.maxCoalLogSize) config.addressWidth, (1 << config.maxCoalLogSize) * 8)
val coalescer = Module(new MultiCoalescer(reqQueues, coalReqT, config)) val coalescer = Module(new MultiCoalescer(reqQueues, coalReqT, config))
coalescer.io.window := reqQueues.io coalescer.io.window := reqQueues.io
reqQueues.io.coalescable := coalescer.io.coalescable reqQueues.io.coalescable := coalescer.io.coalescable
@@ -692,7 +703,8 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig) extends
// coalesced request. Upper bound is min(DEPTH, 2**sourceWidth). // coalesced request. Upper bound is min(DEPTH, 2**sourceWidth).
val numPerLaneReqs = config.queueDepth val numPerLaneReqs = config.queueDepth
val respQueueEntryT = new RespQueueEntry(sourceWidth, log2Ceil(config.maxCoalLogSize), config.maxCoalLogSize) val respQueueEntryT = new RespQueueEntry(sourceWidth, log2Ceil(config.maxCoalLogSize),
(1 << config.maxCoalLogSize) * 8)
val respQueues = Seq.tabulate(config.numLanes) { _ => val respQueues = Seq.tabulate(config.numLanes) { _ =>
Module( Module(
new MultiPortQueue( new MultiPortQueue(
@@ -798,7 +810,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, config: CoalescerConfig) extends
} }
reqEntry.valid := (coalescer.io.invalidate.valid && inv) reqEntry.valid := (coalescer.io.invalidate.valid && inv)
reqEntry.source := req.source reqEntry.source := req.source
reqEntry.offset := ((req.address % (1 << config.maxCoalLogSize).U) >> config.wordWidth) reqEntry.offset := ((req.address % (1 << config.maxCoalLogSize).U) >> config.wordSizeWidth)
reqEntry.sizeEnum := config.sizeEnum.logSizeToEnum(req.size) reqEntry.sizeEnum := config.sizeEnum.logSizeToEnum(req.size)
// TODO: load/store op // TODO: load/store op
} }
@@ -879,7 +891,8 @@ class Uncoalescer(config: CoalescerConfig) extends Module {
Vec( Vec(
config.queueDepth, config.queueDepth,
ValidIO( ValidIO(
new RespQueueEntry(log2Ceil(config.numOldSrcIds), config.wordWidth, config.wordSizeInBytes) new RespQueueEntry(log2Ceil(config.numOldSrcIds), config.wordSizeWidth,
config.wordSizeInBytes * 8)
) )
) )
) )
@@ -959,7 +972,7 @@ class Uncoalescer(config: CoalescerConfig) extends Module {
// split the coalesced response back to individual per-lane responses with the // split the coalesced response back to individual per-lane responses with the
// right metadata. // right metadata.
class InflightCoalReqTable(config: CoalescerConfig) extends Module { class InflightCoalReqTable(config: CoalescerConfig) extends Module {
val offsetBits = config.maxCoalLogSize - config.wordWidth // assumes word offset val offsetBits = config.maxCoalLogSize - config.wordSizeWidth // assumes word offset
val entryT = new InflightCoalReqTableEntry( val entryT = new InflightCoalReqTableEntry(
config.numLanes, config.numLanes,
config.queueDepth, config.queueDepth,
@@ -1257,8 +1270,8 @@ class MemTraceDriverImp(outer: MemTraceDriver, config: CoalescerConfig, filename
dontTouch(tlOut.d) dontTouch(tlOut.d)
} }
// Give some slack time after trace EOF to the downstream system so that we // Give some slack time after trace EOF to the downstream system to make sure
// make sure to receive all outstanding responses. // we receive all (hopefully) outstanding responses back.
val finishCounter = RegInit(200.U(64.W)) val finishCounter = RegInit(200.U(64.W))
when(sim.io.trace_read.finished) { when(sim.io.trace_read.finished) {
finishCounter := finishCounter - 1.U finishCounter := finishCounter - 1.U
@@ -1319,6 +1332,7 @@ class MemTraceLogger(
reqEnable: Boolean = true, reqEnable: Boolean = true,
respEnable: Boolean = true, respEnable: Boolean = true,
// filename suffix that is unique to this logger module. // filename suffix that is unique to this logger module.
// This will be appended to the filename of the generated trace.
loggerName: String = ".logger" loggerName: String = ".logger"
)(implicit )(implicit
p: Parameters p: Parameters
@@ -1626,7 +1640,7 @@ class DummyDriverImp(outer: DummyDriver, config: CoalescerConfig)
// generate dummy traffic to coalescer to prevent it from being optimized // generate dummy traffic to coalescer to prevent it from being optimized
// out during synthesis // out during synthesis
val address = Wire(UInt(config.addressWidth.W)) val address = Wire(UInt(config.addressWidth.W))
address := Cat((finishCounter + (lane.U % 3.U)), 0.U(config.wordWidth.W)) address := Cat((finishCounter + (lane.U % 3.U)), 0.U(config.wordSizeWidth.W))
val (tl, edge) = node.out(0) val (tl, edge) = node.out(0)
val (legal, bits) = edge.Put( val (legal, bits) = edge.Put(
fromSource = sourceIdCounter, fromSource = sourceIdCounter,
@@ -1841,20 +1855,20 @@ class CoalescerXbar(config: CoalescerConfig) (implicit p: Parameters) extends La
val nonCoalEntryT = new ReqQueueEntry( val nonCoalEntryT = new ReqQueueEntry(
log2Ceil(config.numOldSrcIds), log2Ceil(config.numOldSrcIds),
config.wordWidth, config.wordSizeWidth,
config.addressWidth, config.addressWidth,
log2Ceil(config.wordSizeInBytes) config.wordSizeInBytes * 8
) )
val coalEntryT = new ReqQueueEntry( val coalEntryT = new ReqQueueEntry(
log2Ceil(config.numOldSrcIds), log2Ceil(config.numOldSrcIds),
log2Ceil(config.maxCoalLogSize), log2Ceil(config.maxCoalLogSize),
config.addressWidth, config.addressWidth,
config.maxCoalLogSize //already log 2 (1 << config.maxCoalLogSize) * 8
) )
val respNonCoalEntryT = new RespQueueEntry( val respNonCoalEntryT = new RespQueueEntry(
log2Ceil(config.numOldSrcIds), log2Ceil(config.numOldSrcIds),
config.wordWidth, config.wordSizeWidth,
log2Ceil(config.wordSizeInBytes) config.wordSizeInBytes * 8
) )
val respCoalBundleT = new CoalescedResponseBundle(config) val respCoalBundleT = new CoalescedResponseBundle(config)

View File

@@ -186,7 +186,6 @@ object testConfig extends CoalescerConfig(
dataBusWidth = 5, dataBusWidth = 5,
// watermark = 2, // watermark = 2,
wordSizeInBytes = 4, wordSizeInBytes = 4,
wordWidth = 2,
numOldSrcIds = 16, numOldSrcIds = 16,
numNewSrcIds = 4, numNewSrcIds = 4,
respQueueDepth = 4, respQueueDepth = 4,
@@ -716,7 +715,6 @@ object uncoalescerTestConfig extends CoalescerConfig(
dataBusWidth = 5, dataBusWidth = 5,
// watermark = 2, // watermark = 2,
wordSizeInBytes = 4, wordSizeInBytes = 4,
wordWidth = 2,
numOldSrcIds = 16, numOldSrcIds = 16,
numNewSrcIds = 4, numNewSrcIds = 4,
respQueueDepth = 4, respQueueDepth = 4,