@@ -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