Skip to content

Commit 8d187b2

Browse files
authored
Branch Prediction API (#139)
A code introducing branch prediction API.
1 parent 9d9a345 commit 8d187b2

File tree

7 files changed

+264
-0
lines changed

7 files changed

+264
-0
lines changed

core/BranchPredIF.hpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// <Branch.hpp> -*- C++ -*-
2+
3+
//!
4+
//! \file BranchPred.hpp
5+
//! \brief Definition of Branch Prediction API
6+
//!
7+
8+
/*
9+
* This file defines the Branch Prediction API.
10+
* The goal is to define an API that is generic and yet flexible enough to support various
11+
* branch prediction microarchitecture.
12+
* To the end, we envision a generic branch predictor as a black box with following inputs
13+
* and outputs:
14+
* * A generic Prediction output
15+
* * A generic Prediction input
16+
* * A generic Update input
17+
*
18+
* The generic branch predictor may have two operations:
19+
* * getPrediction: produces Prediction output based on the Prediction input.
20+
* * updatePredictor: updates Predictor with Update input.
21+
*
22+
* It is intended that an implementation of branch predictor must also specify
23+
* implementations of Prediction output, Prediction input and Update input, along with
24+
* implementations of getPrediction and updatePredictor operations.
25+
* */
26+
#pragma once
27+
28+
namespace olympia
29+
{
30+
namespace BranchPredictor
31+
{
32+
33+
template <class PredictionT, class UpdateT, class InputT>
34+
class BranchPredictorIF
35+
{
36+
public:
37+
// TODO: create constexpr for bytes per compressed and uncompressed inst
38+
static constexpr uint8_t bytes_per_inst = 4;
39+
virtual ~BranchPredictorIF() { };
40+
virtual PredictionT getPrediction(const InputT &) = 0;
41+
virtual void updatePredictor(const UpdateT &) = 0;
42+
};
43+
44+
} // namespace BranchPredictor
45+
} // namespace olympia

core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
project (core)
22
add_library(core
33
Core.cpp
4+
SimpleBranchPred.cpp
45
Fetch.cpp
56
Decode.cpp
67
Rename.cpp

core/SimpleBranchPred.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "SimpleBranchPred.hpp"
2+
3+
/*
4+
* The algorithm used for prediction / update is as follows:
5+
* Prediction:
6+
* - look up BHT to determine if the branch is predicted taken or not
7+
* using 2-bit saturated counter
8+
* - value 3: strongly taken
9+
* - value 2: weakly taken
10+
* - value 1: weakly not taken
11+
* - value 0: strongly not taken
12+
* - look up BTB to see if an entry exists for the input fetch pc
13+
* - if present in BTB and predicted taken, BTB entry is used to determine
14+
* prediction branch idx and predicted_PC
15+
* - if present in BTB but predicted not taken, BTB entry is used to determine
16+
* prediction branch idx, while predicted_PC is the fall through addr
17+
* - if not present in BTB entry, prediction branch idx is the last instr of
18+
* the FetchPacket, while predicted PC is the fall through addr. Also, create
19+
* a new BTB entry
20+
* Update:
21+
* - a valid BTB entry must be present for fetch PC
22+
* - TBD
23+
*
24+
*/
25+
namespace olympia
26+
{
27+
namespace BranchPredictor
28+
{
29+
30+
void SimpleBranchPredictor::updatePredictor(const DefaultUpdate & update) {
31+
32+
sparta_assert(branch_target_buffer_.find(update.fetch_PC) != branch_target_buffer_.end());
33+
branch_target_buffer_[update.fetch_PC].branch_idx = update.branch_idx;
34+
if (update.actually_taken) {
35+
branch_history_table_[update.fetch_PC] =
36+
(branch_history_table_[update.fetch_PC] == 3) ? 3 :
37+
branch_history_table_[update.fetch_PC] + 1;
38+
branch_target_buffer_[update.fetch_PC].predicted_PC = update.corrected_PC;
39+
} else {
40+
branch_history_table_[update.fetch_PC] =
41+
(branch_history_table_[update.fetch_PC] == 0) ? 0 :
42+
branch_history_table_[update.fetch_PC] - 1;
43+
}
44+
}
45+
46+
DefaultPrediction SimpleBranchPredictor::getPrediction(const DefaultInput & input) {
47+
bool predictTaken = false;
48+
if (branch_history_table_.find(input.fetch_PC) != branch_history_table_.end()) {
49+
predictTaken = (branch_history_table_[input.fetch_PC] > 1);
50+
} else {
51+
// add a new entry to BHT, biased towards not taken
52+
branch_history_table_.insert(std::pair<uint64_t, uint8_t>(input.fetch_PC, 1));
53+
}
54+
55+
DefaultPrediction prediction;
56+
if (branch_target_buffer_.find(input.fetch_PC) != branch_target_buffer_.end()) {
57+
// BTB hit
58+
const BTBEntry & btb_entry = branch_target_buffer_[input.fetch_PC];
59+
prediction.branch_idx = btb_entry.branch_idx;
60+
if (predictTaken) {
61+
prediction.predicted_PC = btb_entry.predicted_PC;
62+
} else {
63+
// fall through address
64+
prediction.predicted_PC = input.fetch_PC + prediction.branch_idx + BranchPredictorIF::bytes_per_inst;
65+
}
66+
} else {
67+
// BTB miss
68+
prediction.branch_idx = max_fetch_insts_;
69+
prediction.predicted_PC = input.fetch_PC + max_fetch_insts_ * bytes_per_inst;
70+
// add new entry to BTB
71+
branch_target_buffer_.insert(std::pair<uint64_t,BTBEntry>(
72+
input.fetch_PC, BTBEntry(prediction.branch_idx, prediction.predicted_PC)));
73+
}
74+
75+
return prediction;
76+
}
77+
78+
} // namespace BranchPredictor
79+
} // namespace olympia

core/SimpleBranchPred.hpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// <SimpleBranchPred.hpp> -*- C++ -*-
2+
3+
//!
4+
//! \file SimpleBranchPred.hpp
5+
//! \brief Class definition of a simple brranch prediction using branch prediction interface
6+
//!
7+
8+
/*
9+
* This file defines the class SimpleBranchPredictor, as well as, a default Prediction
10+
* output class, a default Prediction input class, a defeault Prediction input class
11+
* as required by Olympia's Branch Prediction inteface
12+
* */
13+
#pragma once
14+
15+
#include <cstdint>
16+
#include <map>
17+
#include <limits>
18+
#include "sparta/utils/SpartaAssert.hpp"
19+
#include "BranchPredIF.hpp"
20+
21+
namespace olympia
22+
{
23+
namespace BranchPredictor
24+
{
25+
26+
// following class definitions are example inputs & outputs for a very simple branch
27+
// predictor
28+
class DefaultPrediction
29+
{
30+
public:
31+
// index of branch instruction in the fetch packet
32+
// branch_idx can vary from 0 to (FETCH_WIDTH - 1)
33+
// initialized to default max to catch errors
34+
uint32_t branch_idx = std::numeric_limits<uint32_t>::max();
35+
// predicted target PC
36+
uint64_t predicted_PC = std::numeric_limits<uint64_t>::max();
37+
};
38+
39+
class DefaultUpdate
40+
{
41+
public:
42+
uint64_t fetch_PC = std::numeric_limits<uint64_t>::max();
43+
uint32_t branch_idx = std::numeric_limits<uint32_t>::max();
44+
uint64_t corrected_PC = std::numeric_limits<uint64_t>::max();
45+
bool actually_taken = false;
46+
};
47+
48+
class DefaultInput
49+
{
50+
public:
51+
// PC of first instruction of fetch packet
52+
uint64_t fetch_PC = std::numeric_limits<uint64_t>::max();
53+
};
54+
55+
class BTBEntry
56+
{
57+
public:
58+
// use of BTBEntry in std:map operator [] requires default constructor
59+
BTBEntry() = default;
60+
BTBEntry(uint32_t bidx, uint64_t predPC) :
61+
branch_idx(bidx),
62+
predicted_PC(predPC)
63+
{}
64+
uint32_t branch_idx {std::numeric_limits<uint32_t>::max()};
65+
uint64_t predicted_PC {std::numeric_limits<uint64_t>::max()};
66+
};
67+
68+
// Currently SimpleBranchPredictor works only with uncompressed instructions
69+
// TODO: generalize SimpleBranchPredictor for both compressed and uncompressed instructions
70+
class SimpleBranchPredictor : public BranchPredictorIF<DefaultPrediction, DefaultUpdate, DefaultInput>
71+
{
72+
public:
73+
SimpleBranchPredictor(uint32_t max_fetch_insts) :
74+
max_fetch_insts_(max_fetch_insts)
75+
{}
76+
DefaultPrediction getPrediction(const DefaultInput &);
77+
void updatePredictor(const DefaultUpdate &);
78+
private:
79+
// maximum number of instructions in a FetchPacket
80+
const uint32_t max_fetch_insts_;
81+
// BHT and BTB of SimpleBranchPredictor is unlimited in size
82+
// a map of branch PC to 2 bit staurating counter tracking branch history
83+
std::map <uint64_t, uint8_t> branch_history_table_; // BHT
84+
// a map of branch PC to target of the branch
85+
std::map <uint64_t, BTBEntry> branch_target_buffer_; // BTB
86+
};
87+
88+
} // namespace BranchPredictor
89+
} // namespace olympia

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ add_subdirectory(core/l2cache)
3232
add_subdirectory(core/rename)
3333
add_subdirectory(core/lsu)
3434
add_subdirectory(core/issue_queue)
35+
add_subdirectory(core/branch_pred)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "SimpleBranchPred.hpp"
2+
#include "sparta/utils/SpartaTester.hpp"
3+
4+
TEST_INIT
5+
6+
void runTest(int argc, char **argv)
7+
{
8+
olympia::BranchPredictor::SimpleBranchPredictor predictor(4); //specify max num insts to fetch
9+
10+
olympia::BranchPredictor::DefaultInput input;
11+
input.fetch_PC = 0x0;
12+
13+
// BTB miss
14+
olympia::BranchPredictor::DefaultPrediction prediction = predictor.getPrediction(input);
15+
16+
EXPECT_EQUAL(prediction.branch_idx, 4);
17+
EXPECT_EQUAL(prediction.predicted_PC, 16);
18+
19+
// there was a taken branch at the 3rd instruction from fetchPC, with target 0x100
20+
olympia::BranchPredictor::DefaultUpdate update;
21+
update.fetch_PC = 0x0;
22+
update.branch_idx = 2;
23+
update.corrected_PC = 0x100;
24+
update.actually_taken = true;
25+
predictor.updatePredictor(update);
26+
27+
// try the same input with fetchPC 0x0 again
28+
prediction = predictor.getPrediction(input);
29+
30+
EXPECT_EQUAL(prediction.branch_idx, 2);
31+
EXPECT_EQUAL(prediction.predicted_PC, 0x100);
32+
33+
// TODO: add more tests
34+
35+
}
36+
37+
int main(int argc, char **argv)
38+
{
39+
runTest(argc, argv);
40+
41+
REPORT_ERROR;
42+
return (int)ERROR_CODE;
43+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
project(BranchPred_test)
2+
3+
add_executable(BranchPred_test BranchPred_test.cpp)
4+
target_link_libraries(BranchPred_test core common_test SPARTA::sparta)
5+
6+
sparta_named_test(BranchPred_test_Run BranchPred_test)

0 commit comments

Comments
 (0)