Skip to content

Branch Prediction Unit #243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions core/FTQ.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "FTQ.hpp"

namespace olympia
{
const char* FTQ::name = "ftq";

FTQ::FTQ(sparta::TreeNode* node, const FTQParameterSet* p) :
sparta::Unit(node),
ftq_capacity_(p->ftq_capacity)
{
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(FTQ, sendInitialCreditsToBPU_));

in_bpu_first_prediction_output_.registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(
FTQ, getFirstPrediction_, BranchPredictor::PredictionOutput));

in_bpu_second_prediction_output_.registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(
FTQ, getSecondPrediction_, BranchPredictor::PredictionOutput));

in_fetch_credits_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(FTQ, getFetchCredits_, uint32_t));

/**in_rob_signal_.registerConsumerHandler
(CREATE_SPARTA_HANDLER_WITH_DATA(FTQ, getROBSignal_, uint32_t));
**/
}

FTQ::~FTQ() {}

void FTQ::sendInitialCreditsToBPU_() { sendCreditsToBPU_(5); }

void FTQ::sendCreditsToBPU_(const uint32_t & credits)
{
ILOG("Send " << credits << " credits to BPU");
out_bpu_credits_.send(credits);
}

void FTQ::getFirstPrediction_(const BranchPredictor::PredictionOutput & prediction)
{
if (fetch_target_queue_.size() < ftq_capacity_)
{
ILOG("FTQ receives first PredictionOutput from BPU");
fetch_target_queue_.push_back(prediction);

if(fetch_target_queue_.size() == 1) {
ftq_it = fetch_target_queue_.begin();
}
sendPrediction_();
}
}

void FTQ::getSecondPrediction_(const BranchPredictor::PredictionOutput & prediction)
{
// check if it matches the prediction made by first tier of bpu
ILOG("FTQ receives second PredictionOutput from BPU");
handleMismatch(prediction);
}

#define TAKEN 0
#define NOT_TAKEN 1

void FTQ::handleMismatch(const BranchPredictor::PredictionOutput & tage_prediction)
{
ILOG("Checking mismatch between BasePredictor and TAGE_SC_L");

std::deque<BranchPredictor::PredictionOutput> :: iterator it = fetch_target_queue_.begin();
while(it != fetch_target_queue_.end()) {
if(it->instrPC == tage_prediction.instrPC) {
ILOG("BasePredictor prediction direction: " << it->predDirection_);
ILOG("TAGE_SC_L prediction direction: " << tage_prediction.predDirection_);
if(it->predDirection_ != tage_prediction.predDirection_) {
ILOG("Prediction mismatch between tage and base predictor");

/***
* TODO:
* We need to update predPC_ in case there is an update of output
* How to do it in case a not taken prediction is updated to taken?
*/
if(it->predDirection_ == TAKEN && tage_prediction.predDirection_ == NOT_TAKEN) {
it->predPC_ = it->instrPC + 4;
}
else if(it->predDirection_ == NOT_TAKEN && tage_prediction.predDirection_ == TAKEN) {
// ??
}

it->predDirection_ = tage_prediction.predDirection_;

ftq_it = it;

sendPrediction_();
break;
}
}
}
}

void FTQ::getFetchCredits_(const uint32_t & credits)
{
ILOG("FTQ: Received " << credits << " credits from Fetch")
fetch_credits_ += credits;

sendPrediction_();
}

void FTQ::sendPrediction_()
{
if (fetch_credits_ > 0)
{
// send prediction to Fetch
if (fetch_target_queue_.size() > 0)
{
fetch_credits_--;
ILOG("Send prediction from FTQ to Fetch");
auto output = *ftq_it;
ftq_it++;

out_fetch_prediction_output_.send(output);
}
}
}

void FTQ::firstMispredictionFlush_() {}

void FTQ::getROBSignal_(const uint32_t & signal) {}

void FTQ::deallocateEntry_() {}
} // namespace olympia
88 changes: 88 additions & 0 deletions core/FTQ.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once

#include "sparta/ports/DataPort.hpp"
#include "sparta/events/UniqueEvent.hpp"
#include "sparta/simulation/Unit.hpp"
#include "sparta/simulation/ParameterSet.hpp"
#include "sparta/simulation/TreeNode.hpp"
#include "sparta/log/MessageSource.hpp"
#include "sparta/utils/DataContainer.hpp"

#include "fetch/BPU.hpp"
#include "ROB.hpp"
#include "FlushManager.hpp"

#include <deque>

namespace olympia
{
class FTQ : public sparta::Unit
{
public:
class FTQParameterSet : public sparta::ParameterSet
{
public:
FTQParameterSet(sparta::TreeNode* n) : sparta::ParameterSet(n) {}

// for now set default to 10, chnage it later
PARAMETER(uint32_t, ftq_capacity, 10, "Capacity of fetch target queue")
};

static const char* name;

FTQ(sparta::TreeNode* node, const FTQParameterSet* p);

~FTQ();

private:
const uint32_t ftq_capacity_;

uint32_t fetch_credits_ = 0;
std::deque<BranchPredictor::PredictionOutput> fetch_target_queue_;

// Iterator pointing to next element to send further
std::deque<BranchPredictor::PredictionOutput> :: iterator ftq_it;

sparta::DataInPort<BranchPredictor::PredictionOutput> in_bpu_first_prediction_output_{
&unit_port_set_, "in_bpu_first_prediction_output", 1};
sparta::DataInPort<BranchPredictor::PredictionOutput> in_bpu_second_prediction_output_{
&unit_port_set_, "in_bpu_second_prediction_output", 1};
sparta::DataInPort<uint32_t> in_fetch_credits_{&unit_port_set_, "in_fetch_credits", 1};
/**sparta::DataOutPort<FlushManager::FlushingCriteria> out_first_misprediction_flush_
{&unit_port_set_,
"out_first_misprediction_flush", 1};***/
sparta::DataOutPort<BranchPredictor::PredictionOutput> out_fetch_prediction_output_{
&unit_port_set_, "out_fetch_prediction_output", 1};
/***sparta::DataInPort<bool> in_rob_signal_ {&unit_port_set_, "in_rob_signal", 1}; ***/
sparta::DataOutPort<BranchPredictor::UpdateInput> out_bpu_update_input_{
&unit_port_set_, "out_bpu_update_input", 1};
sparta::DataOutPort<uint32_t> out_bpu_credits_{&unit_port_set_, "out_bpu_credits", 1};

void sendInitialCreditsToBPU_();

void sendCreditsToBPU_(const uint32_t &);

// receives prediction from BasePredictor and pushes it into FTQ
void getFirstPrediction_(const BranchPredictor::PredictionOutput &);

// receives prediction from TAGE_SC_L, checks if there's a mismatch
// updates ftq appropriately
void getSecondPrediction_(const BranchPredictor::PredictionOutput &);

void handleMismatch(const BranchPredictor::PredictionOutput &);

void getFetchCredits_(const uint32_t &);

// continuously send instructions to fetch/icache
void sendPrediction_();

// flushes instruction if first prediction does not matvh second prediction
void firstMispredictionFlush_();

// receives branch resolution signal from ROB at the time of commit
void getROBSignal_(const uint32_t & signal);

// deallocate FTQ entry once branch instruction is committed
void deallocateEntry_();
};
} // namespace olympia
165 changes: 165 additions & 0 deletions core/fetch/BPU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@

#include "fetch/BPU.hpp"

namespace olympia
{
namespace BranchPredictor
{
const char* BPU::name = "bpu";

BPU::BPU(sparta::TreeNode* node, const BPUParameterSet* p) :
sparta::Unit(node),
ghr_size_(p->ghr_size),
ghr_hash_bits_(p->ghr_hash_bits),
pht_size_(p->pht_size),
ctr_bits_(p->ctr_bits),
btb_size_(p->btb_size),
ras_size_(p->ras_size),
ras_enable_overwrite_(p->ras_enable_overwrite),
tage_bim_table_size_(p->tage_bim_table_size),
tage_bim_ctr_bits_(p->tage_bim_ctr_bits),
tage_tagged_table_num_(p->tage_tagged_table_num),
logical_table_num_(p->logical_table_num),
loop_pred_table_size_(p->loop_pred_table_size),
loop_pred_table_way_(p->loop_pred_table_way),
base_predictor_(pht_size_, ctr_bits_, btb_size_, ras_size_, ras_enable_overwrite_),
tage_predictor_(tage_bim_table_size_, tage_bim_ctr_bits_, /*5,*/ 2, 3, 10, 2, 2, 1024,
tage_tagged_table_num_, 10)
{
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(BPU, sendIntitialCreditsToFetch_));

in_fetch_prediction_request_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(BPU, getPredictionRequest_, PredictionRequest));

in_ftq_credits_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(BPU, getCreditsFromFTQ_, uint32_t));

in_ftq_update_input_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(BPU, getUpdateInput_, UpdateInput));
}

BPU::~BPU() {}

PredictionOutput BPU::getPrediction(const PredictionRequest &)
{
PredictionOutput output;
output.predDirection_ = true;
output.predPC_ = 5;
return output;
}

void BPU::updatePredictor(const UpdateInput &) {}

void BPU::getPredictionRequest_(const PredictionRequest & request)
{
predictionRequestBuffer_.push_back(request);
ILOG("BPU received PredictionRequest from Fetch");
}

/***void BPU::makePrediction_() {
ILOG("making prediction");
std::cout << "making prediction\n";
if(predictionRequestBuffer_.size() > ev_make_second_prediction_0) {
//auto input = predictionRequestBuffer_.front();
predictionRequestBuffer_.pop_front();

// call base predictor on input
PredictionOutput output;

output.predDirection_ = true;
output.predPC_ = 100;
generatedPredictionOutputBuffer_.push_back(output);

sendFirstPrediction_();

// call tage_sc_l on input
}
}**/

void BPU::getCreditsFromFTQ_(const uint32_t & credits)
{
ftq_credits_ += credits;
ILOG("BPU received " << credits << " credits from FTQ");
ev_send_first_prediction_.schedule(1);
ev_send_second_prediction_.schedule(4);
}

void BPU::sendFirstPrediction_()
{
// take first PredictionRequest from buffer
if (ftq_credits_ > 0 && predictionRequestBuffer_.size() > 0)
{
PredictionOutput output;

PredictionRequest in = predictionRequestBuffer_.front();

ILOG("Getting direction from base predictor");
bool dir = base_predictor_.getDirection(in.PC_, in.instType_);
ILOG("Getting target from base predictor");
uint64_t target = base_predictor_.getTarget(in.PC_, in.instType_);

output.instrPC = in.PC_;
output.predPC_ = target;
output.predDirection_ = dir;

generatedPredictionOutputBuffer_.push_back(output);

auto firstPrediction = generatedPredictionOutputBuffer_.front();
generatedPredictionOutputBuffer_.pop_front();
ILOG("Sending first PredictionOutput from BPU to FTQ");
out_ftq_first_prediction_output_.send(firstPrediction);
ftq_credits_--;
}
}

void BPU::sendSecondPrediction_()
{
// send prediction made by TAGE_SC_L
PredictionOutput output;

PredictionRequest in = predictionRequestBuffer_.front();

ILOG("Getting direction prediction from TAGE");
output.instrPC = in.PC_;
output.predDirection_ = tage_predictor_.predict(in.PC_);
// TAGE only predicts whether branch will be taken or not, so predPC_ value will be ignored
output.predPC_ = 0;
ILOG("Sending second PredictionOutput from BPU to FTQ");
out_ftq_second_prediction_output_.send(output);
}

void BPU::getUpdateInput_(const UpdateInput & input)
{
// internal_update_input_ = input;

ILOG("BPU received UpdateInput from FTQ");

// updateBPU_(internal_update_input_);
}

/**

void BPU::updateBPU_(const UpdateInput & input) {

// Update internal state of BasePredictor according to UpdateInput received
//base_predictor_.update(input);

// Update internal state of TAGE_SC_L according to UpdateInput received
// TODO
// tage_sc_l.update(input);
}
**/

void BPU::sendCreditsToFetch_(const uint32_t & credits)
{
ILOG("Send " << credits << " credits from BPU to Fetch");
out_fetch_credits_.send(credits);
}

void BPU::sendIntitialCreditsToFetch_() { sendCreditsToFetch_(pred_req_buffer_capacity_); }

void BPU::updateGHRTaken_() {}

void BPU::updateGHRNotTaken_() {}
} // namespace BranchPredictor
} // namespace olympia
Loading
Loading