Skip to content

Commit 3e6a5f6

Browse files
authored
align RTL: tage window method, block resolveQ if update failed due to bank conflict. (#644)
* cpu-o3: test tage bank conflict to block resolveQ * cpu-o3: probe if can resolveUpdate, then update otherwise main BTB may do resolve udpate twice if tage resolveUpdate failed Change-Id: I660cb52b7f70a4202ca7741d8c7ed564fa7cd5b8 * cpu-o3: update tage unit test Change-Id: I44f3e6433d110d7625f4fbdb29df0e95d5ab9181
1 parent d67127e commit 3e6a5f6

File tree

9 files changed

+125
-56
lines changed

9 files changed

+125
-56
lines changed

src/cpu/o3/fetch.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,8 +1535,13 @@ Fetch::handleIEWSignals()
15351535
for (const auto resolvedInstPC : entry.resolvedInstPC) {
15361536
dbpbtb->markCFIResolved(stream_id, resolvedInstPC);
15371537
}
1538-
dbpbtb->resolveUpdate(stream_id);
1539-
resolveQueue.pop_front();
1538+
bool success = dbpbtb->resolveUpdate(stream_id);
1539+
if (success) {
1540+
dbpbtb->notifyResolveSuccess();
1541+
resolveQueue.pop_front();
1542+
} else {
1543+
dbpbtb->notifyResolveFailure();
1544+
}
15401545
}
15411546
}
15421547

src/cpu/pred/BranchPredictor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,3 +1171,4 @@ class DecoupledBPUWithBTB(BranchPredictor):
11711171
enableLoopBuffer = Param.Bool(False, "Enable loop buffer to supply inst for loops")
11721172
enableLoopPredictor = Param.Bool(False, "Use loop predictor to predict loop exit")
11731173
enableJumpAheadPredictor = Param.Bool(False, "Use jump ahead predictor to skip no-need-to-predict blocks")
1174+
resolveBlockThreshold = Param.Unsigned(8, "Consecutive resolve dequeue failures before blocking prediction once")

src/cpu/pred/btb/btb_tage.cc

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ tageStats(this, p.numPredictors, p.numBanks)
137137

138138
// initialize use_alt_on_na table
139139
useAlt.resize(useAltOnNaSize, 0);
140-
141140
#ifndef UNIT_TEST
142141
hasDB = true;
143142
switch (getDelay()) {
@@ -640,56 +639,58 @@ BTBTAGE::handleNewEntryAllocation(const Addr &startPC,
640639
}
641640

642641
/**
643-
* @brief Updates the TAGE predictor state based on actual branch execution results
644-
*
645-
* @param stream The fetch stream containing branch execution information
642+
* @brief Probe resolved update for bank conflicts without mutating state.
643+
* Returns false if the update cannot proceed due to a bank conflict.
646644
*/
647-
void
648-
BTBTAGE::update(const FetchStream &stream) {
645+
bool
646+
BTBTAGE::canResolveUpdate(const FetchStream &stream) {
649647
Addr startAddr = stream.getRealStartPC();
650648
unsigned updateBank = getBankId(startAddr);
651649

652-
DPRINTF(TAGE, "update startAddr: %#lx, bank: %u\n", startAddr, updateBank);
653-
654650
#ifndef UNIT_TEST
655-
// Record update access per bank
651+
// Record attempted update access per bank (even if it conflicts)
656652
tageStats.updateAccessPerBank[updateBank]++;
657653
#endif
658654

659-
// ========== Bank Conflict Detection ==========
660-
// Check if this update conflicts with the last prediction
661-
// Only perform conflict detection if enableBankConflict is true
662-
if (enableBankConflict) {
663-
// Conflict happens when:
664-
// 1. There was a valid prediction in this or previous tick (predBankValid)
665-
// 2. Update and prediction target the same bank (updateBank == lastPredBankId)
666-
//
667-
// Note: This assumes one update() call per tick (guaranteed by fetch stage)
668-
if (predBankValid && updateBank == lastPredBankId) {
669-
tageStats.updateBankConflict++;
670-
tageStats.updateDroppedDueToConflict++;
671-
655+
if (enableBankConflict && predBankValid && updateBank == lastPredBankId) {
656+
tageStats.updateBankConflict++;
657+
tageStats.updateDeferredDueToConflict++;
672658
#ifndef UNIT_TEST
673-
// Record conflict for this specific bank
674-
tageStats.updateBankConflictPerBank[updateBank]++;
659+
tageStats.updateBankConflictPerBank[updateBank]++;
675660
#endif
661+
DPRINTF(TAGE, "Bank conflict detected: update bank %u conflicts with prediction bank %u, "
662+
"deferring this update (will retry after blocking prediction)\n",
663+
updateBank, lastPredBankId);
664+
predBankValid = false;
665+
return false;
666+
}
676667

677-
DPRINTF(TAGE, "Bank conflict detected: update bank %u conflicts with "
678-
"prediction bank %u, dropping this update\n",
679-
updateBank, lastPredBankId);
680-
681-
// Clear prediction state after consuming it
682-
predBankValid = false;
683-
return; // Drop this update entirely
684-
}
668+
return true;
669+
}
685670

686-
// If no conflict, clear prediction state (prediction has been consumed)
687-
if (predBankValid) {
688-
DPRINTF(TAGE, "No bank conflict: update bank %u != prediction bank %u\n",
689-
updateBank, lastPredBankId);
690-
}
671+
/**
672+
* @brief Perform resolved update after probe success.
673+
*/
674+
void
675+
BTBTAGE::doResolveUpdate(const FetchStream &stream) {
676+
if (enableBankConflict && predBankValid) {
677+
// Prediction consumed; clear bank tag for next cycle
691678
predBankValid = false;
692679
}
680+
update(stream);
681+
}
682+
683+
/**
684+
* @brief Updates the TAGE predictor state based on actual branch execution results
685+
*
686+
* @param stream The fetch stream containing branch execution information
687+
*/
688+
void
689+
BTBTAGE::update(const FetchStream &stream) {
690+
Addr startAddr = stream.getRealStartPC();
691+
unsigned updateBank = getBankId(startAddr);
692+
693+
DPRINTF(TAGE, "update startAddr: %#lx, bank: %u\n", startAddr, updateBank);
693694

694695
// ========== Normal Update Logic ==========
695696
// Prepare BTB entries to update
@@ -1023,7 +1024,7 @@ BTBTAGE::TageStats::TageStats(statistics::Group* parent, int numPredictors, int
10231024
ADD_STAT(updateMispred, statistics::units::Count::get(), "mispred when update"),
10241025
ADD_STAT(updateResetU, statistics::units::Count::get(), "reset u when update"),
10251026
ADD_STAT(updateBankConflict, statistics::units::Count::get(), "number of bank conflicts detected"),
1026-
ADD_STAT(updateDroppedDueToConflict, statistics::units::Count::get(), "number of updates dropped due to bank conflict"),
1027+
ADD_STAT(updateDeferredDueToConflict, statistics::units::Count::get(), "number of updates deferred due to bank conflict (retried later)"),
10271028
ADD_STAT(updateBankConflictPerBank, statistics::units::Count::get(), "bank conflicts per bank"),
10281029
ADD_STAT(updateAccessPerBank, statistics::units::Count::get(), "update accesses per bank"),
10291030
ADD_STAT(predAccessPerBank, statistics::units::Count::get(), "prediction accesses per bank"),

src/cpu/pred/btb/btb_tage.hh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ class BTBTAGE : public TimedBaseBTBPredictor
150150

151151
// Update predictor state based on actual branch outcomes
152152
void update(const FetchStream &entry) override;
153+
bool canResolveUpdate(const FetchStream &entry) override;
154+
void doResolveUpdate(const FetchStream &entry) override;
153155

154156
#ifndef UNIT_TEST
155157
void commitBranch(const FetchStream &stream, const DynInstPtr &inst) override;
@@ -349,7 +351,7 @@ class BTBTAGE : public TimedBaseBTBPredictor
349351

350352
// Bank conflict statistics
351353
Scalar updateBankConflict; // Number of bank conflicts detected
352-
Scalar updateDroppedDueToConflict; // Number of updates dropped due to bank conflict
354+
Scalar updateDeferredDueToConflict; // Number of updates deferred due to bank conflict (retried later)
353355

354356
#ifndef UNIT_TEST
355357
// Fine-grained per-bank statistics

src/cpu/pred/btb/decoupled_bpred.cc

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ DecoupledBPUWithBTB::DecoupledBPUWithBTB(const DecoupledBPUWithBTBParams &p)
4444
bpDBSwitches(p.bpDBSwitches),
4545
numStages(p.numStages),
4646
historyManager(16), // TODO: fix this
47+
resolveBlockThreshold(p.resolveBlockThreshold),
4748
dbpBtbStats(this, p.numStages, p.fsq_size, maxInstsNum)
4849
{
4950
if (bpDBSwitches.size() > 0) {
@@ -139,8 +140,14 @@ DecoupledBPUWithBTB::tick()
139140

140141
// 1. Request new prediction if FSQ not full and we are idle
141142
if (bpuState == BpuState::IDLE && !streamQueueFull()) {
142-
requestNewPrediction();
143-
bpuState = BpuState::PREDICTOR_DONE;
143+
if (blockPredictionPending) {
144+
DPRINTF(Override, "Prediction blocked to prioritize resolve update\n");
145+
dbpBtbStats.predictionBlockedForUpdate++;
146+
blockPredictionPending = false;
147+
} else {
148+
requestNewPrediction();
149+
bpuState = BpuState::PREDICTOR_DONE;
150+
}
144151
}
145152

146153
// 2. Handle pending prediction if available
@@ -620,26 +627,61 @@ DecoupledBPUWithBTB::update(unsigned stream_id, ThreadID tid)
620627
historyManager.commit(stream_id);
621628
}
622629

623-
void
630+
bool
624631
DecoupledBPUWithBTB::resolveUpdate(unsigned &stream_id)
625632
{
626633
auto stream_it = fetchStreamQueue.find(stream_id);
627634
if (stream_it == fetchStreamQueue.end()) {
628635
DPRINTF(DecoupleBP, "Stream id %u not found in fetchStreamQueue, cannot update predictors\n", stream_id);
629-
return;
636+
return true;
630637
}
631638

632639
auto &stream = stream_it->second;
633640

634641
// Update predictor components only if the stream is hit or taken
635-
if (stream.isHit || stream.exeTaken) {
636-
// Update predictor components
637-
for (int i = 0; i < numComponents; ++i) {
638-
if (components[i]->getResolvedUpdate()) {
639-
components[i]->update(stream);
642+
if (!(stream.isHit || stream.exeTaken)) {
643+
return true;
644+
}
645+
646+
// Phase 1: probe all resolved-update components to ensure no blocker
647+
for (int i = 0; i < numComponents; ++i) {
648+
if (components[i]->getResolvedUpdate()) {
649+
if (!components[i]->canResolveUpdate(stream)) {
650+
return false;
640651
}
641652
}
642653
}
654+
655+
// Phase 2: all clear, perform updates once
656+
for (int i = 0; i < numComponents; ++i) {
657+
if (components[i]->getResolvedUpdate()) {
658+
components[i]->doResolveUpdate(stream);
659+
}
660+
}
661+
662+
return true;
663+
}
664+
665+
void
666+
DecoupledBPUWithBTB::notifyResolveSuccess()
667+
{
668+
resolveDequeueFailCounter = 0;
669+
}
670+
671+
void
672+
DecoupledBPUWithBTB::notifyResolveFailure()
673+
{
674+
resolveDequeueFailCounter++;
675+
if (resolveDequeueFailCounter >= resolveBlockThreshold) {
676+
blockPredictionOnce();
677+
resolveDequeueFailCounter = 0;
678+
}
679+
}
680+
681+
void
682+
DecoupledBPUWithBTB::blockPredictionOnce()
683+
{
684+
blockPredictionPending = true;
643685
}
644686

645687
void

src/cpu/pred/btb/decoupled_bpred.hh

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ class DecoupledBPUWithBTB : public BPredUnit
165165
bool squashing{false};
166166

167167
HistoryManager historyManager;
168+
bool blockPredictionPending{false};
169+
unsigned resolveDequeueFailCounter{0};
170+
const unsigned resolveBlockThreshold;
168171

169172
unsigned numOverrideBubbles{0};
170173

@@ -362,6 +365,9 @@ class DecoupledBPUWithBTB : public BPredUnit
362365
statistics::Scalar predFalseHit;
363366
statistics::Scalar commitFalseHit;
364367

368+
// Window blocking statistics
369+
statistics::Scalar predictionBlockedForUpdate; // Times prediction was blocked for update priority
370+
365371
DBPBTBStats(statistics::Group* parent, unsigned numStages, unsigned fsqSize, unsigned maxInstsNum);
366372
} dbpBtbStats;
367373

@@ -815,11 +821,14 @@ class DecoupledBPUWithBTB : public BPredUnit
815821
void resetPC(Addr new_pc);
816822

817823
// Helper functions for update
818-
void resolveUpdate(unsigned &stream_id);
824+
bool resolveUpdate(unsigned &stream_id);
819825
void prepareResolveUpdateEntries(unsigned &stream_id);
820826
void markCFIResolved(unsigned &stream_id, uint64_t resolvedInstPC);
821827
void updatePredictorComponents(FetchStream &stream);
822828
void updateStatistics(const FetchStream &stream);
829+
void notifyResolveSuccess();
830+
void notifyResolveFailure();
831+
void blockPredictionOnce();
823832

824833
// Helper function to process FTQ entry completion
825834
void processFetchTargetCompletion(const FtqEntry &target_to_fetch);

src/cpu/pred/btb/decoupled_bpred_stats.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,8 @@ DecoupledBPUWithBTB::DBPBTBStats::DBPBTBStats(
496496
ADD_STAT(btbEntriesWithDifferentStart, statistics::units::Count::get(), "number of btb entries with different start PC"),
497497
ADD_STAT(btbEntriesWithOnlyOneJump, statistics::units::Count::get(), "number of btb entries with different start PC starting with a jump"),
498498
ADD_STAT(predFalseHit, statistics::units::Count::get(), "false hit detected at pred"),
499-
ADD_STAT(commitFalseHit, statistics::units::Count::get(), "false hit detected at commit")
499+
ADD_STAT(commitFalseHit, statistics::units::Count::get(), "false hit detected at commit"),
500+
ADD_STAT(predictionBlockedForUpdate, statistics::units::Count::get(), "prediction blocked for update priority")
500501
{
501502
predsOfEachStage.init(numStages);
502503
commitPredsFromEachStage.init(numStages+1);

src/cpu/pred/btb/test/btb_tage.test.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -901,10 +901,11 @@ TEST_F(BTBTAGETest, BankConflict) {
901901
setupTageEntry(bankTage, 0xa0, 0, 1, false);
902902

903903
uint64_t conflicts_before = bankTage->tageStats.updateBankConflict;
904-
bankTage->update(stream);
904+
bool can_update = bankTage->canResolveUpdate(stream);
905905

906-
// Should detect conflict and drop update
906+
// Should detect conflict and defer update
907907
EXPECT_EQ(bankTage->tageStats.updateBankConflict, conflicts_before + 1);
908+
EXPECT_FALSE(can_update);
908909
EXPECT_FALSE(bankTage->predBankValid);
909910
}
910911

@@ -918,7 +919,9 @@ TEST_F(BTBTAGETest, BankConflict) {
918919
FetchStream stream = createStream(0x104, createBTBEntry(0x104), true, meta);
919920

920921
uint64_t conflicts_before = bankTage->tageStats.updateBankConflict;
921-
bankTage->update(stream);
922+
bool can_update = bankTage->canResolveUpdate(stream);
923+
ASSERT_TRUE(can_update);
924+
bankTage->doResolveUpdate(stream);
922925

923926
// Should not detect conflict
924927
EXPECT_EQ(bankTage->tageStats.updateBankConflict, conflicts_before);
@@ -936,7 +939,9 @@ TEST_F(BTBTAGETest, BankConflict) {
936939
setupTageEntry(bankTage, 0xa0, 0, 1, false);
937940

938941
uint64_t conflicts_before = bankTage->tageStats.updateBankConflict;
939-
bankTage->update(stream);
942+
bool can_update = bankTage->canResolveUpdate(stream);
943+
ASSERT_TRUE(can_update);
944+
bankTage->doResolveUpdate(stream);
940945

941946
// No conflict even with same bank
942947
EXPECT_EQ(bankTage->tageStats.updateBankConflict, conflicts_before);

src/cpu/pred/btb/timed_base_pred.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ class TimedBaseBTBPredictor: public SimObject
7575
virtual void update(const FetchStream &entry) {}
7676
virtual unsigned getDelay() {return numDelay;}
7777
virtual bool getResolvedUpdate() {return resolvedUpdate;}
78+
// Two-phase resolved update: probe first, then apply
79+
virtual bool canResolveUpdate(const FetchStream &entry) { return true; }
80+
virtual void doResolveUpdate(const FetchStream &entry) { update(entry); }
7881
#ifndef UNIT_TEST
7982
// do some statistics on a per-branch and per-predictor basis
8083
virtual void commitBranch(const FetchStream &entry, const DynInstPtr &inst) {}

0 commit comments

Comments
 (0)