@@ -31,9 +31,7 @@ class ScoreboardRead(
3131}
3232
3333class 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