Skip to content

Commit 655a23e

Browse files
committed
Merge branch 'scoreboard-multiwarp'
2 parents b1167ee + 29fb771 commit 655a23e

3 files changed

Lines changed: 263 additions & 230 deletions

File tree

src/main/scala/radiance/muon/Backend.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,25 @@ class Backend(
3333
hazard.io.ibuf <> io.ibuf
3434

3535
val scoreboard = Module(new Scoreboard)
36-
scoreboard.io.hazard <> hazard.io.scb
36+
scoreboard.io.hazard(0) <> hazard.io.scb
37+
scoreboard.io.hazard.zipWithIndex.foreach { case (scb, wid) =>
38+
wid match {
39+
case 0 => scb <> hazard.io.scb
40+
case _ => {
41+
scb.updateRS.enable := false.B
42+
scb.updateRS.write := 0.U.asTypeOf(new ScoreboardRegUpdate)
43+
scb.updateRS.reads.foreach(_ := 0.U.asTypeOf(new ScoreboardRegUpdate))
44+
scb.readRs1.enable := false.B
45+
scb.readRs1.pReg := 0.U
46+
scb.readRs2.enable := false.B
47+
scb.readRs2.pReg := 0.U
48+
scb.readRs3.enable := false.B
49+
scb.readRs3.pReg := 0.U
50+
scb.readRd.enable := false.B
51+
scb.readRd.pReg := 0.U
52+
}
53+
}
54+
}
3755
dontTouch(scoreboard.io)
3856

3957
val reservStation = Module(new ReservationStation)

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

Lines changed: 114 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ class ScoreboardRead(
3131
}
3232

3333
class ScoreboardHazardIO(implicit p: Parameters) extends CoreBundle()(p) {
34-
/** scoreboard pending-read/write increments on RS admission */
3534
val updateRS = new ScoreboardUpdate
36-
/** scoreboard accesses on RS admission */
3735
val readRs1 = new ScoreboardRead(scoreboardReadCountBits, scoreboardWriteCountBits)
3836
val readRs2 = new ScoreboardRead(scoreboardReadCountBits, scoreboardWriteCountBits)
3937
val readRs3 = new ScoreboardRead(scoreboardReadCountBits, scoreboardWriteCountBits)
@@ -53,19 +51,20 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
5351
val updateColl = new ScoreboardUpdate
5452
/** scoreboard pending-write decrements on writeback */
5553
val updateWB = new ScoreboardUpdate
56-
/** scoreboard accesses used for RS admission from the Hazard stage */
57-
val hazard = new ScoreboardHazardIO
54+
/** scoreboard accesses/updates on RS admission from the Hazard stage.
55+
* These are per-warp ports. */
56+
val hazard = Vec(numWarps, new ScoreboardHazardIO)
5857
})
5958

6059
class Entry extends Bundle {
61-
val pendingReads = chiselTypeOf(io.hazard.readRd.pendingReads)
62-
val pendingWrites = chiselTypeOf(io.hazard.readRd.pendingWrites)
60+
val pendingReads = chiselTypeOf(io.hazard.head.readRd.pendingReads)
61+
val pendingWrites = chiselTypeOf(io.hazard.head.readRd.pendingWrites)
6362
// TODO: reads epoch
6463
}
6564

6665
// flip-flops
67-
val readTable = Mem(muonParams.numPhysRegs, chiselTypeOf(io.hazard.readRd.pendingReads))
68-
val writeTable = Mem(muonParams.numPhysRegs, chiselTypeOf(io.hazard.readRd.pendingWrites))
66+
val readTable = Mem(muonParams.numPhysRegs, chiselTypeOf(io.hazard.head.readRd.pendingReads))
67+
val writeTable = Mem(muonParams.numPhysRegs, chiselTypeOf(io.hazard.head.readRd.pendingWrites))
6968

7069
// reset
7170
// @synthesis: unsure if this will generate expensive trees, revisit
@@ -94,6 +93,7 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
9493
// if rs1/rs2/rs3 points to the same reg, bump the counters by the number
9594
// of duplicates. This keeps coalescing out of consideration when designing
9695
// the collector.
96+
// note the final Seq is always length 3 (maxNumRegs).
9797
def consolidateUpdates(updates: Seq[ScoreboardRegUpdate]): Seq[ConsolidatedRegUpdate] = {
9898
// check if reg # is unique with prefix Sum
9999
val matchCount = updates.zipWithIndex.map { case (self, i) =>
@@ -104,7 +104,7 @@ class Scoreboard(implicit p: Parameters) extends CoreModule()(p) {
104104
}.fold(0.U)(_ +& _)
105105
}
106106

107-
// coalesce total incr/decrs to the same reg with prefix sum
107+
// coalesce total incr/decrs to the same reg with prefix-sum
108108
val coalescedIncDec = updates.zipWithIndex.map { case (self, i) =>
109109
// forward prefix sum
110110
(i until updates.length).map { j =>
@@ -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,117 +225,116 @@ 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)
223229
}
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+
}
242+
}
243+
244+
(prevRecords ++ newRecords, success)
245+
}
224246

225-
(newRecords, success)
247+
def commitUpdate(recs: Seq[UpdateRecord], isWrite: Boolean) = {
248+
recs.foreach { r =>
249+
when (r.dirty) {
250+
assert(r.pReg =/= 0.U, "update to x0 not filtered in the logic?")
251+
if (isWrite) {
252+
printf(cf"scoreboard: committed write (pReg:${r.pReg}, new pendingWrites:${r.counter})\n")
253+
writeTable(r.pReg) := r.counter
254+
} else {
255+
printf(cf"scoreboard: committed read (pReg:${r.pReg}, new pendingReads:${r.counter})\n")
256+
readTable(r.pReg) := r.counter
257+
}
258+
}
259+
}
226260
}
227261

228-
// collector and writeback updates
262+
// collector and writeback updates. These are global across-warp
263+
//
229264
val uniqCollReadUpdates = consolidateUpdates(io.updateColl.reads)
230265
val uniqWBWriteUpdates = consolidateUpdates(Seq(io.updateWB.write))
231-
val (collReadRecs, collSuccess) = applyUpdates(Seq(), uniqCollReadUpdates, isWrite = false, debug = "coll")
232-
val (wbWriteRecs, wbSuccess) = applyUpdates(Seq(), uniqWBWriteUpdates, isWrite = true, debug = "wb")
266+
val (collRecs, collSuccess) = applyUpdates(Seq(), uniqCollReadUpdates, isWrite = false, debug = "coll")
267+
val (wbRecs, wbSuccess) = applyUpdates(Seq(), uniqWBWriteUpdates, isWrite = true, debug = "wb")
233268
// assert(collSuccess && wbSuccess, "scoreboard: collector / WB update must always succeed!")
234269

235-
// RS admission updates
236-
val uniqRSReadUpdates = consolidateUpdates(io.hazard.updateRS.reads)
237-
val uniqRSWriteUpdates = consolidateUpdates(Seq(io.hazard.updateRS.write))
238-
val (rsReadRecs, rsReadSuccess) = applyUpdates(collReadRecs, uniqRSReadUpdates, isWrite = false, debug = "rsRead")
239-
val (rsWriteRecs, rsWriteSuccess) = applyUpdates(wbWriteRecs, uniqRSWriteUpdates, isWrite = true, debug = "rsWrite")
240-
val rsSuccess = WireDefault(rsReadSuccess && rsWriteSuccess)
241-
dontTouch(rsSuccess)
242-
243-
io.hazard.updateRS.success := io.hazard.updateRS.enable && rsSuccess
244270
io.updateWB.success := wbSuccess
245271
io.updateColl.success := collSuccess
246272

247-
when (io.hazard.updateRS.enable || io.updateWB.enable || io.updateColl.enable) {
248-
when (io.hazard.updateRS.enable) {
249-
printf(cf"scoreboard: received RS update ")
250-
printUpdate(io.hazard.updateRS)
251-
}
252-
when (io.updateWB.enable) {
253-
printf(cf"scoreboard: received WB update ")
254-
printUpdate(io.updateWB)
255-
}
256-
when (io.updateColl.enable) {
257-
printf(cf"scoreboard: received collector update ")
258-
printUpdate(io.updateColl)
259-
}
260-
261-
def commitUpdate(recs: Seq[UpdateRecord], isWrite: Boolean) = {
262-
// need to reflect the latest index in the seq
263-
// TODO: refactor; the logic is largely similar to consolidateUpdates
264-
val syncRecs = recs.zipWithIndex.map { case (r, i) =>
265-
val count = WireDefault(r.counter)
266-
val dirty = WireDefault(r.dirty)
267-
for (j <- i + 1 until recs.length) {
268-
when (recs(j).dirty && recs(j).pReg === r.pReg) {
269-
// prefix-sum overwrite; relies on these orders being preserved in
270-
// the elaborated verilog
271-
count := recs(j).counter
272-
dirty := recs(j).dirty
273-
}
274-
}
275-
UpdateRecord(dirty, r.pReg, count, width = r.counter.getWidth)
276-
}
273+
when (io.updateWB.enable) {
274+
printf(cf"scoreboard: received WB update ")
275+
printUpdate(io.updateWB)
276+
}
277+
when (io.updateColl.enable) {
278+
printf(cf"scoreboard: received collector update ")
279+
printUpdate(io.updateColl)
280+
}
277281

278-
syncRecs.foreach { r =>
279-
when (r.dirty) {
280-
assert(r.pReg =/= 0.U, "update to x0 not filtered in the logic?")
281-
if (isWrite) {
282-
printf(cf"scoreboard: committed write (pReg:${r.pReg}, new pendingWrites:${r.counter})\n")
283-
writeTable(r.pReg) := r.counter
284-
} else {
285-
printf(cf"scoreboard: committed read (pReg:${r.pReg}, new pendingReads:${r.counter})\n")
286-
readTable(r.pReg) := r.counter
287-
}
288-
}
289-
}
282+
// RS admit updates. These are per-warp
283+
//
284+
def perWarp(warpIo: ScoreboardHazardIO, warpId: Int) = {
285+
// RS admission updates
286+
val uniqRSReadUpdates = consolidateUpdates(warpIo.updateRS.reads)
287+
val uniqRSWriteUpdates = consolidateUpdates(Seq(warpIo.updateRS.write))
288+
val (rsReadRecs, rsReadSuccess) = applyUpdates(collRecs, uniqRSReadUpdates, isWrite = false, debug = "rsRead")
289+
val (rsWriteRecs, rsWriteSuccess) = applyUpdates(wbRecs, uniqRSWriteUpdates, isWrite = true, debug = "rsWrite")
290+
val rsSuccess = WireDefault(rsReadSuccess && rsWriteSuccess)
291+
dontTouch(rsSuccess)
292+
293+
warpIo.updateRS.success := warpIo.updateRS.enable && rsSuccess
294+
295+
when (warpIo.updateRS.enable) {
296+
printf(cf"scoreboard: received RS update (warp=${warpId}) ")
297+
printUpdate(warpIo.updateRS)
290298
}
291299

292-
// conditionally apply RS updates on success
293-
// make sure this happens later than coll/WB!
294-
when (rsSuccess) {
295-
commitUpdate(collReadRecs ++ rsReadRecs, isWrite = false)
296-
commitUpdate(wbWriteRecs ++ rsWriteRecs, isWrite = true)
297-
}.otherwise {
298-
commitUpdate(collReadRecs, isWrite = false)
299-
commitUpdate(wbWriteRecs, isWrite = true)
300+
when (warpIo.updateRS.enable || io.updateWB.enable || io.updateColl.enable) {
301+
commitUpdate(rsReadRecs, isWrite = false)
302+
commitUpdate(rsWriteRecs, isWrite = true)
300303

301304
when (!rsReadSuccess) {
302-
printf(cf"scoreboard: failed to commit RS update due to read overflow: ")
303-
printUpdate(io.hazard.updateRS)
305+
printf(cf"scoreboard: warp=${warpId}: failed to commit RS update due to read overflow: ")
306+
printUpdate(warpIo.updateRS)
304307
}.elsewhen (!rsWriteSuccess) {
305-
printf(cf"scoreboard: failed to commit RS update due to write overflow: ")
306-
printUpdate(io.hazard.updateRS)
308+
printf(cf"scoreboard: warp=${warpId}: failed to commit RS update due to write overflow: ")
309+
printUpdate(warpIo.updateRS)
307310
}
308-
}
309311

310-
printf("scoreboard: table update, content beforehand:\n")
311-
printTable
312-
}
312+
printf(cf"scoreboard: warp=${warpId}: table update, content beforehand:\n")
313+
printTable
314+
}
313315

314-
// read
315-
// ----
316-
//
317-
def read(port: ScoreboardRead) = {
318-
port.pendingReads := 0.U
319-
port.pendingWrites := 0.U
320-
when (port.enable) {
321-
// using lookup here enables bypassing same-cycle updates to reads to the
322-
// same pReg
323-
// NOTE: don't use rsReadRecs/rsWriteRecs here, otherwise results in a
324-
// combinational cycle with the RS admission logic in the Hazard module
325-
port.pendingReads := lookup(collReadRecs, port.pReg, isWrite = false)._1
326-
port.pendingWrites := lookup(wbWriteRecs, port.pReg, isWrite = true)._1
316+
// read
317+
// ----
318+
//
319+
def read(port: ScoreboardRead) = {
320+
port.pendingReads := 0.U
321+
port.pendingWrites := 0.U
322+
when (port.enable) {
323+
// using lookup here enables bypassing same-cycle updates to reads to the
324+
// same pReg
325+
// NOTE: don't use rsReadRecs/rsWriteRecs here, otherwise results in a
326+
// combinational cycle with the RS admission logic in the Hazard module
327+
port.pendingReads := lookup(collRecs, port.pReg, isWrite = false)._2._1
328+
port.pendingWrites := lookup(wbRecs, port.pReg, isWrite = true)._2._1
329+
}
327330
}
331+
read(warpIo.readRs1)
332+
read(warpIo.readRs2)
333+
read(warpIo.readRs3)
334+
read(warpIo.readRd)
328335
}
329-
read(io.hazard.readRs1)
330-
read(io.hazard.readRs2)
331-
read(io.hazard.readRs3)
332-
read(io.hazard.readRd)
336+
337+
io.hazard.zipWithIndex.foreach { case (io, wid) => perWarp(io, wid) }
333338

334339
def printUpdate(upd: ScoreboardUpdate) = {
335340
def printReg(reg: ScoreboardRegUpdate) = {

0 commit comments

Comments
 (0)