Skip to content

Commit 29fb771

Browse files
committed
scoreboard: Refactor coll/WB -> RS re-write
Make invalidation of previous same-reg updates explicit, and ge rid of an expensive prefix-sum.
1 parent 3697ac6 commit 29fb771

1 file changed

Lines changed: 37 additions & 35 deletions

File tree

src/main/scala/radiance/muon/Scoreboard.scala

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -148,42 +148,48 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
148148

149149
// Look up the most recent counter values for a pReg, by not only reading
150150
// the flip-flop table, but also the given update records.
151+
// Also returns a bitvector that indicates which of the `records` was hit for
152+
// the lookup.
151153
def lookup(records: Seq[UpdateRecord], pReg: UInt, isWrite: Boolean) = {
152154
val table = (if (isWrite) writeTable else readTable)
155+
val hit = WireDefault(false.B)
153156
val value = Wire(UInt(table(0).getWidth.W))
154157
val dirty = WireDefault(false.B)
155158
value := table(pReg)
156159

157160
// priority-tree
158-
records.foreach { rec =>
159-
when (rec.pReg === pReg) {
161+
val hitvec = records.map { rec =>
162+
val hit = (rec.pReg === pReg)
163+
when (hit) {
160164
value := rec.counter
161165
dirty := rec.dirty
162166
}
167+
hit
163168
}
164169

165-
(value, dirty)
170+
(hitvec, (value, dirty))
166171
}
167172

168173
// updateWB/updateColl is decrement-only, and always succeeds (otherwise we
169174
// risk deadlock);
170175
// updateRS is increment-only, and may fail due to counter overflow.
171176
//
172-
// Apply updateWB/Coll first, and on the updated counter values, try
173-
// applying updateRS. If the latter fails, only commit the post-WB/Coll
174-
// values to the table. This requires generating updated counter values as
175-
// a separate stage into a set of Wires, and conditionally latching those values
176-
// to the Mem; that is what UpdateRecord is for.
177+
// Apply updateWB/Coll first, and on the updated counter values, try applying
178+
// updateRS. If the latter fails, only commit the post-WB/Coll values to the
179+
// table. This requires generating updated counter values as a separate stage
180+
// into a set of Wires, and conditionally latching those values to the Mem;
181+
// that is what UpdateRecord is for.
177182

178183
def applyUpdates(records: Seq[UpdateRecord], uniqUpdates: Seq[ConsolidatedRegUpdate], isWrite: Boolean, debug: String = ""):
179-
(Seq[UpdateRecord] /* new table */, Bool /* success */) = {
184+
(Seq[UpdateRecord] /* new updates */, Bool /* success */) = {
180185
val maxCount = (if (isWrite) {maxPendingWritesU} else {maxPendingReadsU})
181186
val countName = (if (isWrite) {"pendingWrites"} else {"pendingReads"})
182187
val success = WireDefault(true.B)
183188

184-
val newRecords = uniqUpdates.map { u =>
189+
val processUpdates = uniqUpdates.map { u =>
185190
val dirtied = WireDefault(false.B)
186-
val (currCount, prevDirty) = lookup(records, u.pReg, isWrite)
191+
// are we updating upon another same-cycle update?
192+
val (hitvec, (currCount, prevDirty)) = lookup(records, u.pReg, isWrite)
187193
val newCount = WireDefault(currCount)
188194

189195
// skip x0 updates
@@ -219,31 +225,27 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
219225
}
220226

221227
val dirty = prevDirty || dirtied
222-
UpdateRecord(dirty, u.pReg, newCount, width = currCount.getWidth)
228+
(UpdateRecord(dirty, u.pReg, newCount, width = currCount.getWidth), hitvec)
229+
}
230+
val newRecords = processUpdates.map(_._1)
231+
232+
// if each of the `records` got dirtied by the new updates, we need to
233+
// invalidate them so we don't do double-updates to the counter table
234+
val prevRecords: Seq[UpdateRecord] = if (records.isEmpty) {
235+
Seq()
236+
} else {
237+
val recordsHitvec = processUpdates.map(_._2).map(VecInit(_).asUInt).reduce(_ | _).asBools
238+
assert(recordsHitvec.length == records.length)
239+
(records zip recordsHitvec).map { case (rec, hit) =>
240+
UpdateRecord(!hit && rec.dirty, rec.pReg, rec.counter, width = rec.counter.getWidth)
241+
}
223242
}
224243

225-
(newRecords, success)
244+
(prevRecords ++ newRecords, success)
226245
}
227246

228247
def commitUpdate(recs: Seq[UpdateRecord], isWrite: Boolean) = {
229-
// need to reflect the latest index in the seq
230-
// TODO: refactor; the logic is largely similar to consolidateUpdates
231-
val syncRecs = recs.zipWithIndex.map { case (r, i) =>
232-
val count = WireDefault(r.counter)
233-
val dirty = WireDefault(r.dirty)
234-
// prefix-sum; overwrite self with right-most match
235-
// relies on its order being preserved in elaboration
236-
// NOTE: @perf: has high chance of being expensive for long recs!
237-
for (j <- i + 1 until recs.length) {
238-
when (recs(j).dirty && recs(j).pReg === r.pReg) {
239-
count := recs(j).counter
240-
dirty := recs(j).dirty
241-
}
242-
}
243-
UpdateRecord(dirty, r.pReg, count, width = r.counter.getWidth)
244-
}
245-
246-
syncRecs.foreach { r =>
248+
recs.foreach { r =>
247249
when (r.dirty) {
248250
assert(r.pReg =/= 0.U, "update to x0 not filtered in the logic?")
249251
if (isWrite) {
@@ -296,8 +298,8 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
296298
}
297299

298300
when (warpIo.updateRS.enable || io.updateWB.enable || io.updateColl.enable) {
299-
commitUpdate(collRecs ++ rsReadRecs, isWrite = false)
300-
commitUpdate(wbRecs ++ rsWriteRecs, isWrite = true)
301+
commitUpdate(rsReadRecs, isWrite = false)
302+
commitUpdate(rsWriteRecs, isWrite = true)
301303

302304
when (!rsReadSuccess) {
303305
printf(cf"scoreboard: warp=${warpId}: failed to commit RS update due to read overflow: ")
@@ -322,8 +324,8 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
322324
// same pReg
323325
// NOTE: don't use rsReadRecs/rsWriteRecs here, otherwise results in a
324326
// combinational cycle with the RS admission logic in the Hazard module
325-
port.pendingReads := lookup(collRecs, port.pReg, isWrite = false)._1
326-
port.pendingWrites := lookup(wbRecs, port.pReg, isWrite = true)._1
327+
port.pendingReads := lookup(collRecs, port.pReg, isWrite = false)._2._1
328+
port.pendingWrites := lookup(wbRecs, port.pReg, isWrite = true)._2._1
327329
}
328330
}
329331
read(warpIo.readRs1)

0 commit comments

Comments
 (0)