From 4550c9664a96d2cae8705d85992ea21b6a2424ce Mon Sep 17 00:00:00 2001
From: Phillip Assmann 
Date: Thu, 10 Apr 2025 11:15:27 +0000
Subject: [PATCH 1/3] cpu: fix bac memory leak (by @dhschall)
---
 src/mem/cache/prefetch/fdp.cc | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/mem/cache/prefetch/fdp.cc b/src/mem/cache/prefetch/fdp.cc
index 6eff00a450d..95176a65457 100644
--- a/src/mem/cache/prefetch/fdp.cc
+++ b/src/mem/cache/prefetch/fdp.cc
@@ -171,23 +171,25 @@ FetchDirectedPrefetcher::translationComplete(PrefetchRequest *pfr, bool failed)
     assert(cache != nullptr);
 
     if (failed) {
-        DPRINTF(HWPrefetch, "Translation of %#x failed\n", pfr->addr);
+        DPRINTF(HWPrefetch, "Translation of %#x failed\n", it->addr);
         stats.translationFail++;
     } else {
-        DPRINTF(HWPrefetch, "Translation of %#x succeeded\n", pfr->addr);
+        DPRINTF(HWPrefetch, "Translation of %#x succeeded\n", it->addr);
         stats.translationSuccess++;
-        it->createPkt(curTick() + latency);
-        stats.pfPacketsCreated++;
-
-        if (cacheSnoop && (cache->inCache(pfr->pkt->getAddr(), pfr->pkt->isSecure())
-                    || (cache->inMissQueue(pfr->pkt->getAddr(), pfr->pkt->isSecure())))) {
+        if (cacheSnoop && (cache->inCache(
+            it->req->getPaddr(), it->req->isSecure()
+        ) || (cache->inMissQueue(
+            it->req->getPaddr(), it->req->isSecure()
+        )))) {
             stats.pfInCache++;
             DPRINTF(HWPrefetch, "Drop Packet. In Cache / MSHR\n");
         } else {
-        
+            it->createPkt(curTick() + latency);
+            stats.pfPacketsCreated++;
+
             DPRINTF(HWPrefetch, "Addr: %#x Add packet to PFQ. pkt PA:%#x, "
-                    "PFQ sz:%i\n", pfr->addr, pfr->pkt->getAddr(), pfq.size());
-        
+                    "PFQ sz:%i\n", it->addr, it->pkt->getAddr(), pfq.size());
+
             stats.pfCandidatesAdded++;
             pfq.push_back(*it);
         }
From 39a8609f14749e129a8549462ad6f99433441174 Mon Sep 17 00:00:00 2001
From: Phillip Assmann 
Date: Thu, 10 Apr 2025 11:24:42 +0000
Subject: [PATCH 2/3] cpu: add bac branch predictor delay
---
 src/cpu/o3/BaseO3CPU.py |  1 +
 src/cpu/o3/bac.cc       | 15 +++++++++++++++
 src/cpu/o3/bac.hh       |  6 ++++++
 3 files changed, 22 insertions(+)
diff --git a/src/cpu/o3/BaseO3CPU.py b/src/cpu/o3/BaseO3CPU.py
index a6906c16437..e1cc8358854 100644
--- a/src/cpu/o3/BaseO3CPU.py
+++ b/src/cpu/o3/BaseO3CPU.py
@@ -106,6 +106,7 @@ def support_take_over(cls):
 
     # Forward pipeline delays
     bacToFetchDelay = Param.Cycles(1, "Branch address calc. to fetch delay")
+    bacBranchPredictDelay = Param.Cycles(0, "BAC Branch Predictor delay")
     fetchToDecodeDelay = Param.Cycles(1, "Fetch to decode delay")
     decodeWidth = Param.Unsigned(8, "Decode width")
 
diff --git a/src/cpu/o3/bac.cc b/src/cpu/o3/bac.cc
index cbb11762693..b7dce266ae1 100644
--- a/src/cpu/o3/bac.cc
+++ b/src/cpu/o3/bac.cc
@@ -72,6 +72,7 @@ BAC::BAC(CPU *_cpu, const BaseO3CPUParams ¶ms)
       decodeToFetchDelay(params.decodeToFetchDelay),
       commitToFetchDelay(params.commitToFetchDelay),
       bacToFetchDelay(params.bacToFetchDelay),
+      bacBranchPredictDelay(params.bacBranchPredictDelay),
       fetchTargetWidth(params.fetchTargetWidth),
       minInstSize(params.minInstSize),
       numThreads(params.numThreads),
@@ -83,6 +84,7 @@ BAC::BAC(CPU *_cpu, const BaseO3CPUParams ¶ms)
     for (int i = 0; i < MaxThreads; i++) {
         bacPC[i].reset(params.isa[0]->newPCState());
         stalls[i] = {false, false, false};
+        branchPredictRemaining[i] = Cycles(0);
     }
 
     assert(bpu!=nullptr);
@@ -401,12 +403,24 @@ BAC::checkSignalsAndUpdate(ThreadID tid)
         return true;
     }
 
+    if (branchPredictRemaining[tid] > Cycles(0)) {
+        --branchPredictRemaining[tid];
+        DPRINTF(BAC,
+            "[global] Stalling for Branch Predictor for %i more cycles.\n",
+            branchPredictRemaining
+        );
+        stalls[tid].bpu = true;
+    } else {
+        stalls[tid].bpu = false;
+    }
+
     if (checkStall(tid)) {
         // return block(tid);
         bacStatus[tid] = Blocked;
         return false;
     }
 
+
     // If at this point the FTQ is still invalid we need to wait for
     // A resteer/squash signal.
     if (!ftq->isValid(tid) && bacStatus[tid] != Idle) {
@@ -679,6 +693,7 @@ BAC::generateFetchTargets(ThreadID tid, bool &status_change)
         // Now make the actual prediction. Note the BPU will advance
         // the PC to the next instruction.
         predict_taken = predict(tid, staticInst, curFT, *next_pc);
+        branchPredictRemaining[tid] = Cycles(bacBranchPredictDelay);
 
         DPRINTF(BAC, "[tid:%i, ftn:%llu] Branch found at PC %#x "
                 "taken?:%i, target:%#x\n",
diff --git a/src/cpu/o3/bac.hh b/src/cpu/o3/bac.hh
index 8d6e59072b5..7e1f899bdf9 100644
--- a/src/cpu/o3/bac.hh
+++ b/src/cpu/o3/bac.hh
@@ -373,6 +373,9 @@ class BAC
      */
     bool wroteToTimeBuffer;
 
+    /** Tracks remaining cycles that the branch predictor stalls BAC */
+    Cycles branchPredictRemaining[MaxThreads];
+
     /** Source of possible stalls. */
     struct Stalls
     {
@@ -399,6 +402,9 @@ class BAC
     /** BAC to fetch delay. */
     const Cycles bacToFetchDelay;
 
+    /** BAC branch predict delay. */
+    const Cycles bacBranchPredictDelay;
+
     /** The maximum width of a fetch target. This also determines the
      * maximum addresses searched in one cycle. (FT width / minInstSize) */
     const unsigned fetchTargetWidth;
From b2c1442fd7e648ad32a092855b26ee43041445da Mon Sep 17 00:00:00 2001
From: Phillip Assmann 
Date: Thu, 10 Apr 2025 15:23:50 +0000
Subject: [PATCH 3/3] cpu: move cond pred out of bpred unit
---
 src/cpu/minor/BaseMinorCPU.py               |   5 +-
 src/cpu/o3/BaseO3CPU.py                     |   5 +-
 src/cpu/pred/2bit_local.cc                  |   8 +-
 src/cpu/pred/2bit_local.hh                  |   7 +-
 src/cpu/pred/BranchPredictor.py             |  28 +++-
 src/cpu/pred/SConscript                     |   2 +
 src/cpu/pred/bi_mode.cc                     |   2 +-
 src/cpu/pred/bi_mode.hh                     |   4 +-
 src/cpu/pred/bpred_unit.cc                  |  24 ++--
 src/cpu/pred/bpred_unit.hh                  |  97 +++----------
 src/cpu/pred/conditional.cc                 |  52 +++++++
 src/cpu/pred/conditional.hh                 | 148 ++++++++++++++++++++
 src/cpu/pred/multiperspective_perceptron.cc |   2 +-
 src/cpu/pred/multiperspective_perceptron.hh |   4 +-
 src/cpu/pred/tage.cc                        |   4 +-
 src/cpu/pred/tage.hh                        |   4 +-
 src/cpu/pred/tagescl_ref.cc                 |   2 +-
 src/cpu/pred/tagescl_ref.hh                 |   4 +-
 src/cpu/pred/tournament.cc                  |   2 +-
 src/cpu/pred/tournament.hh                  |   4 +-
 20 files changed, 295 insertions(+), 113 deletions(-)
 create mode 100644 src/cpu/pred/conditional.cc
 create mode 100644 src/cpu/pred/conditional.hh
diff --git a/src/cpu/minor/BaseMinorCPU.py b/src/cpu/minor/BaseMinorCPU.py
index 545dfeaee56..5484a11a882 100644
--- a/src/cpu/minor/BaseMinorCPU.py
+++ b/src/cpu/minor/BaseMinorCPU.py
@@ -426,7 +426,10 @@ def support_take_over(cls):
     )
 
     branchPred = Param.BranchPredictor(
-        TournamentBP(numThreads=Parent.numThreads), "Branch Predictor"
+        BranchPredictor(
+            conditionalBranchPred=TournamentBP(numThreads=Parent.numThreads)
+        ),
+        "Branch Predictor",
     )
 
     def addCheckerCpu(self):
diff --git a/src/cpu/o3/BaseO3CPU.py b/src/cpu/o3/BaseO3CPU.py
index e1cc8358854..d97c1f2c7dd 100644
--- a/src/cpu/o3/BaseO3CPU.py
+++ b/src/cpu/o3/BaseO3CPU.py
@@ -210,7 +210,10 @@ def support_take_over(cls):
     smtCommitPolicy = Param.CommitPolicy("RoundRobin", "SMT Commit Policy")
 
     branchPred = Param.BranchPredictor(
-        TournamentBP(numThreads=Parent.numThreads), "Branch Predictor"
+        BranchPredictor(
+            conditionalBranchPred=TournamentBP(numThreads=Parent.numThreads)
+        ),
+        "Branch Predictor",
     )
     needsTSO = Param.Bool(False, "Enable TSO Memory model")
 
diff --git a/src/cpu/pred/2bit_local.cc b/src/cpu/pred/2bit_local.cc
index 7c27355b20f..34b495bc218 100644
--- a/src/cpu/pred/2bit_local.cc
+++ b/src/cpu/pred/2bit_local.cc
@@ -52,7 +52,7 @@ namespace branch_prediction
 {
 
 LocalBP::LocalBP(const LocalBPParams ¶ms)
-    : BPredUnit(params),
+    : ConditionalPredictor(params),
       localPredictorSize(params.localPredictorSize),
       localCtrBits(params.localCtrBits),
       localPredictorSets(localPredictorSize / localCtrBits),
@@ -78,6 +78,12 @@ LocalBP::LocalBP(const LocalBPParams ¶ms)
             instShiftAmt);
 }
 
+void LocalBP::branchPlaceholder(ThreadID tid, Addr pc,
+                                bool uncond, void * &bpHistory)
+{
+// Placeholder for a function that only returns history items
+}
+
 void
 LocalBP::updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken,
                          Addr target, const StaticInstPtr &inst,
diff --git a/src/cpu/pred/2bit_local.hh b/src/cpu/pred/2bit_local.hh
index 8d77289f966..bb539d37a9c 100644
--- a/src/cpu/pred/2bit_local.hh
+++ b/src/cpu/pred/2bit_local.hh
@@ -46,7 +46,7 @@
 
 #include "base/sat_counter.hh"
 #include "base/types.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "params/LocalBP.hh"
 
 namespace gem5
@@ -62,7 +62,7 @@ namespace branch_prediction
  * predictor state that needs to be recorded or updated; the update can be
  * determined solely by the branch being taken or not taken.
  */
-class LocalBP : public BPredUnit
+class LocalBP : public ConditionalPredictor
 {
   public:
     /**
@@ -73,6 +73,9 @@ class LocalBP : public BPredUnit
     // Overriding interface functions
     bool lookup(ThreadID tid, Addr pc, void * &bp_history) override;
 
+    void branchPlaceholder(ThreadID tid, Addr pc, bool uncond,
+                           void * &bpHistory) override;
+
     void updateHistories(ThreadID tid, Addr pc, bool uncond, bool taken,
                          Addr target, const StaticInstPtr &inst,
                          void * &bp_history) override;
diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py
index 496b92a02d7..d23249899a5 100644
--- a/src/cpu/pred/BranchPredictor.py
+++ b/src/cpu/pred/BranchPredictor.py
@@ -142,6 +142,18 @@ class SimpleBTB(BranchTargetBuffer):
     )
 
 
+class ConditionalPredictor(SimObject):
+    type = "ConditionalPredictor"
+    cxx_class = "gem5::branch_prediction::ConditionalPredictor"
+    cxx_header = "cpu/pred/conditional.hh"
+    abstract = True
+
+    numThreads = Param.Unsigned(Parent.numThreads, "Number of threads")
+    instShiftAmt = Param.Unsigned(
+        Parent.instShiftAmt, "Number of bits to shift instructions by"
+    )
+
+
 class IndirectPredictor(SimObject):
     type = "IndirectPredictor"
     cxx_class = "gem5::branch_prediction::IndirectPredictor"
@@ -179,7 +191,6 @@ class BranchPredictor(SimObject):
     type = "BranchPredictor"
     cxx_class = "gem5::branch_prediction::BPredUnit"
     cxx_header = "cpu/pred/bpred_unit.hh"
-    abstract = True
 
     numThreads = Param.Unsigned(Parent.numThreads, "Number of threads")
     instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by")
@@ -197,6 +208,9 @@ class BranchPredictor(SimObject):
     ras = Param.ReturnAddrStack(
         ReturnAddrStack(), "Return address stack, set to NULL to disable RAS."
     )
+    conditionalBranchPred = Param.ConditionalPredictor(
+        "Conditional branch predictor"
+    )
     indirectBranchPred = Param.IndirectPredictor(
         SimpleIndirectPredictor(),
         "Indirect branch predictor, set to NULL to disable "
@@ -212,7 +226,7 @@ class BranchPredictor(SimObject):
     )
 
 
-class LocalBP(BranchPredictor):
+class LocalBP(ConditionalPredictor):
     type = "LocalBP"
     cxx_class = "gem5::branch_prediction::LocalBP"
     cxx_header = "cpu/pred/2bit_local.hh"
@@ -221,7 +235,7 @@ class LocalBP(BranchPredictor):
     localCtrBits = Param.Unsigned(2, "Bits per counter")
 
 
-class TournamentBP(BranchPredictor):
+class TournamentBP(ConditionalPredictor):
     type = "TournamentBP"
     cxx_class = "gem5::branch_prediction::TournamentBP"
     cxx_header = "cpu/pred/tournament.hh"
@@ -235,7 +249,7 @@ class TournamentBP(BranchPredictor):
     choiceCtrBits = Param.Unsigned(2, "Bits of choice counters")
 
 
-class BiModeBP(BranchPredictor):
+class BiModeBP(ConditionalPredictor):
     type = "BiModeBP"
     cxx_class = "gem5::branch_prediction::BiModeBP"
     cxx_header = "cpu/pred/bi_mode.hh"
@@ -310,7 +324,7 @@ class TAGEBase(SimObject):
 
 # TAGE branch predictor as described in https://www.jilp.org/vol8/v8paper1.pdf
 # The default sizes below are for the 8C-TAGE configuration (63.5 Kbits)
-class TAGE(BranchPredictor):
+class TAGE(ConditionalPredictor):
     type = "TAGE"
     cxx_class = "gem5::branch_prediction::TAGE"
     cxx_header = "cpu/pred/tage.hh"
@@ -776,7 +790,7 @@ class TAGE_SC_L_8KB(TAGE_SC_L):
     statistical_corrector = TAGE_SC_L_8KB_StatisticalCorrector()
 
 
-class MultiperspectivePerceptron(BranchPredictor):
+class MultiperspectivePerceptron(ConditionalPredictor):
     type = "MultiperspectivePerceptron"
     cxx_class = "gem5::branch_prediction::MultiperspectivePerceptron"
     cxx_header = "cpu/pred/multiperspective_perceptron.hh"
@@ -1117,7 +1131,7 @@ class MultiperspectivePerceptronTAGE8KB(MultiperspectivePerceptronTAGE):
     statistical_corrector = MPP_StatisticalCorrector_8KB()
 
 
-class TageSCLRef(BranchPredictor):
+class TageSCLRef(ConditionalPredictor):
     type = "TageSCLRef"
     cxx_class = "gem5::branch_prediction::TageSCLRef"
     cxx_header = "cpu/pred/tagescl_ref.hh"
diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript
index f52299fecc0..e791f318d26 100644
--- a/src/cpu/pred/SConscript
+++ b/src/cpu/pred/SConscript
@@ -44,6 +44,7 @@ Import('*')
 SimObject('BranchPredictor.py',
     sim_objects=[
     'BranchPredictor',
+    'ConditionalPredictor',
     'IndirectPredictor', 'SimpleIndirectPredictor',
     'BranchTargetBuffer', 'SimpleBTB', 'BTBIndexingPolicy', 'BTBSetAssociative',
     'ReturnAddrStack',
@@ -68,6 +69,7 @@ Source('bpred_unit.cc')
 Source('2bit_local.cc')
 Source('simple_indirect.cc')
 Source('it_tage.cc')
+Source('conditional.cc')
 Source('indirect.cc')
 Source('ras.cc')
 Source('tournament.cc')
diff --git a/src/cpu/pred/bi_mode.cc b/src/cpu/pred/bi_mode.cc
index f9f9330b883..ed39731ab28 100644
--- a/src/cpu/pred/bi_mode.cc
+++ b/src/cpu/pred/bi_mode.cc
@@ -54,7 +54,7 @@ namespace branch_prediction
 {
 
 BiModeBP::BiModeBP(const BiModeBPParams ¶ms)
-    : BPredUnit(params),
+    : ConditionalPredictor(params),
       globalHistoryReg(params.numThreads, 0),
       globalHistoryBits(ceilLog2(params.globalPredictorSize)),
       choicePredictorSize(params.choicePredictorSize),
diff --git a/src/cpu/pred/bi_mode.hh b/src/cpu/pred/bi_mode.hh
index c0513826730..2131677ae90 100644
--- a/src/cpu/pred/bi_mode.hh
+++ b/src/cpu/pred/bi_mode.hh
@@ -46,7 +46,7 @@
 #define __CPU_PRED_BI_MODE_PRED_HH__
 
 #include "base/sat_counter.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "params/BiModeBP.hh"
 
 namespace gem5
@@ -69,7 +69,7 @@ namespace branch_prediction
  * the branch's PC to choose between the two, destructive aliasing is reduced.
  */
 
-class BiModeBP : public BPredUnit
+class BiModeBP : public ConditionalPredictor
 {
   public:
     BiModeBP(const BiModeBPParams ¶ms);
diff --git a/src/cpu/pred/bpred_unit.cc b/src/cpu/pred/bpred_unit.cc
index 2e0daec6760..95aa71026fc 100644
--- a/src/cpu/pred/bpred_unit.cc
+++ b/src/cpu/pred/bpred_unit.cc
@@ -63,6 +63,7 @@ BPredUnit::BPredUnit(const Params ¶ms)
       predHist(numThreads),
       btb(params.btb),
       ras(params.ras),
+      cPred(params.conditionalBranchPred),
       iPred(params.indirectBranchPred),
       stats(this)
 {
@@ -94,12 +95,6 @@ BPredUnit::drainSanityCheck() const
         assert(ph.empty());
 }
 
-void
-BPredUnit::branchPlaceholder(ThreadID tid, Addr pc,
-                             bool uncond, void * &bp_history)
-{
-    panic("BPredUnit::branchPlaceholder() not implemented for this BP.\n");
-}
 
 bool
 BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
@@ -156,7 +151,7 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
     } else {
         // Conditional branches -------
         ++stats.condPredicted;
-        hist->condPred = lookup(tid, pc.instAddr(), hist->bpHistory);
+        hist->condPred = cPred->lookup(tid, pc.instAddr(), hist->bpHistory);
 
         if (hist->condPred) {
             ++stats.condPredictedTaken;
@@ -326,7 +321,7 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
      * The actual prediction tables will updated once
      * we know the correct direction.
      **/
-    updateHistories(tid, hist->pc, hist->uncond, hist->predTaken,
+    cPred->updateHistories(tid, hist->pc, hist->uncond, hist->predTaken,
                     hist->target->instAddr(), hist->inst, hist->bpHistory);
 
 
@@ -383,7 +378,7 @@ BPredUnit::commitBranch(ThreadID tid, PredictorHistory* &hist)
                 hist->target->instAddr());
 
     // Update the branch predictor with the correct results.
-    update(tid, hist->pc,
+    cPred->update(tid, hist->pc,
                 hist->actuallyTaken,
                 hist->bpHistory, false,
                 hist->inst,
@@ -469,7 +464,7 @@ BPredUnit::squashHistory(ThreadID tid, PredictorHistory* &history)
     }
 
     // This call will delete the bpHistory.
-    squash(tid, history->bpHistory);
+    cPred->squash(tid, history->bpHistory);
 
     delete history;
     history = nullptr;
@@ -548,7 +543,7 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
         set(hist->target,  corr_target);
 
         // Correct Direction predictor ------------------
-        update(tid, hist->pc, actually_taken, hist->bpHistory,
+        cPred->update(tid, hist->pc, actually_taken, hist->bpHistory,
                true, hist->inst, corr_target.instAddr());
 
 
@@ -633,6 +628,13 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
     }
 }
 
+void
+BPredUnit::branchPlaceholder(ThreadID tid, Addr pc,
+                             bool uncond, void * &bp_history)
+{
+    // Delegate to conditional predictor
+    cPred->branchPlaceholder(tid, pc, uncond, bp_history);
+}
 
 void
 BPredUnit::dump()
diff --git a/src/cpu/pred/bpred_unit.hh b/src/cpu/pred/bpred_unit.hh
index c94e5231818..f3f4ceded38 100644
--- a/src/cpu/pred/bpred_unit.hh
+++ b/src/cpu/pred/bpred_unit.hh
@@ -49,6 +49,7 @@
 #include "cpu/inst_seq.hh"
 #include "cpu/pred/branch_type.hh"
 #include "cpu/pred/btb.hh"
+#include "cpu/pred/conditional.hh"
 #include "cpu/pred/indirect.hh"
 #include "cpu/pred/ras.hh"
 #include "cpu/static_inst.hh"
@@ -141,81 +142,6 @@ class BPredUnit : public SimObject
      * Interface functions to the conditional branch predictor
      *
     */
-
-    /**
-     * Looks up a given conditional branch PC of in the BP to see if it
-     * is taken or not taken.
-     * @param tid The thread id.
-     * @param pc The PC to look up.
-     * @param bp_history Pointer that will be set to an object that
-     * has the branch predictor state associated with the lookup.
-     * @return Whether the branch is taken or not taken.
-     */
-    virtual bool lookup(ThreadID tid, Addr pc, void * &bp_history) = 0;
-
-    /**
-     * Ones done with the prediction this function updates the
-     * path and global history. All branches call this function
-     * including unconditional once.
-     * @param tid The thread id.
-     * @param pc The branch's pc that will be updated.
-     * @param uncond Wheather or not this branch is an unconditional branch.
-     * @param taken Whether or not the branch was taken
-     * @param target The final target of branch. Some modern
-     * predictors use the target in their history.
-     * @param inst Static instruction information
-     * @param bp_history Pointer that will be set to an object that
-     * has the branch predictor state associated with the lookup.
-     *
-     */
-    virtual void updateHistories(ThreadID tid, Addr pc, bool uncond,
-                           bool taken, Addr target,
-                           const StaticInstPtr &inst, void * &bp_history) = 0;
-
-    /**
-     * @param tid The thread id.
-     * @param bp_history Pointer to the history object.  The predictor
-     * will need to update any state and delete the object.
-     */
-    virtual void squash(ThreadID tid, void * &bp_history) = 0;
-
-
-    /**
-     * Updates the BP with taken/not taken information.
-     * @param tid The thread id.
-     * @param pc The branch's PC that will be updated.
-     * @param taken Whether the branch was taken or not taken.
-     * @param bp_history Pointer to the branch predictor state that is
-     * associated with the branch lookup that is being updated.
-     * @param squashed Set to true when this function is called during a
-     * squash operation.
-     * @param inst Static instruction information
-     * @param target The resolved target of the branch (only needed
-     * for squashed branches)
-     * @todo Make this update flexible enough to handle a global predictor.
-     */
-    virtual void update(ThreadID tid, Addr pc, bool taken,
-                        void * &bp_history, bool squashed,
-                        const StaticInstPtr &inst, Addr target) = 0;
-
-    /**
-     * Special function for the decoupled front-end. In it there can be
-     * branches which are not detected by the BPU in the first place as it
-     * requires a BTB hit. This function will generate a placeholder for
-     * such a branch once it is pre-decoded in the fetch stage. It will
-     * only create the branch history object but not update any internal state
-     * of the BPU.
-     * If the branch turns to be wrong then decode or commit will
-     * be able to use the normal squash functionality to correct the branch.
-     * Note that not all branch predictors implement this functionality.
-     * @param tid The thread id.
-     * @param pc The branch's PC.
-     * @param uncond Whether or not this branch is an unconditional branch.
-     * @param bp_history Pointer that will be set to an branch history object.
-     */
-    virtual void branchPlaceholder(ThreadID tid, Addr pc,
-                                   bool uncond, void * &bp_history);
-
     /**
      * Looks up a given PC in the BTB to see if a matching entry exists.
      * @param tid The thread id.
@@ -270,6 +196,24 @@ class BPredUnit : public SimObject
         return btb->update(tid, pc, target);
     }
 
+    /**
+     * Special function for the decoupled front-end. In it there can be
+     * branches which are not detected by the BPU in the first place as it
+     * requires a BTB hit. This function will generate a placeholder for
+     * such a branch once it is pre-decoded in the fetch stage. It will
+     * only create the branch history object but not update any internal state
+     * of the BPU.
+     * If the branch turns to be wrong then decode or commit will
+     * be able to use the normal squash functionality to correct the branch.
+     * Note that not all branch predictors implement this functionality.
+     * @param tid The thread id.
+     * @param pc The branch's PC.
+     * @param uncond Whether or not this branch is an unconditional branch.
+     * @param bp_history Pointer that will be set to an branch history object.
+     */
+    void branchPlaceholder(ThreadID tid, Addr pc,
+                            bool uncond, void * &bp_history);
+
 
     void dump();
 
@@ -492,6 +436,9 @@ class BPredUnit : public SimObject
     /** The return address stack. */
     ReturnAddrStack * ras;
 
+    /** The conditional branch predictor. */
+    ConditionalPredictor * cPred;
+
     /** The indirect target predictor. */
     IndirectPredictor * iPred;
 
diff --git a/src/cpu/pred/conditional.cc b/src/cpu/pred/conditional.cc
new file mode 100644
index 00000000000..a59ecd99120
--- /dev/null
+++ b/src/cpu/pred/conditional.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2025 Technical University of Munich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cpu/pred/conditional.hh"
+
+namespace gem5
+{
+
+namespace branch_prediction
+{
+
+ConditionalPredictor::ConditionalPredictor(const Params ¶ms)
+    : SimObject(params),
+      instShiftAmt(params.instShiftAmt)
+{
+}
+
+
+void
+ConditionalPredictor::branchPlaceholder(ThreadID tid, Addr pc,
+                             bool uncond, void * &bp_history)
+{
+    panic("BPredUnit::branchPlaceholder() not implemented for this BP.\n");
+}
+
+} // namespace branch_prediction
+} // namespace gem5
diff --git a/src/cpu/pred/conditional.hh b/src/cpu/pred/conditional.hh
new file mode 100644
index 00000000000..46dbf2b3a34
--- /dev/null
+++ b/src/cpu/pred/conditional.hh
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2025 Technical University of Munich
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* @file
+ * Conditional branch predictor interface
+ */
+
+#ifndef __CPU_PRED_CONDITIONAL_BASE_HH__
+#define __CPU_PRED_CONDITIONAL_BASE_HH__
+
+#include "arch/generic/pcstate.hh"
+#include "cpu/inst_seq.hh"
+#include "cpu/pred/branch_type.hh"
+#include "params/ConditionalPredictor.hh"
+#include "sim/sim_object.hh"
+
+namespace gem5
+{
+
+namespace branch_prediction
+{
+
+class ConditionalPredictor : public SimObject
+{
+  public:
+
+    typedef ConditionalPredictorParams Params;
+
+    ConditionalPredictor(const Params ¶ms);
+
+
+    /**
+     * Looks up a given conditional branch PC of in the BP to see if it
+     * is taken or not taken.
+     * @param tid The thread id.
+     * @param pc The PC to look up.
+     * @param bp_history Pointer that will be set to an object that
+     * has the branch predictor state associated with the lookup.
+     * @return Whether the branch is taken or not taken.
+     */
+    virtual bool lookup(ThreadID tid, Addr pc, void * &bp_history) = 0;
+
+    /**
+     * Ones done with the prediction this function updates the
+     * path and global history. All branches call this function
+     * including unconditional once.
+     * @param tid The thread id.
+     * @param pc The branch's pc that will be updated.
+     * @param uncond Wheather or not this branch is an unconditional branch.
+     * @param taken Whether or not the branch was taken
+     * @param target The final target of branch. Some modern
+     * predictors use the target in their history.
+     * @param inst Static instruction information
+     * @param bp_history Pointer that will be set to an object that
+     * has the branch predictor state associated with the lookup.
+     *
+     */
+    virtual void updateHistories(ThreadID tid, Addr pc, bool uncond,
+                           bool taken, Addr target,
+                           const StaticInstPtr &inst, void * &bp_history) = 0;
+
+    /**
+     * @param tid The thread id.
+     * @param bp_history Pointer to the history object.  The predictor
+     * will need to update any state and delete the object.
+     */
+    virtual void squash(ThreadID tid, void * &bp_history) = 0;
+
+
+    /**
+     * Updates the BP with taken/not taken information.
+     * @param tid The thread id.
+     * @param pc The branch's PC that will be updated.
+     * @param taken Whether the branch was taken or not taken.
+     * @param bp_history Pointer to the branch predictor state that is
+     * associated with the branch lookup that is being updated.
+     * @param squashed Set to true when this function is called during a
+     * squash operation.
+     * @param inst Static instruction information
+     * @param target The resolved target of the branch (only needed
+     * for squashed branches)
+     * @todo Make this update flexible enough to handle a global predictor.
+     */
+    virtual void update(ThreadID tid, Addr pc, bool taken,
+                        void * &bp_history, bool squashed,
+                        const StaticInstPtr &inst, Addr target) = 0;
+
+    /**
+     * Special function for the decoupled front-end. In it there can be
+     * branches which are not detected by the BPU in the first place as it
+     * requires a BTB hit. This function will generate a placeholder for
+     * such a branch once it is pre-decoded in the fetch stage. It will
+     * only create the branch history object but not update any internal state
+     * of the BPU.
+     * If the branch turns to be wrong then decode or commit will
+     * be able to use the normal squash functionality to correct the branch.
+     * Note that not all branch predictors implement this functionality.
+     * @param tid The thread id.
+     * @param pc The branch's PC.
+     * @param uncond Whether or not this branch is an unconditional branch.
+     * @param bp_history Pointer that will be set to an branch history object.
+     */
+    virtual void branchPlaceholder(ThreadID tid, Addr pc,
+                                   bool uncond, void * &bp_history);
+  protected:
+
+    /** Number of bits to shift instructions by for predictor addresses. */
+    const unsigned instShiftAmt;
+};
+
+} // namespace branch_prediction
+} // namespace gem5
+
+#endif // __CPU_PRED_CONDITIONAL_BASE_HH__
diff --git a/src/cpu/pred/multiperspective_perceptron.cc b/src/cpu/pred/multiperspective_perceptron.cc
index c8284e49a1d..3f1bdec504b 100644
--- a/src/cpu/pred/multiperspective_perceptron.cc
+++ b/src/cpu/pred/multiperspective_perceptron.cc
@@ -128,7 +128,7 @@ MultiperspectivePerceptron::ThreadData::ThreadData(int num_filters,
 }
 
 MultiperspectivePerceptron::MultiperspectivePerceptron(
-    const MultiperspectivePerceptronParams &p) : BPredUnit(p),
+    const MultiperspectivePerceptronParams &p) : ConditionalPredictor(p),
     blockSize(p.block_size), pcshift(p.pcshift), threshold(p.threshold),
     bias0(p.bias0), bias1(p.bias1), biasmostly0(p.biasmostly0),
     biasmostly1(p.biasmostly1), nbest(p.nbest), tunebits(p.tunebits),
diff --git a/src/cpu/pred/multiperspective_perceptron.hh b/src/cpu/pred/multiperspective_perceptron.hh
index f1055d5fae3..af00715fdf1 100644
--- a/src/cpu/pred/multiperspective_perceptron.hh
+++ b/src/cpu/pred/multiperspective_perceptron.hh
@@ -55,7 +55,7 @@
 #include 
 
 #include "base/random.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "params/MultiperspectivePerceptron.hh"
 
 namespace gem5
@@ -64,7 +64,7 @@ namespace gem5
 namespace branch_prediction
 {
 
-class MultiperspectivePerceptron : public BPredUnit
+class MultiperspectivePerceptron : public ConditionalPredictor
 {
   protected:
     /**
diff --git a/src/cpu/pred/tage.cc b/src/cpu/pred/tage.cc
index dd6ef5ddbe0..831d1834aaa 100644
--- a/src/cpu/pred/tage.cc
+++ b/src/cpu/pred/tage.cc
@@ -62,7 +62,9 @@ namespace gem5
 namespace branch_prediction
 {
 
-TAGE::TAGE(const TAGEParams ¶ms) : BPredUnit(params), tage(params.tage)
+TAGE::TAGE(const TAGEParams ¶ms) :
+    ConditionalPredictor(params),
+    tage(params.tage)
 {
 }
 
diff --git a/src/cpu/pred/tage.hh b/src/cpu/pred/tage.hh
index 329ba922ad4..482b546c391 100644
--- a/src/cpu/pred/tage.hh
+++ b/src/cpu/pred/tage.hh
@@ -64,7 +64,7 @@
 
 #include "base/random.hh"
 #include "base/types.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "cpu/pred/tage_base.hh"
 #include "params/TAGE.hh"
 
@@ -74,7 +74,7 @@ namespace gem5
 namespace branch_prediction
 {
 
-class TAGE: public BPredUnit
+class TAGE: public ConditionalPredictor
 {
   protected:
     TAGEBase *tage;
diff --git a/src/cpu/pred/tagescl_ref.cc b/src/cpu/pred/tagescl_ref.cc
index 032bded5285..8a13365b62a 100644
--- a/src/cpu/pred/tagescl_ref.cc
+++ b/src/cpu/pred/tagescl_ref.cc
@@ -54,7 +54,7 @@ namespace branch_prediction
 {
 
 TageSCLRef::TageSCLRef(const TageSCLRefParams ¶ms)
-    : BPredUnit(params)
+    : ConditionalPredictor(params)
 {
     predictor = new PREDICTOR();
 }
diff --git a/src/cpu/pred/tagescl_ref.hh b/src/cpu/pred/tagescl_ref.hh
index 7ae2db85c0d..f1cb86e94b3 100644
--- a/src/cpu/pred/tagescl_ref.hh
+++ b/src/cpu/pred/tagescl_ref.hh
@@ -46,7 +46,7 @@
 
 #include "base/sat_counter.hh"
 #include "base/types.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "params/TageSCLRef.hh"
 
 namespace gem5
@@ -63,7 +63,7 @@ namespace branch_prediction
  * predictor state that needs to be recorded or updated; the update can be
  * determined solely by the branch being taken or not taken.
  */
-class TageSCLRef : public BPredUnit
+class TageSCLRef : public ConditionalPredictor
 {
   public:
     /**
diff --git a/src/cpu/pred/tournament.cc b/src/cpu/pred/tournament.cc
index a6428575f28..bbedaf15504 100644
--- a/src/cpu/pred/tournament.cc
+++ b/src/cpu/pred/tournament.cc
@@ -51,7 +51,7 @@ namespace branch_prediction
 {
 
 TournamentBP::TournamentBP(const TournamentBPParams ¶ms)
-    : BPredUnit(params),
+    : ConditionalPredictor(params),
       localPredictorSize(params.localPredictorSize),
       localCtrBits(params.localCtrBits),
       localCtrs(localPredictorSize, SatCounter8(localCtrBits)),
diff --git a/src/cpu/pred/tournament.hh b/src/cpu/pred/tournament.hh
index 36b50c706a4..8de9faa695e 100644
--- a/src/cpu/pred/tournament.hh
+++ b/src/cpu/pred/tournament.hh
@@ -46,7 +46,7 @@
 
 #include "base/sat_counter.hh"
 #include "base/types.hh"
-#include "cpu/pred/bpred_unit.hh"
+#include "cpu/pred/conditional.hh"
 #include "params/TournamentBP.hh"
 
 namespace gem5
@@ -63,7 +63,7 @@ namespace branch_prediction
  * predictor chooses between the two.  Both the global history register
  * and the selected local history are speculatively updated.
  */
-class TournamentBP : public BPredUnit
+class TournamentBP : public ConditionalPredictor
 {
   public:
     /**