generated from riscv-software-src/template-riscv-code
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A code introducing branch prediction API.
- Loading branch information
1 parent
9d9a345
commit 8d187b2
Showing
7 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// <Branch.hpp> -*- C++ -*- | ||
|
||
//! | ||
//! \file BranchPred.hpp | ||
//! \brief Definition of Branch Prediction API | ||
//! | ||
|
||
/* | ||
* This file defines the Branch Prediction API. | ||
* The goal is to define an API that is generic and yet flexible enough to support various | ||
* branch prediction microarchitecture. | ||
* To the end, we envision a generic branch predictor as a black box with following inputs | ||
* and outputs: | ||
* * A generic Prediction output | ||
* * A generic Prediction input | ||
* * A generic Update input | ||
* | ||
* The generic branch predictor may have two operations: | ||
* * getPrediction: produces Prediction output based on the Prediction input. | ||
* * updatePredictor: updates Predictor with Update input. | ||
* | ||
* It is intended that an implementation of branch predictor must also specify | ||
* implementations of Prediction output, Prediction input and Update input, along with | ||
* implementations of getPrediction and updatePredictor operations. | ||
* */ | ||
#pragma once | ||
|
||
namespace olympia | ||
{ | ||
namespace BranchPredictor | ||
{ | ||
|
||
template <class PredictionT, class UpdateT, class InputT> | ||
class BranchPredictorIF | ||
{ | ||
public: | ||
// TODO: create constexpr for bytes per compressed and uncompressed inst | ||
static constexpr uint8_t bytes_per_inst = 4; | ||
virtual ~BranchPredictorIF() { }; | ||
virtual PredictionT getPrediction(const InputT &) = 0; | ||
virtual void updatePredictor(const UpdateT &) = 0; | ||
}; | ||
|
||
} // namespace BranchPredictor | ||
} // namespace olympia |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
project (core) | ||
add_library(core | ||
Core.cpp | ||
SimpleBranchPred.cpp | ||
Fetch.cpp | ||
Decode.cpp | ||
Rename.cpp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#include "SimpleBranchPred.hpp" | ||
|
||
/* | ||
* The algorithm used for prediction / update is as follows: | ||
* Prediction: | ||
* - look up BHT to determine if the branch is predicted taken or not | ||
* using 2-bit saturated counter | ||
* - value 3: strongly taken | ||
* - value 2: weakly taken | ||
* - value 1: weakly not taken | ||
* - value 0: strongly not taken | ||
* - look up BTB to see if an entry exists for the input fetch pc | ||
* - if present in BTB and predicted taken, BTB entry is used to determine | ||
* prediction branch idx and predicted_PC | ||
* - if present in BTB but predicted not taken, BTB entry is used to determine | ||
* prediction branch idx, while predicted_PC is the fall through addr | ||
* - if not present in BTB entry, prediction branch idx is the last instr of | ||
* the FetchPacket, while predicted PC is the fall through addr. Also, create | ||
* a new BTB entry | ||
* Update: | ||
* - a valid BTB entry must be present for fetch PC | ||
* - TBD | ||
* | ||
*/ | ||
namespace olympia | ||
{ | ||
namespace BranchPredictor | ||
{ | ||
|
||
void SimpleBranchPredictor::updatePredictor(const DefaultUpdate & update) { | ||
|
||
sparta_assert(branch_target_buffer_.find(update.fetch_PC) != branch_target_buffer_.end()); | ||
branch_target_buffer_[update.fetch_PC].branch_idx = update.branch_idx; | ||
if (update.actually_taken) { | ||
branch_history_table_[update.fetch_PC] = | ||
(branch_history_table_[update.fetch_PC] == 3) ? 3 : | ||
branch_history_table_[update.fetch_PC] + 1; | ||
branch_target_buffer_[update.fetch_PC].predicted_PC = update.corrected_PC; | ||
} else { | ||
branch_history_table_[update.fetch_PC] = | ||
(branch_history_table_[update.fetch_PC] == 0) ? 0 : | ||
branch_history_table_[update.fetch_PC] - 1; | ||
} | ||
} | ||
|
||
DefaultPrediction SimpleBranchPredictor::getPrediction(const DefaultInput & input) { | ||
bool predictTaken = false; | ||
if (branch_history_table_.find(input.fetch_PC) != branch_history_table_.end()) { | ||
predictTaken = (branch_history_table_[input.fetch_PC] > 1); | ||
} else { | ||
// add a new entry to BHT, biased towards not taken | ||
branch_history_table_.insert(std::pair<uint64_t, uint8_t>(input.fetch_PC, 1)); | ||
} | ||
|
||
DefaultPrediction prediction; | ||
if (branch_target_buffer_.find(input.fetch_PC) != branch_target_buffer_.end()) { | ||
// BTB hit | ||
const BTBEntry & btb_entry = branch_target_buffer_[input.fetch_PC]; | ||
prediction.branch_idx = btb_entry.branch_idx; | ||
if (predictTaken) { | ||
prediction.predicted_PC = btb_entry.predicted_PC; | ||
} else { | ||
// fall through address | ||
prediction.predicted_PC = input.fetch_PC + prediction.branch_idx + BranchPredictorIF::bytes_per_inst; | ||
} | ||
} else { | ||
// BTB miss | ||
prediction.branch_idx = max_fetch_insts_; | ||
prediction.predicted_PC = input.fetch_PC + max_fetch_insts_ * bytes_per_inst; | ||
// add new entry to BTB | ||
branch_target_buffer_.insert(std::pair<uint64_t,BTBEntry>( | ||
input.fetch_PC, BTBEntry(prediction.branch_idx, prediction.predicted_PC))); | ||
} | ||
|
||
return prediction; | ||
} | ||
|
||
} // namespace BranchPredictor | ||
} // namespace olympia |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// <SimpleBranchPred.hpp> -*- C++ -*- | ||
|
||
//! | ||
//! \file SimpleBranchPred.hpp | ||
//! \brief Class definition of a simple brranch prediction using branch prediction interface | ||
//! | ||
|
||
/* | ||
* This file defines the class SimpleBranchPredictor, as well as, a default Prediction | ||
* output class, a default Prediction input class, a defeault Prediction input class | ||
* as required by Olympia's Branch Prediction inteface | ||
* */ | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <map> | ||
#include <limits> | ||
#include "sparta/utils/SpartaAssert.hpp" | ||
#include "BranchPredIF.hpp" | ||
|
||
namespace olympia | ||
{ | ||
namespace BranchPredictor | ||
{ | ||
|
||
// following class definitions are example inputs & outputs for a very simple branch | ||
// predictor | ||
class DefaultPrediction | ||
{ | ||
public: | ||
// index of branch instruction in the fetch packet | ||
// branch_idx can vary from 0 to (FETCH_WIDTH - 1) | ||
// initialized to default max to catch errors | ||
uint32_t branch_idx = std::numeric_limits<uint32_t>::max(); | ||
// predicted target PC | ||
uint64_t predicted_PC = std::numeric_limits<uint64_t>::max(); | ||
}; | ||
|
||
class DefaultUpdate | ||
{ | ||
public: | ||
uint64_t fetch_PC = std::numeric_limits<uint64_t>::max(); | ||
uint32_t branch_idx = std::numeric_limits<uint32_t>::max(); | ||
uint64_t corrected_PC = std::numeric_limits<uint64_t>::max(); | ||
bool actually_taken = false; | ||
}; | ||
|
||
class DefaultInput | ||
{ | ||
public: | ||
// PC of first instruction of fetch packet | ||
uint64_t fetch_PC = std::numeric_limits<uint64_t>::max(); | ||
}; | ||
|
||
class BTBEntry | ||
{ | ||
public: | ||
// use of BTBEntry in std:map operator [] requires default constructor | ||
BTBEntry() = default; | ||
BTBEntry(uint32_t bidx, uint64_t predPC) : | ||
branch_idx(bidx), | ||
predicted_PC(predPC) | ||
{} | ||
uint32_t branch_idx {std::numeric_limits<uint32_t>::max()}; | ||
uint64_t predicted_PC {std::numeric_limits<uint64_t>::max()}; | ||
}; | ||
|
||
// Currently SimpleBranchPredictor works only with uncompressed instructions | ||
// TODO: generalize SimpleBranchPredictor for both compressed and uncompressed instructions | ||
class SimpleBranchPredictor : public BranchPredictorIF<DefaultPrediction, DefaultUpdate, DefaultInput> | ||
{ | ||
public: | ||
SimpleBranchPredictor(uint32_t max_fetch_insts) : | ||
max_fetch_insts_(max_fetch_insts) | ||
{} | ||
DefaultPrediction getPrediction(const DefaultInput &); | ||
void updatePredictor(const DefaultUpdate &); | ||
private: | ||
// maximum number of instructions in a FetchPacket | ||
const uint32_t max_fetch_insts_; | ||
// BHT and BTB of SimpleBranchPredictor is unlimited in size | ||
// a map of branch PC to 2 bit staurating counter tracking branch history | ||
std::map <uint64_t, uint8_t> branch_history_table_; // BHT | ||
// a map of branch PC to target of the branch | ||
std::map <uint64_t, BTBEntry> branch_target_buffer_; // BTB | ||
}; | ||
|
||
} // namespace BranchPredictor | ||
} // namespace olympia |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include "SimpleBranchPred.hpp" | ||
#include "sparta/utils/SpartaTester.hpp" | ||
|
||
TEST_INIT | ||
|
||
void runTest(int argc, char **argv) | ||
{ | ||
olympia::BranchPredictor::SimpleBranchPredictor predictor(4); //specify max num insts to fetch | ||
|
||
olympia::BranchPredictor::DefaultInput input; | ||
input.fetch_PC = 0x0; | ||
|
||
// BTB miss | ||
olympia::BranchPredictor::DefaultPrediction prediction = predictor.getPrediction(input); | ||
|
||
EXPECT_EQUAL(prediction.branch_idx, 4); | ||
EXPECT_EQUAL(prediction.predicted_PC, 16); | ||
|
||
// there was a taken branch at the 3rd instruction from fetchPC, with target 0x100 | ||
olympia::BranchPredictor::DefaultUpdate update; | ||
update.fetch_PC = 0x0; | ||
update.branch_idx = 2; | ||
update.corrected_PC = 0x100; | ||
update.actually_taken = true; | ||
predictor.updatePredictor(update); | ||
|
||
// try the same input with fetchPC 0x0 again | ||
prediction = predictor.getPrediction(input); | ||
|
||
EXPECT_EQUAL(prediction.branch_idx, 2); | ||
EXPECT_EQUAL(prediction.predicted_PC, 0x100); | ||
|
||
// TODO: add more tests | ||
|
||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
runTest(argc, argv); | ||
|
||
REPORT_ERROR; | ||
return (int)ERROR_CODE; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
project(BranchPred_test) | ||
|
||
add_executable(BranchPred_test BranchPred_test.cpp) | ||
target_link_libraries(BranchPred_test core common_test SPARTA::sparta) | ||
|
||
sparta_named_test(BranchPred_test_Run BranchPred_test) |