From 52f6f964b381362a1966bb238b94bd419f680744 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:51:36 +0200 Subject: [PATCH 01/20] Adding PerceptronPredictor class and rebasing --- configs/DEMO_RISCV.yaml | 7 +- configs/a64fx.yaml | 5 +- configs/a64fx_SME.yaml | 5 +- configs/m1_firestorm.yaml | 9 +- configs/tx2.yaml | 5 +- src/include/simeng/CoreInstance.hh | 1 + src/include/simeng/PerceptronPredictor.hh | 79 ++++++++++++ src/lib/CMakeLists.txt | 2 + src/lib/CoreInstance.cc | 7 +- src/lib/PerceptronPredictor.cc | 145 ++++++++++++++++++++++ 10 files changed, 246 insertions(+), 19 deletions(-) create mode 100644 src/include/simeng/PerceptronPredictor.hh create mode 100644 src/lib/PerceptronPredictor.cc diff --git a/configs/DEMO_RISCV.yaml b/configs/DEMO_RISCV.yaml index 5f3387ef1f..1cd86d8646 100644 --- a/configs/DEMO_RISCV.yaml +++ b/configs/DEMO_RISCV.yaml @@ -26,11 +26,10 @@ Queue-Sizes: Load: 64 Store: 36 Branch-Predictor: + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 10 - RAS-entries: 5 - Fallback-Static-Predictor: "Always-Taken" + Global-History-Length: 19 + RAS-entries: 1 L1-Data-Memory: Interface-Type: Fixed L1-Instruction-Memory: diff --git a/configs/a64fx.yaml b/configs/a64fx.yaml index 2170e22b21..36d09a42c9 100644 --- a/configs/a64fx.yaml +++ b/configs/a64fx.yaml @@ -29,11 +29,10 @@ Queue-Sizes: Load: 40 Store: 24 Branch-Predictor: + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 11 + Global-History-Length: 19 RAS-entries: 8 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: Fixed L1-Instruction-Memory: diff --git a/configs/a64fx_SME.yaml b/configs/a64fx_SME.yaml index 6be2b171cd..7b1442cc32 100644 --- a/configs/a64fx_SME.yaml +++ b/configs/a64fx_SME.yaml @@ -31,11 +31,10 @@ Queue-Sizes: Load: 40 Store: 24 Branch-Predictor: + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 11 + Global-History-Length: 19 RAS-entries: 8 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: Fixed L1-Instruction-Memory: diff --git a/configs/m1_firestorm.yaml b/configs/m1_firestorm.yaml index 901ed5ad5a..a593500685 100644 --- a/configs/m1_firestorm.yaml +++ b/configs/m1_firestorm.yaml @@ -25,11 +25,10 @@ Queue-Sizes: Load: 130 Store: 60 Branch-Predictor: - BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 11 - RAS-entries: 8 - Fallback-Static-Predictor: "Always-Taken" + Type: "Perceptron" + BTB-Tag-Bits: 11 + Global-History-Length: 19 + RAS-entries: 8 L1-Data-Memory: Interface-Type: Fixed L1-Instruction-Memory: diff --git a/configs/tx2.yaml b/configs/tx2.yaml index 5c57440111..a5e28807f9 100644 --- a/configs/tx2.yaml +++ b/configs/tx2.yaml @@ -27,11 +27,10 @@ Queue-Sizes: Load: 64 Store: 36 Branch-Predictor: + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 10 + Global-History-Length: 19 RAS-entries: 5 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: Fixed L1-Instruction-Memory: diff --git a/src/include/simeng/CoreInstance.hh b/src/include/simeng/CoreInstance.hh index 2e7b923a65..8b687cab7e 100644 --- a/src/include/simeng/CoreInstance.hh +++ b/src/include/simeng/CoreInstance.hh @@ -8,6 +8,7 @@ #include "simeng/FixedLatencyMemoryInterface.hh" #include "simeng/FlatMemoryInterface.hh" #include "simeng/GenericPredictor.hh" +#include "simeng/PerceptronPredictor.hh" #include "simeng/SpecialFileDirGen.hh" #include "simeng/arch/Architecture.hh" #include "simeng/arch/aarch64/Architecture.hh" diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh new file mode 100644 index 0000000000..a8f1458751 --- /dev/null +++ b/src/include/simeng/PerceptronPredictor.hh @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +#include "simeng/BranchPredictor.hh" +#include "simeng/config/SimInfo.hh" + +namespace simeng { + +/** A Perceptron branch predictor implementing the branch predictor described in + * Jimenez and Lin ("Dynamic branch prediction with perceptrons", IEEE High- + * Performance Computer Architecture Symposium Proceedings (2001), 197-206. + * The following predictors have been included: + * + * - Static predictor based on pre-allocated branch type. + * + * - A Branch Target Buffer (BTB) with a local and global indexing scheme and a + * perceptron. + * + * - A Return Address Stack (RAS) is also in use. + */ + +class PerceptronPredictor : public BranchPredictor { + public: + /** Initialise predictor models. */ + PerceptronPredictor(ryml::ConstNodeRef config = config::SimInfo::getConfig()); + ~PerceptronPredictor(); + + /** Generate a branch prediction for the supplied instruction address, a + * branch type, and a known branch offset; defaults to 0 meaning offset is not + * known. Returns a branch direction and branch target address. */ + BranchPrediction predict(uint64_t address, BranchType type, + int64_t knownOffset = 0) override; + + /** Updates appropriate predictor model objects based on the address and + * outcome of the branch instruction. */ + void update(uint64_t address, bool taken, uint64_t targetAddress, + BranchType type) override; + + /** Provides RAS rewinding behaviour. */ + void flush(uint64_t address) override; + + private: + /** The bitlength of the BTB index; BTB will have 2^bits entries. */ + uint64_t btbBits_; + + /** A 2^bits length vector of pairs containing a (globalHistoryLength_ + 1) + * length perceptron and a branch target. */ + std::vector, uint64_t>> btb_; + + /** The previous global history for an address. */ + std::map btbHistory_; + + /** An n-bit history of previous branch directions where n is equal to + * globalHistoryLength_. */ + uint64_t globalHistory_ = 0; + + /** The number of previous branch directions recorded globally. */ + uint64_t globalHistoryLength_; + + /** A return address stack. */ + std::deque ras_; + + /** RAS history with instruction address as the keys. A non-zero value + * represents the target prediction for a return instruction and a 0 entry for + * a branch-and-link instruction. */ + std::map rasHistory_; + + /** The size of the RAS. */ + uint64_t rasSize_; + + /** The magnitude of the dot product of the perceptron and the global history, + * below which the perceptron's weight must be updated */ + uint64_t trainingThreshold_; +}; + +} // namespace simeng diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 4703ed5e88..aa4e326f0b 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -45,6 +45,8 @@ set(SIMENG_SOURCES RegisterFileSet.cc RegisterValue.cc SpecialFileDirGen.cc + ../include/simeng/PerceptronPredictor.hh + PerceptronPredictor.cc ) configure_file(${capstone_SOURCE_DIR}/arch/AArch64/AArch64GenInstrInfo.inc AArch64GenInstrInfo.inc COPYONLY) diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index af4ee73b9d..2029ed86a1 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -220,7 +220,12 @@ void CoreInstance::createCore() { } // Construct branch predictor object - predictor_ = std::make_unique(); + std::string predictorType = config_["Branch-Predictor"]["Type"].as(); + if (predictorType == "Generic") { + predictor_ = std::make_unique(); + } else if (predictorType == "Perceptron") { + predictor_ = std::make_unique(); + } // Extract the port arrangement from the config file auto config_ports = config_["Ports"]; diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc new file mode 100644 index 0000000000..9b70bbad22 --- /dev/null +++ b/src/lib/PerceptronPredictor.cc @@ -0,0 +1,145 @@ +#include "simeng/PerceptronPredictor.hh" + + +namespace simeng { + +PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) + : btbBits_(config["Branch-Predictor"]["BTB-Tag-Bits"].as()), + globalHistoryLength_(config["Branch-Predictor"]["Global-History-Length"].as()), + rasSize_(config["Branch-Predictor"]["RAS-entries"].as()) { + // Build BTB based on config options + btb_.resize(1 << (btbBits_)); + for (int i = 0; i < (1 << (btbBits_)); i++) { + btb_[i].first.assign(globalHistoryLength_, 0); + btb_[i].first.push_back(1); + btb_[i].second = 0; + } + + // Set up training threshold according to empirically determined formula + trainingThreshold_ = (int)((1.93 * (globalHistoryLength_)) + 14); +} + +PerceptronPredictor::~PerceptronPredictor() { + ras_.clear(); + rasHistory_.clear(); +} + +BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, + int64_t knownOffset) { + // Get index via an XOR hash between the global history and the lower btbBits_ + // bits of the instruction address + uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); + + // Store the global history for correct hashing in update() + btbHistory_[address] = globalHistory_; + + // Retrieve the perceptron from the BTB + std::vector perceptron = btb_[hashedIndex].first; + + // Determine direction prediction from perceptron, starting with the bias weight + int64_t weights = perceptron[globalHistoryLength_]; + for (int i = 0; i < globalHistoryLength_; i++) { + bool historyTaken = + ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); + weights += historyTaken ? perceptron[i] : (0 - perceptron[i]); + } + bool direction = (weights >= 0); + + // Retrieve target prediction + uint64_t target = + (knownOffset != 0) ? address + knownOffset : btb_[hashedIndex].second; + + BranchPrediction prediction = {direction, target}; + + // Ammend prediction based on branch type + if (type == BranchType::Unconditional) { + prediction.taken = true; + } else if (type == BranchType::Return) { + prediction.taken = true; + // Return branches can use the RAS if an entry is available + if (ras_.size() > 0) { + prediction.target = ras_.back(); + // Record top of RAS used for target prediction + rasHistory_[address] = ras_.back(); + ras_.pop_back(); + } + } else if (type == BranchType::SubroutineCall) { + prediction.taken = true; + // Subroutine call branches must push their associated return address to RAS + if (ras_.size() >= rasSize_) { + ras_.pop_front(); + } + ras_.push_back(address + 4); + // Record that this address is a branch-and-link instruction + rasHistory_[address] = 0; + } else if (type == BranchType::Conditional) { + if (!prediction.taken) prediction.target = address + 4; + } + return prediction; +} + +void PerceptronPredictor::update(uint64_t address, bool taken, + uint64_t targetAddress, BranchType type) { + uint64_t prevGlobalHistory = btbHistory_[address]; + uint64_t hashedIndex = + ((address >> 2) ^ prevGlobalHistory) & ((1 << btbBits_) - 1); + + std::vector perceptron = btb_[hashedIndex].first; + + int64_t weights = perceptron[globalHistoryLength_]; + for (int i = 0; i < globalHistoryLength_; i++) { + bool historyTaken = + ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); + weights += historyTaken ? perceptron[i] : (0 - perceptron[i]); + } + + bool directionPrediction = (weights >= 0); + uint64_t magnitude = (weights < 0) ? (0 - weights) : weights; + + if ((directionPrediction != taken) || (magnitude < trainingThreshold_)) { + int8_t t = (taken) ? 1 : -1; + + for (int i = 0; i < globalHistoryLength_; i++) { + int8_t xi = + ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) ? -1 : 1; + int8_t product_xi_t = xi * t; + // Make sure no overflow + if ((product_xi_t > 0 && perceptron[i] < 127) || (product_xi_t < 0 && perceptron[i] > -127)) { + perceptron[i] += product_xi_t; + } + } + perceptron[globalHistoryLength_] += t; + } + + btb_[hashedIndex].first = perceptron; + btb_[hashedIndex].second = targetAddress; + + globalHistory_ = + ((globalHistory_ << 1) | taken) & ((1 << globalHistoryLength_) - 1); + return; +} + +void PerceptronPredictor::flush(uint64_t address) { + // If address interacted with RAS, rewind entry + auto it = rasHistory_.find(address); + if (it != rasHistory_.end()) { + uint64_t target = it->second; + if (target != 0) { + // If history entry belongs to a return instruction, push target back onto + // stack + if (ras_.size() >= rasSize_) { + ras_.pop_front(); + } + ras_.push_back(target); + } else { + // If history entry belongs to a branch-and-link instruction, pop target + // off of stack + if (ras_.size()) { + ras_.pop_back(); + } + } + rasHistory_.erase(it); + } +} + +} // namespace simeng From fec45b74dc2c912e97719a1c922cf397fc0a1641 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:48:16 +0200 Subject: [PATCH 02/20] Updating default config options to include Branch Predictor Type --- src/lib/CoreInstance.cc | 1 + src/lib/config/ModelConfig.cc | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index 2029ed86a1..cf18379030 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -220,6 +220,7 @@ void CoreInstance::createCore() { } // Construct branch predictor object + std::cout << "building BP" << std::endl; std::string predictorType = config_["Branch-Predictor"]["Type"].as(); if (predictorType == "Generic") { predictor_ = std::make_unique(); diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index 697c14c7c4..8c293f8fb1 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -424,6 +424,11 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_.addChild( ExpectationNode::createExpectation("Branch-Predictor")); + expectations_["Branch-Predictor"].addChild( + ExpectationNode::createExpectation("Perceptron", "Type")); + expectations_["Branch-Predictor"]["Type"].setValueSet( + std::vector{"Generic", "Perceptron"}); + expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation(8, "BTB-Tag-Bits")); expectations_["Branch-Predictor"]["BTB-Tag-Bits"].setValueBounds(1, From b0afe42277fdb32c0febae3f08e9cc66c10273a1 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:07:34 +0200 Subject: [PATCH 03/20] Shuffling order of declarations in PerceptronPRedictor.hh --- src/include/simeng/PerceptronPredictor.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index a8f1458751..b05b19845b 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -60,6 +60,10 @@ class PerceptronPredictor : public BranchPredictor { /** The number of previous branch directions recorded globally. */ uint64_t globalHistoryLength_; + /** The magnitude of the dot product of the perceptron and the global history, + * below which the perceptron's weight must be updated */ + uint64_t trainingThreshold_; + /** A return address stack. */ std::deque ras_; @@ -70,10 +74,6 @@ class PerceptronPredictor : public BranchPredictor { /** The size of the RAS. */ uint64_t rasSize_; - - /** The magnitude of the dot product of the perceptron and the global history, - * below which the perceptron's weight must be updated */ - uint64_t trainingThreshold_; }; } // namespace simeng From 68387e0efc355aa97784b7aee691725e168d841c Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:34:19 +0200 Subject: [PATCH 04/20] Shuffling order of declarations in PerceptronPRedictor.hh --- src/lib/CoreInstance.cc | 1 - src/lib/PerceptronPredictor.cc | 12 +++++++++++- src/lib/config/ModelConfig.cc | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index cf18379030..2029ed86a1 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -220,7 +220,6 @@ void CoreInstance::createCore() { } // Construct branch predictor object - std::cout << "building BP" << std::endl; std::string predictorType = config_["Branch-Predictor"]["Type"].as(); if (predictorType == "Generic") { predictor_ = std::make_unique(); diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index 9b70bbad22..c4e55adcdb 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -1,19 +1,29 @@ #include "simeng/PerceptronPredictor.hh" +// ToDo -- remove this include +#include namespace simeng { PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) : btbBits_(config["Branch-Predictor"]["BTB-Tag-Bits"].as()), - globalHistoryLength_(config["Branch-Predictor"]["Global-History-Length"].as()), + globalHistoryLength_( + config["Branch-Predictor"]["Global-History-Length"].as()), rasSize_(config["Branch-Predictor"]["RAS-entries"].as()) { // Build BTB based on config options btb_.resize(1 << (btbBits_)); +// std::cout << "BTB size is " << btb_.capacity() << std::endl; for (int i = 0; i < (1 << (btbBits_)); i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); btb_[i].second = 0; } +// std::cout << "First perceptron:" << std::endl << "{"; +// for (int i = 0; i < (globalHistoryLength_); i++) { +// std::cout << (int)btb_[0].first[i] << ", "; +// } +// std::cout << (int)btb_[0].first[globalHistoryLength_] << "}" << std::endl; + // Set up training threshold according to empirically determined formula trainingThreshold_ = (int)((1.93 * (globalHistoryLength_)) + 14); diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index 8c293f8fb1..b9b6c55bd3 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -435,7 +435,7 @@ void ModelConfig::setExpectations(bool isDefault) { 64); expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation(2, "Saturating-Count-Bits")); + ExpectationNode::createExpectation(2, "Saturating-Count-Bits", true)); expectations_["Branch-Predictor"]["Saturating-Count-Bits"] .setValueBounds(1, 64); @@ -451,7 +451,7 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation( - "Always-Taken", "Fallback-Static-Predictor")); + "Always-Taken", "Fallback-Static-Predictor", true)); expectations_["Branch-Predictor"]["Fallback-Static-Predictor"].setValueSet( std::vector{"Always-Taken", "Always-Not-Taken"}); From bc95bcea30d3234ca59802ab1ba78fdb27fe509a Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:36:02 +0200 Subject: [PATCH 05/20] debugging -- squash this commit --- src/include/simeng/PerceptronPredictor.hh | 2 +- src/lib/CoreInstance.cc | 3 ++ src/lib/PerceptronPredictor.cc | 36 ++++++++++++----------- src/lib/config/ModelConfig.cc | 3 +- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index b05b19845b..5a764dfa50 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -50,7 +50,7 @@ class PerceptronPredictor : public BranchPredictor { * length perceptron and a branch target. */ std::vector, uint64_t>> btb_; - /** The previous global history for an address. */ + /** The previous hashed index for an address. */ std::map btbHistory_; /** An n-bit history of previous branch directions where n is equal to diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index 2029ed86a1..fc77f9a7ea 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -220,10 +220,13 @@ void CoreInstance::createCore() { } // Construct branch predictor object + // todo -- remove print statements std::string predictorType = config_["Branch-Predictor"]["Type"].as(); if (predictorType == "Generic") { + std::cout << "Branch Predictor Type = Generic" << std::endl; predictor_ = std::make_unique(); } else if (predictorType == "Perceptron") { + std::cout << "Branch Predictor Type = Perceptron" << std::endl; predictor_ = std::make_unique(); } diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index c4e55adcdb..b61e4112b4 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -12,21 +12,17 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) rasSize_(config["Branch-Predictor"]["RAS-entries"].as()) { // Build BTB based on config options btb_.resize(1 << (btbBits_)); -// std::cout << "BTB size is " << btb_.capacity() << std::endl; for (int i = 0; i < (1 << (btbBits_)); i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); btb_[i].second = 0; } -// std::cout << "First perceptron:" << std::endl << "{"; -// for (int i = 0; i < (globalHistoryLength_); i++) { -// std::cout << (int)btb_[0].first[i] << ", "; -// } -// std::cout << (int)btb_[0].first[globalHistoryLength_] << "}" << std::endl; - // Set up training threshold according to empirically determined formula trainingThreshold_ = (int)((1.93 * (globalHistoryLength_)) + 14); + + // ToDo -- remove print statement + std::cout << "Making Perceptron Predictor" << std::endl; } PerceptronPredictor::~PerceptronPredictor() { @@ -40,22 +36,24 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, // bits of the instruction address uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); - // Store the global history for correct hashing in update() + // Store the global history for correct hashing in update() -- + // needs to be global history and not the hashed index as hashing looses information at longer + // global history lengths btbHistory_[address] = globalHistory_; // Retrieve the perceptron from the BTB std::vector perceptron = btb_[hashedIndex].first; // Determine direction prediction from perceptron, starting with the bias weight - int64_t weights = perceptron[globalHistoryLength_]; + int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { bool historyTaken = ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); - weights += historyTaken ? perceptron[i] : (0 - perceptron[i]); + Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); } - bool direction = (weights >= 0); - + bool direction = (Pout >= 0); // Retrieve target prediction + uint64_t target = (knownOffset != 0) ? address + knownOffset : btb_[hashedIndex].second; @@ -96,16 +94,20 @@ void PerceptronPredictor::update(uint64_t address, bool taken, std::vector perceptron = btb_[hashedIndex].first; - int64_t weights = perceptron[globalHistoryLength_]; + // Work out the most recent prediction + int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { bool historyTaken = - ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); - weights += historyTaken ? perceptron[i] : (0 - perceptron[i]); + ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) != 0); + Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); } + bool directionPrediction = (Pout >= 0); - bool directionPrediction = (weights >= 0); - uint64_t magnitude = (weights < 0) ? (0 - weights) : weights; + // Determine the magnitude of the dot product for training + uint64_t magnitude = (Pout < 0) ? (0 - Pout) : Pout; + // update the perceptron if the prediction was wrong, or the dot product's magnitude + // was not greater than the training threshold if ((directionPrediction != taken) || (magnitude < trainingThreshold_)) { int8_t t = (taken) ? 1 : -1; diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index b9b6c55bd3..21642ade15 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -435,7 +435,8 @@ void ModelConfig::setExpectations(bool isDefault) { 64); expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation(2, "Saturating-Count-Bits", true)); + ExpectationNode::createExpectation(2, "Saturating-Count-Bits", + true)); expectations_["Branch-Predictor"]["Saturating-Count-Bits"] .setValueBounds(1, 64); From 8f54745be0bcd96b374e92284400d23581a20c30 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:38:14 +0100 Subject: [PATCH 06/20] Sorting out tests Adding tests for perceptron predictor comparable to generic predictor tests, and amending existing tests which are effected by the changes to the BP config structure --- configs/sst-cores/a64fx-sst.yaml | 1 + configs/sst-cores/m1_firestorm-sst.yaml | 1 + configs/sst-cores/tx2-sst.yaml | 1 + src/lib/CMakeLists.txt | 3 +- src/lib/CoreInstance.cc | 4 - src/lib/PerceptronPredictor.cc | 23 ++-- src/lib/config/ModelConfig.cc | 3 +- test/integration/ConfigTest.cc | 6 +- test/unit/CMakeLists.txt | 1 + test/unit/GenericPredictorTest.cc | 30 ++--- test/unit/PerceptronPredictorTest.cc | 148 ++++++++++++++++++++++++ 11 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 test/unit/PerceptronPredictorTest.cc diff --git a/configs/sst-cores/a64fx-sst.yaml b/configs/sst-cores/a64fx-sst.yaml index 3bb2d3f499..03b1aca479 100644 --- a/configs/sst-cores/a64fx-sst.yaml +++ b/configs/sst-cores/a64fx-sst.yaml @@ -29,6 +29,7 @@ Queue-Sizes: Load: 40 Store: 24 Branch-Predictor: + Type: "Generic" BTB-Tag-Bits: 11 Saturating-Count-Bits: 2 Global-History-Length: 11 diff --git a/configs/sst-cores/m1_firestorm-sst.yaml b/configs/sst-cores/m1_firestorm-sst.yaml index 9ca033bf71..918581b160 100644 --- a/configs/sst-cores/m1_firestorm-sst.yaml +++ b/configs/sst-cores/m1_firestorm-sst.yaml @@ -25,6 +25,7 @@ Queue-Sizes: Load: 130 Store: 60 Branch-Predictor: + Type: "Generic" BTB-Tag-Bits: 11 Saturating-Count-Bits: 2 Global-History-Length: 11 diff --git a/configs/sst-cores/tx2-sst.yaml b/configs/sst-cores/tx2-sst.yaml index b5d4fbf034..a367915d67 100644 --- a/configs/sst-cores/tx2-sst.yaml +++ b/configs/sst-cores/tx2-sst.yaml @@ -27,6 +27,7 @@ Queue-Sizes: Load: 64 Store: 36 Branch-Predictor: + Type: "Generic" BTB-Tag-Bits: 11 Saturating-Count-Bits: 2 Global-History-Length: 10 diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index aa4e326f0b..d5e20a6421 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -42,11 +42,10 @@ set(SIMENG_SOURCES FlatMemoryInterface.cc GenericPredictor.cc Instruction.cc + PerceptronPredictor.cc RegisterFileSet.cc RegisterValue.cc SpecialFileDirGen.cc - ../include/simeng/PerceptronPredictor.hh - PerceptronPredictor.cc ) configure_file(${capstone_SOURCE_DIR}/arch/AArch64/AArch64GenInstrInfo.inc AArch64GenInstrInfo.inc COPYONLY) diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index fc77f9a7ea..fdd6b86765 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -219,14 +219,10 @@ void CoreInstance::createCore() { arch_ = std::make_unique(kernel_); } - // Construct branch predictor object - // todo -- remove print statements std::string predictorType = config_["Branch-Predictor"]["Type"].as(); if (predictorType == "Generic") { - std::cout << "Branch Predictor Type = Generic" << std::endl; predictor_ = std::make_unique(); } else if (predictorType == "Perceptron") { - std::cout << "Branch Predictor Type = Perceptron" << std::endl; predictor_ = std::make_unique(); } diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index b61e4112b4..24b0ef715c 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -1,8 +1,5 @@ #include "simeng/PerceptronPredictor.hh" -// ToDo -- remove this include -#include - namespace simeng { PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) @@ -11,8 +8,9 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) config["Branch-Predictor"]["Global-History-Length"].as()), rasSize_(config["Branch-Predictor"]["RAS-entries"].as()) { // Build BTB based on config options - btb_.resize(1 << (btbBits_)); - for (int i = 0; i < (1 << (btbBits_)); i++) { + uint32_t btbSize = (1 << btbBits_); + btb_.resize(btbSize); + for (int i = 0; i < btbSize; i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); btb_[i].second = 0; @@ -20,9 +18,6 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) // Set up training threshold according to empirically determined formula trainingThreshold_ = (int)((1.93 * (globalHistoryLength_)) + 14); - - // ToDo -- remove print statement - std::cout << "Making Perceptron Predictor" << std::endl; } PerceptronPredictor::~PerceptronPredictor() { @@ -31,7 +26,7 @@ PerceptronPredictor::~PerceptronPredictor() { } BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, - int64_t knownOffset) { + int64_t knownOffset) { // Get index via an XOR hash between the global history and the lower btbBits_ // bits of the instruction address uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); @@ -44,7 +39,9 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, // Retrieve the perceptron from the BTB std::vector perceptron = btb_[hashedIndex].first; - // Determine direction prediction from perceptron, starting with the bias weight + // Determine direction prediction from perceptron by taking its dot product + // with the global history + // Starting with the bias weight int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { bool historyTaken = @@ -87,7 +84,7 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, } void PerceptronPredictor::update(uint64_t address, bool taken, - uint64_t targetAddress, BranchType type) { + uint64_t targetAddress, BranchType type) { uint64_t prevGlobalHistory = btbHistory_[address]; uint64_t hashedIndex = ((address >> 2) ^ prevGlobalHistory) & ((1 << btbBits_) - 1); @@ -116,9 +113,7 @@ void PerceptronPredictor::update(uint64_t address, bool taken, ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) ? -1 : 1; int8_t product_xi_t = xi * t; // Make sure no overflow - if ((product_xi_t > 0 && perceptron[i] < 127) || (product_xi_t < 0 && perceptron[i] > -127)) { - perceptron[i] += product_xi_t; - } + perceptron[i] += (perceptron[i] != 127 && perceptron[i] != -127) ? product_xi_t : 0; } perceptron[globalHistoryLength_] += t; } diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index 21642ade15..41e6020506 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -425,7 +425,8 @@ void ModelConfig::setExpectations(bool isDefault) { ExpectationNode::createExpectation("Branch-Predictor")); expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation("Perceptron", "Type")); + ExpectationNode::createExpectation( + "Generic", "Type")); expectations_["Branch-Predictor"]["Type"].setValueSet( std::vector{"Generic", "Perceptron"}); diff --git a/test/integration/ConfigTest.cc b/test/integration/ConfigTest.cc index ed0f4124de..296e764f6d 100644 --- a/test/integration/ConfigTest.cc +++ b/test/integration/ConfigTest.cc @@ -49,7 +49,7 @@ TEST(ConfigTest, Default) { "'FloatingPoint/SVE-Count': 32\n 'Predicate-Count': 17\n " "'Conditional-Count': 1\n 'Matrix-Count': 1\n'Pipeline-Widths':\n " "Commit: 1\n FrontEnd: 1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: " - "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n 'BTB-Tag-Bits': 8\n " + "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Generic\n 'BTB-Tag-Bits': 8\n " " 'Saturating-Count-Bits': 2\n 'Global-History-Length': 8\n " "'RAS-entries': 8\n 'Fallback-Static-Predictor': " "'Always-Taken'\n'L1-Data-Memory':\n 'Interface-Type': " @@ -104,8 +104,8 @@ TEST(ConfigTest, Default) { "100000\n'Register-Set':\n 'GeneralPurpose-Count': 32\n " "'FloatingPoint-Count': 32\n'Pipeline-Widths':\n Commit: 1\n FrontEnd: " "1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: 32\n Load: 16\n " - "Store: 16\n'Branch-Predictor':\n 'BTB-Tag-Bits': 8\n " - "'Saturating-Count-Bits': 2\n 'Global-History-Length': 8\n " + "Store: 16\n'Branch-Predictor':\n Type: Generic\n 'BTB-Tag-Bits': 8\n " + " 'Saturating-Count-Bits': 2\n 'Global-History-Length': 8\n " "'RAS-entries': 8\n 'Fallback-Static-Predictor': " "'Always-Taken'\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index fd1e4f9882..ade9822076 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -32,6 +32,7 @@ set(TEST_SOURCES ProcessTest.cc RegisterFileSetTest.cc RegisterValueTest.cc + PerceptronPredictorTest.cc SpecialFileDirGenTest.cc ) diff --git a/test/unit/GenericPredictorTest.cc b/test/unit/GenericPredictorTest.cc index 898e7e93e2..c546157021 100644 --- a/test/unit/GenericPredictorTest.cc +++ b/test/unit/GenericPredictorTest.cc @@ -19,17 +19,17 @@ class GenericPredictorTest : public testing::Test { // miss TEST_F(GenericPredictorTest, Miss) { simeng::config::SimInfo::addToConfig( - "{Branch-Predictor: {BTB-Tag-Bits: 11, Saturating-Count-Bits: 2, " - "Global-History-Length: 10, RAS-entries: 5, Fallback-Static-Predictor: " - "Always-Taken}}"); + "{Branch-Predictor: {Type: Generic, BTB-Tag-Bits: 11, " + "Saturating-Count-Bits: 2, Global-History-Length: 10, RAS-entries: 5, " + "Fallback-Static-Predictor: Always-Taken}}"); auto predictor = simeng::GenericPredictor(); auto prediction = predictor.predict(0, BranchType::Conditional, 0); EXPECT_TRUE(prediction.taken); simeng::config::SimInfo::addToConfig( - "{Branch-Predictor: {BTB-Tag-Bits: 11, Saturating-Count-Bits: 2, " - "Global-History-Length: 10, RAS-entries: 5, Fallback-Static-Predictor: " - "Always-Not-Taken}}"); + "{Branch-Predictor: {Type: Generic, BTB-Tag-Bits: 11, " + "Saturating-Count-Bits: 2, Global-History-Length: 10, RAS-entries: 5, " + "Fallback-Static-Predictor: Always-Not-Taken}}"); predictor = simeng::GenericPredictor(); prediction = predictor.predict(0, BranchType::Conditional, 0); EXPECT_FALSE(prediction.taken); @@ -41,9 +41,9 @@ TEST_F(GenericPredictorTest, Miss) { // correctly TEST_F(GenericPredictorTest, RAS) { simeng::config::SimInfo::addToConfig( - "{Branch-Predictor: {BTB-Tag-Bits: 11, Saturating-Count-Bits: 2, " - "Global-History-Length: 10, RAS-entries: 10, Fallback-Static-Predictor: " - "Always-Taken}}"); + "{Branch-Predictor: {Type: Generic, BTB-Tag-Bits: 11, " + "Saturating-Count-Bits: 2, Global-History-Length: 10, RAS-entries: 10, " + "Fallback-Static-Predictor: Always-Taken}}"); auto predictor = simeng::GenericPredictor(); auto prediction = predictor.predict(8, BranchType::SubroutineCall, 8); EXPECT_TRUE(prediction.taken); @@ -82,9 +82,9 @@ TEST_F(GenericPredictorTest, RAS) { // correctly, when no address aliasing has occurred TEST_F(GenericPredictorTest, Hit) { simeng::config::SimInfo::addToConfig( - "{Branch-Predictor: {BTB-Tag-Bits: 11, Saturating-Count-Bits: 2, " - "Global-History-Length: 1, RAS-entries: 5, Fallback-Static-Predictor: " - "Always-Taken}}"); + "{Branch-Predictor: {Type: Generic, BTB-Tag-Bits: 11, " + "Saturating-Count-Bits: 2, Global-History-Length: 1, RAS-entries: 5, " + "Fallback-Static-Predictor: Always-Taken}}"); auto predictor = simeng::GenericPredictor(); predictor.update(0, true, 16, BranchType::Conditional); predictor.update(0, true, 16, BranchType::Conditional); @@ -101,9 +101,9 @@ TEST_F(GenericPredictorTest, Hit) { // behaviours of the same branch but in different states of the program TEST_F(GenericPredictorTest, GlobalIndexing) { simeng::config::SimInfo::addToConfig( - "{Branch-Predictor: {BTB-Tag-Bits: 11, Saturating-Count-Bits: 2, " - "Global-History-Length: 5, RAS-entries: 5, Fallback-Static-Predictor: " - "Always-Not-Taken}}"); + "{Branch-Predictor: {Type: Generic, BTB-Tag-Bits: 11, " + "Saturating-Count-Bits: 2, Global-History-Length: 5, RAS-entries: 5, " + "Fallback-Static-Predictor: Always-Not-Taken}}"); auto predictor = simeng::GenericPredictor(); // Spool up first global history pattern predictor.update(0, true, 4, BranchType::Unconditional); diff --git a/test/unit/PerceptronPredictorTest.cc b/test/unit/PerceptronPredictorTest.cc new file mode 100644 index 0000000000..561404a6fb --- /dev/null +++ b/test/unit/PerceptronPredictorTest.cc @@ -0,0 +1,148 @@ +#include "MockInstruction.hh" +#include "gtest/gtest.h" +#include "simeng/PerceptronPredictor.hh" + +namespace simeng { + +class PerceptronPredictorTest : public testing::Test { + public: + PerceptronPredictorTest() : uop(new MockInstruction), uopPtr(uop) { + uop->setInstructionAddress(0); + } + + protected: + MockInstruction* uop; + std::shared_ptr uopPtr; +}; + +// Tests that the PerceptronPredictor will predict the correct direction on a +// miss +TEST_F(PerceptronPredictorTest, Miss) { + simeng::config::SimInfo::addToConfig( + "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " + "Global-History-Length: 10, RAS-entries: 5}}"); + auto predictor = simeng::PerceptronPredictor(); + auto prediction = predictor.predict(0, BranchType::Conditional, 0); + EXPECT_TRUE(prediction.taken); + prediction = predictor.predict(8, BranchType::Unconditional, 0); + EXPECT_TRUE(prediction.taken); +} + +// Tests that the PerceptronPredictor will predict branch-and-link return pairs +// correctly +TEST_F(PerceptronPredictorTest, RAS) { + simeng::config::SimInfo::addToConfig( + "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " + "Global-History-Length: 10, RAS-entries: 10}}"); + auto predictor = simeng::PerceptronPredictor(); + auto prediction = predictor.predict(8, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 16); + prediction = predictor.predict(24, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 32); + prediction = predictor.predict(40, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 48); + prediction = predictor.predict(56, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 64); + prediction = predictor.predict(72, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 80); + + prediction = predictor.predict(84, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 76); + prediction = predictor.predict(68, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 60); + prediction = predictor.predict(52, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 44); + prediction = predictor.predict(36, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 28); + prediction = predictor.predict(20, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 12); +} + +// Tests that the PerceptronPredictor will predict a previously encountered branch +// correctly, when no address aliasing has occurred +TEST_F(PerceptronPredictorTest, Hit) { + simeng::config::SimInfo::addToConfig( + "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " + "Global-History-Length: 1, RAS-entries: 5}}"); + auto predictor = simeng::PerceptronPredictor(); + predictor.update(0, true, 16, BranchType::Conditional); + predictor.update(0, true, 16, BranchType::Conditional); + predictor.update(0, true, 16, BranchType::Conditional); + predictor.update(0, true, 16, BranchType::Conditional); + predictor.update(0, false, 16, BranchType::Conditional); + + auto prediction = predictor.predict(0, BranchType::Conditional, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 16); +} + +// Tests that the PeceptronPredictor will predict correctly for two different +// behaviours of the same branch but in different states of the program +TEST_F(PerceptronPredictorTest, GlobalIndexing) { + simeng::config::SimInfo::addToConfig( + "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " + "Global-History-Length: 5, RAS-entries: 5}}"); + auto predictor = simeng::PerceptronPredictor(); + // Spool up first global history pattern + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + // Ensure default behaviour for first encounter + auto prediction = predictor.predict(0x1F, BranchType::Conditional, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 0); + // Set entry in BTB + predictor.update(0x1F, false, 0xAB, BranchType::Conditional); + + // Spool up second global history pattern + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + // Ensure default behaviour for re-encounter but with different global history + prediction = predictor.predict(0x1F, BranchType::Conditional, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 0); + // Set entry in BTB + predictor.update(0x1F, true, 0xBA, BranchType::Conditional); + + // Recreate first global history pattern + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + // Get prediction + prediction = predictor.predict(0x1F, BranchType::Conditional, 0); + EXPECT_FALSE(prediction.taken); + EXPECT_EQ(prediction.target, 0x23); + // Set entry in BTB + predictor.update(0x1F, true, 0xAB, BranchType::Conditional); + + // Recreate second global history pattern + predictor.update(0, false, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, true, 4, BranchType::Unconditional); + predictor.update(0, false, 4, BranchType::Unconditional); + // Get prediction + prediction = predictor.predict(0x1F, BranchType::Conditional, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 0xBA); + predictor.update(0x1F, true, 0xBA, BranchType::Conditional); +} + +} // namespace simeng From 0aa88a14961c55c9c4c084599b9a56b8df13a50c Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:43:39 +0100 Subject: [PATCH 07/20] tidying --- src/lib/PerceptronPredictor.cc | 1 + src/lib/config/ModelConfig.cc | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index 24b0ef715c..ad5e36942d 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -85,6 +85,7 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, void PerceptronPredictor::update(uint64_t address, bool taken, uint64_t targetAddress, BranchType type) { + // Work out hash index uint64_t prevGlobalHistory = btbHistory_[address]; uint64_t hashedIndex = ((address >> 2) ^ prevGlobalHistory) & ((1 << btbBits_) - 1); diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index 41e6020506..901a0c8952 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -434,7 +434,6 @@ void ModelConfig::setExpectations(bool isDefault) { ExpectationNode::createExpectation(8, "BTB-Tag-Bits")); expectations_["Branch-Predictor"]["BTB-Tag-Bits"].setValueBounds(1, 64); - expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation(2, "Saturating-Count-Bits", true)); From c1a3cd23fee5ffbdc066c9b84c3f9998c530d438 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:01:20 +0000 Subject: [PATCH 08/20] Adding to documentation --- .../developer/components/branchPred.rst | 17 ++++++++++++++ docs/sphinx/user/configuring_simeng.rst | 7 ++++-- src/include/simeng/PerceptronPredictor.hh | 4 ++-- src/lib/PerceptronPredictor.cc | 22 ++++++++++++------- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/docs/sphinx/developer/components/branchPred.rst b/docs/sphinx/developer/components/branchPred.rst index b49f58c1c1..521f1e7f07 100644 --- a/docs/sphinx/developer/components/branchPred.rst +++ b/docs/sphinx/developer/components/branchPred.rst @@ -35,3 +35,20 @@ Return Address Stack (RAS) Static Prediction Based on the chosen static prediction method of "always taken" or "always not taken", the n-bit saturating counter value in the initial entries of the BTB structure are filled with the weakest variant of taken or not-taken respectively. + +Perceptron Predictor +-------------------- +The ``PerceptronPredictor`` has the same overal structure as the ``GenericPredictor`` but replaces the saturating counter as a means for direction prediction with a perceptron. The ``PerceptronPredictor`` contains the following logic. + +Global History + For indexing relevant prediction structures and for retrieving a direction from the perceptrons, a global history can be utilised. The global history value uses n-bits to store the n most recent branch direction outcomes, with the left-most bit being the oldest. + +Branch Target Buffer (BTB) + For each entry, the BTB stores the most recent target along with a perceptron for an associated direction. The indexing of this structure uses the lower, non-zero bits of an instruction address XOR'ed with the current global branch history value. + + The direction prediction is obtained from the perceptron by taking its dot-product with the global history. The prediction is not taken if this is negative, or taken otherwise. The perceptron is updated when its prediction is wrong or when the magnitude of the dot-product is below a pre-determined threshold (i.e., the confidence of the prediction is low). To update, each ith weight of the perceptron is incremented if the actual outcome of the branch is the same as the ith bit of ``globalHistory_``, and decremented otherwise. + + If the supplied branch type is ``Unconditional``, then the predicted direction is overridden to be taken. If the supplied branch type is ``Conditional`` and the predicted direction is not taken, then the predicted target is overridden to be the next sequential instruction. + +Return Address Stack (RAS) + Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for use by a proceeding Return instruction. \ No newline at end of file diff --git a/docs/sphinx/user/configuring_simeng.rst b/docs/sphinx/user/configuring_simeng.rst index 344585f77b..5c9762f320 100644 --- a/docs/sphinx/user/configuring_simeng.rst +++ b/docs/sphinx/user/configuring_simeng.rst @@ -145,11 +145,14 @@ The Branch-Prediction section contains those options to parameterise the branch The current options include: +Type + The type of branch predictor that is used, the options are ``Generic``, and ``Perceptron``. Both types of predictor use a branch target buffer with each entry containing a direction prediction mechanism and a target address. The direction predictor used in ``Generic`` is a saturating counter, and in ``Perceptron`` it is a perceptron. + BTB-Tag-Bits The number of bits used to denote an entry in the Branch Target Buffer (BTB). For example, a ``bits`` value of 12 could denote 4096 entries with the calculation 1 << ``bits``. Saturating-Count-Bits - The number of bits used in the saturating counter value. + Only needed for a ``Generic`` predictor. The number of bits used in the saturating counter value. Global-History-Length The number of bits used to record the global history of branch directions. Each bit represents one branch direction. @@ -158,7 +161,7 @@ RAS-entries The number of entries in the Return Address Stack (RAS). Fallback-Static-Predictor - The static predictor used when no dynamic prediction is available. The options are either ``"Always-Taken"`` or ``"Always-Not-Taken"``. + Only needed for a ``Generic`` predictor. The static predictor used when no dynamic prediction is available. The options are either ``"Always-Taken"`` or ``"Always-Not-Taken"``. .. _l1dcnf: diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index 5a764dfa50..af33ddaf09 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -46,8 +46,8 @@ class PerceptronPredictor : public BranchPredictor { /** The bitlength of the BTB index; BTB will have 2^bits entries. */ uint64_t btbBits_; - /** A 2^bits length vector of pairs containing a (globalHistoryLength_ + 1) - * length perceptron and a branch target. */ + /** A 2^bits length vector of pairs containing a perceptron with + * globalHistoryLength_ + 1 inputs, and a branch target. */ std::vector, uint64_t>> btb_; /** The previous hashed index for an address. */ diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index ad5e36942d..2cda32af8c 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -10,6 +10,8 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) // Build BTB based on config options uint32_t btbSize = (1 << btbBits_); btb_.resize(btbSize); + // Initialise perceptron values with 0 for the global history weights, and 1 for the bias weight; + // and intialise the target with 0 (i.e., unknown) for (int i = 0; i < btbSize; i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); @@ -17,7 +19,7 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) } // Set up training threshold according to empirically determined formula - trainingThreshold_ = (int)((1.93 * (globalHistoryLength_)) + 14); + trainingThreshold_ = (int)((1.93 * globalHistoryLength_) + 14); } PerceptronPredictor::~PerceptronPredictor() { @@ -27,8 +29,9 @@ PerceptronPredictor::~PerceptronPredictor() { BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, int64_t knownOffset) { - // Get index via an XOR hash between the global history and the lower btbBits_ - // bits of the instruction address + // Get the hashed index for the prediction table. XOR the global history with the + // non-zero bits of the address, and then keep only the btbBits_ bits of the output + // keep it in bounds of the prediction table. uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); // Store the global history for correct hashing in update() -- @@ -44,19 +47,20 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, // Starting with the bias weight int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { + // Get branch direction for ith entry in the globalHistory_ bool historyTaken = ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); } bool direction = (Pout >= 0); - // Retrieve target prediction + // Retrieve target prediction uint64_t target = (knownOffset != 0) ? address + knownOffset : btb_[hashedIndex].second; BranchPrediction prediction = {direction, target}; - // Ammend prediction based on branch type + // Amend prediction based on branch type if (type == BranchType::Unconditional) { prediction.taken = true; } else if (type == BranchType::Return) { @@ -102,9 +106,9 @@ void PerceptronPredictor::update(uint64_t address, bool taken, bool directionPrediction = (Pout >= 0); // Determine the magnitude of the dot product for training - uint64_t magnitude = (Pout < 0) ? (0 - Pout) : Pout; + uint64_t magnitude = abs(Pout); - // update the perceptron if the prediction was wrong, or the dot product's magnitude + // Update the perceptron if the prediction was wrong, or the dot product's magnitude // was not greater than the training threshold if ((directionPrediction != taken) || (magnitude < trainingThreshold_)) { int8_t t = (taken) ? 1 : -1; @@ -114,7 +118,9 @@ void PerceptronPredictor::update(uint64_t address, bool taken, ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) ? -1 : 1; int8_t product_xi_t = xi * t; // Make sure no overflow - perceptron[i] += (perceptron[i] != 127 && perceptron[i] != -127) ? product_xi_t : 0; + if (!(perceptron[i] == 127 && product_xi_t == 1) && !(perceptron[i] == -127 && product_xi_t == -1)) { + perceptron[i] += product_xi_t; + } } perceptron[globalHistoryLength_] += t; } From ede562d46458687782f59d6e98053e82d865354d Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:12:05 +0000 Subject: [PATCH 09/20] Adding to documentation --- docs/sphinx/user/configuring_simeng.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/user/configuring_simeng.rst b/docs/sphinx/user/configuring_simeng.rst index 5c9762f320..fe080aa7dc 100644 --- a/docs/sphinx/user/configuring_simeng.rst +++ b/docs/sphinx/user/configuring_simeng.rst @@ -155,7 +155,7 @@ Saturating-Count-Bits Only needed for a ``Generic`` predictor. The number of bits used in the saturating counter value. Global-History-Length - The number of bits used to record the global history of branch directions. Each bit represents one branch direction. + The number of bits used to record the global history of branch directions. Each bit represents one branch direction. For ``PerceptronPredictor``, this dictates the size of the perceptrons (with each perceptron having Global-History-Length + 1 weights). RAS-entries The number of entries in the Return Address Stack (RAS). From 8a042c3df8532516097e3ab8c3b062c4b2e886ef Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:01:03 +0000 Subject: [PATCH 10/20] Adding comments, adding if to config --- .../developer/components/branchPred.rst | 2 +- src/include/simeng/PerceptronPredictor.hh | 23 ++++++++++-- src/lib/PerceptronPredictor.cc | 35 +++++------------- src/lib/config/ModelConfig.cc | 32 ++++++++++------ test/unit/PerceptronPredictorTest.cc | 37 +++++++++++++++++++ 5 files changed, 89 insertions(+), 40 deletions(-) diff --git a/docs/sphinx/developer/components/branchPred.rst b/docs/sphinx/developer/components/branchPred.rst index 521f1e7f07..0bed7ef3da 100644 --- a/docs/sphinx/developer/components/branchPred.rst +++ b/docs/sphinx/developer/components/branchPred.rst @@ -38,7 +38,7 @@ Static Prediction Perceptron Predictor -------------------- -The ``PerceptronPredictor`` has the same overal structure as the ``GenericPredictor`` but replaces the saturating counter as a means for direction prediction with a perceptron. The ``PerceptronPredictor`` contains the following logic. +The ``PerceptronPredictor`` has the same overall structure as the ``GenericPredictor`` but replaces the saturating counter as a means for direction prediction with a perceptron. The ``PerceptronPredictor`` contains the following logic. Global History For indexing relevant prediction structures and for retrieving a direction from the perceptrons, a global history can be utilised. The global history value uses n-bits to store the n most recent branch direction outcomes, with the left-most bit being the oldest. diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index af33ddaf09..9d8e51cd79 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -11,7 +11,8 @@ namespace simeng { /** A Perceptron branch predictor implementing the branch predictor described in * Jimenez and Lin ("Dynamic branch prediction with perceptrons", IEEE High- - * Performance Computer Architecture Symposium Proceedings (2001), 197-206. + * Performance Computer Architecture Symposium Proceedings (2001), 197-206 -- + * https://www.cs.utexas.edu/~lin/papers/hpca01.pdf). * The following predictors have been included: * * - Static predictor based on pre-allocated branch type. @@ -43,11 +44,27 @@ class PerceptronPredictor : public BranchPredictor { void flush(uint64_t address) override; private: - /** The bitlength of the BTB index; BTB will have 2^bits entries. */ + /** Returns the dot product of a perceptron and a history vector. Used to determine + * a direction prediction */ + int64_t getDotProduct(std::vector perceptron, uint64_t history) { + int64_t Pout = perceptron[globalHistoryLength_]; + for (int i = 0; i < globalHistoryLength_; i++) { + // Get branch direction for ith entry in the history + bool historyTaken = + ((history & (1 << ((globalHistoryLength_ - 1) - i))) != 0); + Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); + } + return Pout; + } + + /** The length in bits of the BTB index; BTB will have 2^bits entries. */ uint64_t btbBits_; /** A 2^bits length vector of pairs containing a perceptron with - * globalHistoryLength_ + 1 inputs, and a branch target. */ + * globalHistoryLength_ + 1 inputs, and a branch target. + * The perceptrons are used to provide a branch direction prediction by + * taking a dot product with the global history, as described + * in Jiminez and Lin */ std::vector, uint64_t>> btb_; /** The previous hashed index for an address. */ diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index 2cda32af8c..b68cbe68cd 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -11,7 +11,7 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) uint32_t btbSize = (1 << btbBits_); btb_.resize(btbSize); // Initialise perceptron values with 0 for the global history weights, and 1 for the bias weight; - // and intialise the target with 0 (i.e., unknown) + // and initialise the target with 0 (i.e., unknown) for (int i = 0; i < btbSize; i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); @@ -19,7 +19,7 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) } // Set up training threshold according to empirically determined formula - trainingThreshold_ = (int)((1.93 * globalHistoryLength_) + 14); + trainingThreshold_ = (uint64_t)((1.93 * globalHistoryLength_) + 14); } PerceptronPredictor::~PerceptronPredictor() { @@ -31,27 +31,20 @@ BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, int64_t knownOffset) { // Get the hashed index for the prediction table. XOR the global history with the // non-zero bits of the address, and then keep only the btbBits_ bits of the output - // keep it in bounds of the prediction table. + // to keep it in bounds of the prediction table. uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); // Store the global history for correct hashing in update() -- - // needs to be global history and not the hashed index as hashing looses information at longer + // needs to be global history and not the hashed index as hashing loses information at longer // global history lengths btbHistory_[address] = globalHistory_; // Retrieve the perceptron from the BTB std::vector perceptron = btb_[hashedIndex].first; - // Determine direction prediction from perceptron by taking its dot product - // with the global history - // Starting with the bias weight - int64_t Pout = perceptron[globalHistoryLength_]; - for (int i = 0; i < globalHistoryLength_; i++) { - // Get branch direction for ith entry in the globalHistory_ - bool historyTaken = - ((globalHistory_ & (1 << ((globalHistoryLength_ - 1) - i))) != 0); - Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); - } + // Get dot product of perceptron and history + int64_t Pout = getDotProduct(perceptron, globalHistory_); + // Determine direction prediction based on its sign bool direction = (Pout >= 0); // Retrieve target prediction @@ -97,27 +90,19 @@ void PerceptronPredictor::update(uint64_t address, bool taken, std::vector perceptron = btb_[hashedIndex].first; // Work out the most recent prediction - int64_t Pout = perceptron[globalHistoryLength_]; - for (int i = 0; i < globalHistoryLength_; i++) { - bool historyTaken = - ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) != 0); - Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); - } + int64_t Pout = getDotProduct(perceptron, prevGlobalHistory); bool directionPrediction = (Pout >= 0); - // Determine the magnitude of the dot product for training - uint64_t magnitude = abs(Pout); - // Update the perceptron if the prediction was wrong, or the dot product's magnitude // was not greater than the training threshold - if ((directionPrediction != taken) || (magnitude < trainingThreshold_)) { + if ((directionPrediction != taken) || (abs(Pout) < trainingThreshold_)) { int8_t t = (taken) ? 1 : -1; for (int i = 0; i < globalHistoryLength_; i++) { int8_t xi = ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) ? -1 : 1; int8_t product_xi_t = xi * t; - // Make sure no overflow + // Make sure no overflow (+-127) if (!(perceptron[i] == 127 && product_xi_t == 1) && !(perceptron[i] == -127 && product_xi_t == -1)) { perceptron[i] += product_xi_t; } diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index 901a0c8952..f919ee95a5 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -426,7 +426,7 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation( - "Generic", "Type")); + "Perceptron", "Type")); expectations_["Branch-Predictor"]["Type"].setValueSet( std::vector{"Generic", "Perceptron"}); @@ -434,11 +434,16 @@ void ModelConfig::setExpectations(bool isDefault) { ExpectationNode::createExpectation(8, "BTB-Tag-Bits")); expectations_["Branch-Predictor"]["BTB-Tag-Bits"].setValueBounds(1, 64); - expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation(2, "Saturating-Count-Bits", - true)); - expectations_["Branch-Predictor"]["Saturating-Count-Bits"] - .setValueBounds(1, 64); + // Saturating counter bits are relevant to the GenericPredictor only + if (!isDefault) { + if (configTree_["Branch-Predictor"]["Type"].as() == "Generic") { + expectations_["Branch-Predictor"].addChild( + ExpectationNode::createExpectation(2, + "Saturating-Count-Bits")); + expectations_["Branch-Predictor"]["Saturating-Count-Bits"] + .setValueBounds(1, 64); + } + } expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation(8, "Global-History-Length")); @@ -450,11 +455,16 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_["Branch-Predictor"]["RAS-entries"].setValueBounds( 1, UINT16_MAX); - expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation( - "Always-Taken", "Fallback-Static-Predictor", true)); - expectations_["Branch-Predictor"]["Fallback-Static-Predictor"].setValueSet( - std::vector{"Always-Taken", "Always-Not-Taken"}); + // The fallback predictor is relevant to the GenericPredictor only + if (!isDefault) { + if (configTree_["Branch-Predictor"]["Type"].as() == "Generic") { + expectations_["Branch-Predictor"].addChild( + ExpectationNode::createExpectation( + "Always-Taken", "Fallback-Static-Predictor")); + expectations_["Branch-Predictor"]["Fallback-Static-Predictor"].setValueSet( + std::vector{"Always-Taken", "Always-Not-Taken"}); + } + } // L1-Data-Memory expectations_.addChild(ExpectationNode::createExpectation("L1-Data-Memory")); diff --git a/test/unit/PerceptronPredictorTest.cc b/test/unit/PerceptronPredictorTest.cc index 561404a6fb..b2ee3caec3 100644 --- a/test/unit/PerceptronPredictorTest.cc +++ b/test/unit/PerceptronPredictorTest.cc @@ -145,4 +145,41 @@ TEST_F(PerceptronPredictorTest, GlobalIndexing) { predictor.update(0x1F, true, 0xBA, BranchType::Conditional); } +// Test Flush of RAS functionality +TEST_F(PerceptronPredictorTest, flush) { + simeng::config::SimInfo::addToConfig( + "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " + "Global-History-Length: 10, RAS-entries: 10}}"); + auto predictor = simeng::PerceptronPredictor(); + // Add some entries to the RAS + auto prediction = predictor.predict(8, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 16); + prediction = predictor.predict(24, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 32); + prediction = predictor.predict(40, BranchType::SubroutineCall, 8); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 48); + + // Start getting entries from RAS + prediction = predictor.predict(52, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 44); + prediction = predictor.predict(36, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 28); + + // Flush address + predictor.flush(36); + + // Continue getting entries from RAS + prediction = predictor.predict(20, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 28); + prediction = predictor.predict(16, BranchType::Return, 0); + EXPECT_TRUE(prediction.taken); + EXPECT_EQ(prediction.target, 12); +} + } // namespace simeng From 13e79e217aebe80c16c262ca3a00b056dc2c0c22 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:02:22 +0000 Subject: [PATCH 11/20] Adjusting tests in view of config changes --- test/integration/ConfigTest.cc | 14 ++++++-------- test/regression/RegressionTest.cc | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/test/integration/ConfigTest.cc b/test/integration/ConfigTest.cc index 296e764f6d..951351f191 100644 --- a/test/integration/ConfigTest.cc +++ b/test/integration/ConfigTest.cc @@ -49,10 +49,9 @@ TEST(ConfigTest, Default) { "'FloatingPoint/SVE-Count': 32\n 'Predicate-Count': 17\n " "'Conditional-Count': 1\n 'Matrix-Count': 1\n'Pipeline-Widths':\n " "Commit: 1\n FrontEnd: 1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: " - "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Generic\n 'BTB-Tag-Bits': 8\n " - " 'Saturating-Count-Bits': 2\n 'Global-History-Length': 8\n " - "'RAS-entries': 8\n 'Fallback-Static-Predictor': " - "'Always-Taken'\n'L1-Data-Memory':\n 'Interface-Type': " + "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Perceptron\n 'BTB-Tag-Bits': 8\n " + " 'Global-History-Length': 8\n " + "'RAS-entries': 8\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " "Flat\n'LSQ-L1-Interface':\n 'Access-Latency': 4\n Exclusive: 0\n " "'Load-Bandwidth': 32\n 'Store-Bandwidth': 32\n " @@ -104,10 +103,9 @@ TEST(ConfigTest, Default) { "100000\n'Register-Set':\n 'GeneralPurpose-Count': 32\n " "'FloatingPoint-Count': 32\n'Pipeline-Widths':\n Commit: 1\n FrontEnd: " "1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: 32\n Load: 16\n " - "Store: 16\n'Branch-Predictor':\n Type: Generic\n 'BTB-Tag-Bits': 8\n " - " 'Saturating-Count-Bits': 2\n 'Global-History-Length': 8\n " - "'RAS-entries': 8\n 'Fallback-Static-Predictor': " - "'Always-Taken'\n'L1-Data-Memory':\n 'Interface-Type': " + "Store: 16\n'Branch-Predictor':\n Type: Perceptron\n 'BTB-Tag-Bits': 8\n" + " 'Global-History-Length': 8\n " + "'RAS-entries': 8\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " "Flat\n'LSQ-L1-Interface':\n 'Access-Latency': 4\n Exclusive: 0\n " "'Load-Bandwidth': 32\n 'Store-Bandwidth': 32\n " diff --git a/test/regression/RegressionTest.cc b/test/regression/RegressionTest.cc index 5b43ac6cd0..30c08ce657 100644 --- a/test/regression/RegressionTest.cc +++ b/test/regression/RegressionTest.cc @@ -5,6 +5,7 @@ #include "simeng/FixedLatencyMemoryInterface.hh" #include "simeng/FlatMemoryInterface.hh" #include "simeng/GenericPredictor.hh" +#include "simeng/PerceptronPredictor.hh" #include "simeng/config/SimInfo.hh" #include "simeng/kernel/Linux.hh" #include "simeng/kernel/LinuxProcess.hh" @@ -94,7 +95,15 @@ void RegressionTest::run(const char* source, const char* triple, createPortAllocator(); // Create a branch predictor for a pipelined core - simeng::GenericPredictor predictor = simeng::GenericPredictor(); + simeng::PerceptronPredictor predictor = simeng::PerceptronPredictor(); + std::unique_ptr predictor_ = nullptr; + std::string predictorType = simeng::config::SimInfo::getConfig()["Branch-Predictor"]["Type"].as(); + if (predictorType == "Generic") { + predictor_ = std::make_unique(); + } else if (predictorType == "Perceptron") { + predictor_ = std::make_unique(); + } + // Create the core model switch (std::get<0>(GetParam())) { case EMULATION: @@ -106,13 +115,13 @@ void RegressionTest::run(const char* source, const char* triple, case INORDER: core_ = std::make_unique( instructionMemory, *flatDataMemory, processMemorySize_, entryPoint, - *architecture_, predictor); + *architecture_, *predictor_); dataMemory = std::move(flatDataMemory); break; case OUTOFORDER: core_ = std::make_unique( instructionMemory, *fixedLatencyDataMemory, processMemorySize_, - entryPoint, *architecture_, predictor, *portAllocator); + entryPoint, *architecture_, *predictor_, *portAllocator); dataMemory = std::move(fixedLatencyDataMemory); break; } From 4351315ce9b5da9a734ddbe16262beb7c6a9a102 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:35:44 +0000 Subject: [PATCH 12/20] clang format and finessing --- configs/sst-cores/a64fx-sst.yaml | 6 ++-- configs/sst-cores/m1_firestorm-sst.yaml | 6 ++-- configs/sst-cores/tx2-sst.yaml | 4 +-- src/include/simeng/PerceptronPredictor.hh | 15 ++------- src/lib/CoreInstance.cc | 3 +- src/lib/PerceptronPredictor.cc | 40 ++++++++++++++++------- src/lib/config/ModelConfig.cc | 23 ++++++------- test/integration/ConfigTest.cc | 9 ++--- test/regression/RegressionTest.cc | 5 +-- test/unit/PerceptronPredictorTest.cc | 4 +-- 10 files changed, 58 insertions(+), 57 deletions(-) diff --git a/configs/sst-cores/a64fx-sst.yaml b/configs/sst-cores/a64fx-sst.yaml index 03b1aca479..fd503c668d 100644 --- a/configs/sst-cores/a64fx-sst.yaml +++ b/configs/sst-cores/a64fx-sst.yaml @@ -29,12 +29,10 @@ Queue-Sizes: Load: 40 Store: 24 Branch-Predictor: - Type: "Generic" + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 11 + Global-History-Length: 19 RAS-entries: 8 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: External L1-Instruction-Memory: diff --git a/configs/sst-cores/m1_firestorm-sst.yaml b/configs/sst-cores/m1_firestorm-sst.yaml index 918581b160..e7bc241b8f 100644 --- a/configs/sst-cores/m1_firestorm-sst.yaml +++ b/configs/sst-cores/m1_firestorm-sst.yaml @@ -25,12 +25,10 @@ Queue-Sizes: Load: 130 Store: 60 Branch-Predictor: - Type: "Generic" + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 - Global-History-Length: 11 + Global-History-Length: 11 RAS-entries: 8 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: External L1-Instruction-Memory: diff --git a/configs/sst-cores/tx2-sst.yaml b/configs/sst-cores/tx2-sst.yaml index a367915d67..e3d1e3231c 100644 --- a/configs/sst-cores/tx2-sst.yaml +++ b/configs/sst-cores/tx2-sst.yaml @@ -27,12 +27,10 @@ Queue-Sizes: Load: 64 Store: 36 Branch-Predictor: - Type: "Generic" + Type: "Perceptron" BTB-Tag-Bits: 11 - Saturating-Count-Bits: 2 Global-History-Length: 10 RAS-entries: 5 - Fallback-Static-Predictor: "Always-Taken" L1-Data-Memory: Interface-Type: External L1-Instruction-Memory: diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index 9d8e51cd79..d42bdbf177 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -44,18 +44,9 @@ class PerceptronPredictor : public BranchPredictor { void flush(uint64_t address) override; private: - /** Returns the dot product of a perceptron and a history vector. Used to determine - * a direction prediction */ - int64_t getDotProduct(std::vector perceptron, uint64_t history) { - int64_t Pout = perceptron[globalHistoryLength_]; - for (int i = 0; i < globalHistoryLength_; i++) { - // Get branch direction for ith entry in the history - bool historyTaken = - ((history & (1 << ((globalHistoryLength_ - 1) - i))) != 0); - Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); - } - return Pout; - } + /** Returns the dot product of a perceptron and a history vector. Used to + * determine a direction prediction */ + int64_t getDotProduct(std::vector perceptron, uint64_t history); /** The length in bits of the BTB index; BTB will have 2^bits entries. */ uint64_t btbBits_; diff --git a/src/lib/CoreInstance.cc b/src/lib/CoreInstance.cc index fdd6b86765..2e9af897ff 100644 --- a/src/lib/CoreInstance.cc +++ b/src/lib/CoreInstance.cc @@ -219,7 +219,8 @@ void CoreInstance::createCore() { arch_ = std::make_unique(kernel_); } - std::string predictorType = config_["Branch-Predictor"]["Type"].as(); + std::string predictorType = + config_["Branch-Predictor"]["Type"].as(); if (predictorType == "Generic") { predictor_ = std::make_unique(); } else if (predictorType == "Perceptron") { diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index b68cbe68cd..1c56467ddd 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -10,8 +10,8 @@ PerceptronPredictor::PerceptronPredictor(ryml::ConstNodeRef config) // Build BTB based on config options uint32_t btbSize = (1 << btbBits_); btb_.resize(btbSize); - // Initialise perceptron values with 0 for the global history weights, and 1 for the bias weight; - // and initialise the target with 0 (i.e., unknown) + // Initialise perceptron values with 0 for the global history weights, and 1 + // for the bias weight; and initialise the target with 0 (i.e., unknown) for (int i = 0; i < btbSize; i++) { btb_[i].first.assign(globalHistoryLength_, 0); btb_[i].first.push_back(1); @@ -29,14 +29,15 @@ PerceptronPredictor::~PerceptronPredictor() { BranchPrediction PerceptronPredictor::predict(uint64_t address, BranchType type, int64_t knownOffset) { - // Get the hashed index for the prediction table. XOR the global history with the - // non-zero bits of the address, and then keep only the btbBits_ bits of the output - // to keep it in bounds of the prediction table. - uint64_t hashedIndex = ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); + // Get the hashed index for the prediction table. XOR the global history with + // the non-zero bits of the address, and then keep only the btbBits_ bits of + // the output to keep it in bounds of the prediction table. + uint64_t hashedIndex = + ((address >> 2) ^ globalHistory_) & ((1 << btbBits_) - 1); // Store the global history for correct hashing in update() -- - // needs to be global history and not the hashed index as hashing loses information at longer - // global history lengths + // needs to be global history and not the hashed index as hashing loses + // information at longer global history lengths btbHistory_[address] = globalHistory_; // Retrieve the perceptron from the BTB @@ -93,17 +94,20 @@ void PerceptronPredictor::update(uint64_t address, bool taken, int64_t Pout = getDotProduct(perceptron, prevGlobalHistory); bool directionPrediction = (Pout >= 0); - // Update the perceptron if the prediction was wrong, or the dot product's magnitude - // was not greater than the training threshold + // Update the perceptron if the prediction was wrong, or the dot product's + // magnitude was not greater than the training threshold if ((directionPrediction != taken) || (abs(Pout) < trainingThreshold_)) { int8_t t = (taken) ? 1 : -1; for (int i = 0; i < globalHistoryLength_; i++) { int8_t xi = - ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) ? -1 : 1; + ((prevGlobalHistory & (1 << ((globalHistoryLength_ - 1) - i))) == 0) + ? -1 + : 1; int8_t product_xi_t = xi * t; // Make sure no overflow (+-127) - if (!(perceptron[i] == 127 && product_xi_t == 1) && !(perceptron[i] == -127 && product_xi_t == -1)) { + if (!(perceptron[i] == 127 && product_xi_t == 1) && + !(perceptron[i] == -127 && product_xi_t == -1)) { perceptron[i] += product_xi_t; } } @@ -141,4 +145,16 @@ void PerceptronPredictor::flush(uint64_t address) { } } +int64_t PerceptronPredictor::getDotProduct(std::vector perceptron, + uint64_t history) { + int64_t Pout = perceptron[globalHistoryLength_]; + for (int i = 0; i < globalHistoryLength_; i++) { + // Get branch direction for ith entry in the history + bool historyTaken = + ((history & (1 << ((globalHistoryLength_ - 1) - i))) != 0); + Pout += historyTaken ? perceptron[i] : (0 - perceptron[i]); + } + return Pout; +} + } // namespace simeng diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index f919ee95a5..b84882fdab 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -425,8 +425,7 @@ void ModelConfig::setExpectations(bool isDefault) { ExpectationNode::createExpectation("Branch-Predictor")); expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation( - "Perceptron", "Type")); + ExpectationNode::createExpectation("Perceptron", "Type")); expectations_["Branch-Predictor"]["Type"].setValueSet( std::vector{"Generic", "Perceptron"}); @@ -435,14 +434,13 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_["Branch-Predictor"]["BTB-Tag-Bits"].setValueBounds(1, 64); // Saturating counter bits are relevant to the GenericPredictor only - if (!isDefault) { - if (configTree_["Branch-Predictor"]["Type"].as() == "Generic") { - expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation(2, - "Saturating-Count-Bits")); - expectations_["Branch-Predictor"]["Saturating-Count-Bits"] - .setValueBounds(1, 64); - } + if (!isDefault && + configTree_["Branch-Predictor"]["Type"].as() == "Generic") { + expectations_["Branch-Predictor"].addChild( + ExpectationNode::createExpectation(2, + "Saturating-Count-Bits")); + expectations_["Branch-Predictor"]["Saturating-Count-Bits"] + .setValueBounds(1, 64); } expectations_["Branch-Predictor"].addChild( @@ -456,14 +454,13 @@ void ModelConfig::setExpectations(bool isDefault) { 1, UINT16_MAX); // The fallback predictor is relevant to the GenericPredictor only - if (!isDefault) { - if (configTree_["Branch-Predictor"]["Type"].as() == "Generic") { + if (!isDefault && + configTree_["Branch-Predictor"]["Type"].as() == "Generic") { expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation( "Always-Taken", "Fallback-Static-Predictor")); expectations_["Branch-Predictor"]["Fallback-Static-Predictor"].setValueSet( std::vector{"Always-Taken", "Always-Not-Taken"}); - } } // L1-Data-Memory diff --git a/test/integration/ConfigTest.cc b/test/integration/ConfigTest.cc index 951351f191..d80b1a8895 100644 --- a/test/integration/ConfigTest.cc +++ b/test/integration/ConfigTest.cc @@ -49,7 +49,8 @@ TEST(ConfigTest, Default) { "'FloatingPoint/SVE-Count': 32\n 'Predicate-Count': 17\n " "'Conditional-Count': 1\n 'Matrix-Count': 1\n'Pipeline-Widths':\n " "Commit: 1\n FrontEnd: 1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: " - "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Perceptron\n 'BTB-Tag-Bits': 8\n " + "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Perceptron\n " + "'BTB-Tag-Bits': 8\n " " 'Global-History-Length': 8\n " "'RAS-entries': 8\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " @@ -103,9 +104,9 @@ TEST(ConfigTest, Default) { "100000\n'Register-Set':\n 'GeneralPurpose-Count': 32\n " "'FloatingPoint-Count': 32\n'Pipeline-Widths':\n Commit: 1\n FrontEnd: " "1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: 32\n Load: 16\n " - "Store: 16\n'Branch-Predictor':\n Type: Perceptron\n 'BTB-Tag-Bits': 8\n" - " 'Global-History-Length': 8\n " - "'RAS-entries': 8\n'L1-Data-Memory':\n 'Interface-Type': " + "Store: 16\n'Branch-Predictor':\n Type: Perceptron\n 'BTB-Tag-Bits': " + "8\n 'Global-History-Length': 8\n 'RAS-entries': " + "8\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " "Flat\n'LSQ-L1-Interface':\n 'Access-Latency': 4\n Exclusive: 0\n " "'Load-Bandwidth': 32\n 'Store-Bandwidth': 32\n " diff --git a/test/regression/RegressionTest.cc b/test/regression/RegressionTest.cc index 30c08ce657..6c5c30e90d 100644 --- a/test/regression/RegressionTest.cc +++ b/test/regression/RegressionTest.cc @@ -95,9 +95,10 @@ void RegressionTest::run(const char* source, const char* triple, createPortAllocator(); // Create a branch predictor for a pipelined core - simeng::PerceptronPredictor predictor = simeng::PerceptronPredictor(); std::unique_ptr predictor_ = nullptr; - std::string predictorType = simeng::config::SimInfo::getConfig()["Branch-Predictor"]["Type"].as(); + std::string predictorType = + simeng::config::SimInfo::getConfig()["Branch-Predictor"]["Type"] + .as(); if (predictorType == "Generic") { predictor_ = std::make_unique(); } else if (predictorType == "Perceptron") { diff --git a/test/unit/PerceptronPredictorTest.cc b/test/unit/PerceptronPredictorTest.cc index b2ee3caec3..b276b3795f 100644 --- a/test/unit/PerceptronPredictorTest.cc +++ b/test/unit/PerceptronPredictorTest.cc @@ -68,8 +68,8 @@ TEST_F(PerceptronPredictorTest, RAS) { EXPECT_EQ(prediction.target, 12); } -// Tests that the PerceptronPredictor will predict a previously encountered branch -// correctly, when no address aliasing has occurred +// Tests that the PerceptronPredictor will predict a previously encountered +// branch correctly, when no address aliasing has occurred TEST_F(PerceptronPredictorTest, Hit) { simeng::config::SimInfo::addToConfig( "{Branch-Predictor: {Type: Perceptron, BTB-Tag-Bits: 11, " From 229862c5df04614204de992d2436ad26d85cdcfb Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:07:23 +0000 Subject: [PATCH 13/20] Making passed perceptron vector a const in getDotProduct() helper fucntion --- src/include/simeng/PerceptronPredictor.hh | 2 +- src/lib/PerceptronPredictor.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index d42bdbf177..d3fadf758b 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -46,7 +46,7 @@ class PerceptronPredictor : public BranchPredictor { private: /** Returns the dot product of a perceptron and a history vector. Used to * determine a direction prediction */ - int64_t getDotProduct(std::vector perceptron, uint64_t history); + int64_t getDotProduct(const std::vector perceptron, uint64_t history); /** The length in bits of the BTB index; BTB will have 2^bits entries. */ uint64_t btbBits_; diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index 1c56467ddd..aff902f83c 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -145,7 +145,7 @@ void PerceptronPredictor::flush(uint64_t address) { } } -int64_t PerceptronPredictor::getDotProduct(std::vector perceptron, +int64_t PerceptronPredictor::getDotProduct(const std::vector perceptron, uint64_t history) { int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { From cb6dedc326695fc52882f6cce6b49909ecd4d2d7 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:56:11 +0000 Subject: [PATCH 14/20] Reformatting ConfigTest.cc strings --- test/integration/ConfigTest.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/integration/ConfigTest.cc b/test/integration/ConfigTest.cc index d80b1a8895..7907f57762 100644 --- a/test/integration/ConfigTest.cc +++ b/test/integration/ConfigTest.cc @@ -50,9 +50,8 @@ TEST(ConfigTest, Default) { "'Conditional-Count': 1\n 'Matrix-Count': 1\n'Pipeline-Widths':\n " "Commit: 1\n FrontEnd: 1\n 'LSQ-Completion': 1\n'Queue-Sizes':\n ROB: " "32\n Load: 16\n Store: 16\n'Branch-Predictor':\n Type: Perceptron\n " - "'BTB-Tag-Bits': 8\n " - " 'Global-History-Length': 8\n " - "'RAS-entries': 8\n'L1-Data-Memory':\n 'Interface-Type': " + "'BTB-Tag-Bits': 8\n 'Global-History-Length': 8\n 'RAS-entries': " + "8\n'L1-Data-Memory':\n 'Interface-Type': " "Flat\n'L1-Instruction-Memory':\n 'Interface-Type': " "Flat\n'LSQ-L1-Interface':\n 'Access-Latency': 4\n Exclusive: 0\n " "'Load-Bandwidth': 32\n 'Store-Bandwidth': 32\n " @@ -69,8 +68,8 @@ TEST(ConfigTest, Default) { " - 6343\n 'Execution-Latency': 1\n 'Execution-Throughput': 1\n " " 'Instruction-Group-Nums':\n - 87\n'CPU-Info':\n " "'Generate-Special-Dir': 1\n 'Special-File-Dir-Path': " SIMENG_BUILD_DIR - "/specialFiles/\n 'Core-Count': 1\n 'Socket-Count': 1\n " - "SMT: 1\n BogoMIPS: 0\n Features: ''\n 'CPU-Implementer': 0x0\n " + "/specialFiles/\n 'Core-Count': 1\n 'Socket-Count': 1\n SMT: 1\n " + "BogoMIPS: 0\n Features: ''\n 'CPU-Implementer': 0x0\n " "'CPU-Architecture': 0\n 'CPU-Variant': 0x0\n 'CPU-Part': 0x0\n " "'CPU-Revision': 0\n 'Package-Count': 1\n"; EXPECT_EQ(emittedConfig, expectedValues); @@ -123,8 +122,8 @@ TEST(ConfigTest, Default) { " - 450\n 'Execution-Latency': 1\n 'Execution-Throughput': 1\n " "'Instruction-Group-Nums':\n - 24\n'CPU-Info':\n " "'Generate-Special-Dir': 1\n 'Special-File-Dir-Path': " SIMENG_BUILD_DIR - "/specialFiles/\n 'Core-Count': 1\n 'Socket-Count': 1\n " - "SMT: 1\n BogoMIPS: 0\n Features: ''\n 'CPU-Implementer': 0x0\n " + "/specialFiles/\n 'Core-Count': 1\n 'Socket-Count': 1\n SMT: 1\n " + "BogoMIPS: 0\n Features: ''\n 'CPU-Implementer': 0x0\n " "'CPU-Architecture': 0\n 'CPU-Variant': 0x0\n 'CPU-Part': 0x0\n " "'CPU-Revision': 0\n 'Package-Count': 1\n"; EXPECT_EQ(emittedConfig, expectedValues); From c8e83b2ef92abf514047fbd641da65aa83d1a826 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:53:40 +0000 Subject: [PATCH 15/20] Consolidating if logic in BP-related ModelConfig.cc --- src/lib/config/ModelConfig.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lib/config/ModelConfig.cc b/src/lib/config/ModelConfig.cc index b84882fdab..d1313dc5a3 100644 --- a/src/lib/config/ModelConfig.cc +++ b/src/lib/config/ModelConfig.cc @@ -433,15 +433,6 @@ void ModelConfig::setExpectations(bool isDefault) { ExpectationNode::createExpectation(8, "BTB-Tag-Bits")); expectations_["Branch-Predictor"]["BTB-Tag-Bits"].setValueBounds(1, 64); - // Saturating counter bits are relevant to the GenericPredictor only - if (!isDefault && - configTree_["Branch-Predictor"]["Type"].as() == "Generic") { - expectations_["Branch-Predictor"].addChild( - ExpectationNode::createExpectation(2, - "Saturating-Count-Bits")); - expectations_["Branch-Predictor"]["Saturating-Count-Bits"] - .setValueBounds(1, 64); - } expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation(8, "Global-History-Length")); @@ -453,9 +444,16 @@ void ModelConfig::setExpectations(bool isDefault) { expectations_["Branch-Predictor"]["RAS-entries"].setValueBounds( 1, UINT16_MAX); - // The fallback predictor is relevant to the GenericPredictor only + // The saturating counter bits and the fallback predictor + // are relevant to the GenericPredictor only if (!isDefault && configTree_["Branch-Predictor"]["Type"].as() == "Generic") { + expectations_["Branch-Predictor"].addChild( + ExpectationNode::createExpectation(2, + "Saturating-Count-Bits")); + expectations_["Branch-Predictor"]["Saturating-Count-Bits"] + .setValueBounds(1, 64); + expectations_["Branch-Predictor"].addChild( ExpectationNode::createExpectation( "Always-Taken", "Fallback-Static-Predictor")); From 2e6e04138f69d7d9db8298c94cd2608202ee5935 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:36:32 +0000 Subject: [PATCH 16/20] passing perceptron by reference in getDotProduct() --- src/include/simeng/PerceptronPredictor.hh | 3 ++- src/lib/PerceptronPredictor.cc | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/include/simeng/PerceptronPredictor.hh b/src/include/simeng/PerceptronPredictor.hh index d3fadf758b..b76e4dd7e4 100644 --- a/src/include/simeng/PerceptronPredictor.hh +++ b/src/include/simeng/PerceptronPredictor.hh @@ -46,7 +46,8 @@ class PerceptronPredictor : public BranchPredictor { private: /** Returns the dot product of a perceptron and a history vector. Used to * determine a direction prediction */ - int64_t getDotProduct(const std::vector perceptron, uint64_t history); + int64_t getDotProduct(const std::vector& perceptron, + uint64_t history); /** The length in bits of the BTB index; BTB will have 2^bits entries. */ uint64_t btbBits_; diff --git a/src/lib/PerceptronPredictor.cc b/src/lib/PerceptronPredictor.cc index aff902f83c..18ae064d32 100644 --- a/src/lib/PerceptronPredictor.cc +++ b/src/lib/PerceptronPredictor.cc @@ -145,8 +145,8 @@ void PerceptronPredictor::flush(uint64_t address) { } } -int64_t PerceptronPredictor::getDotProduct(const std::vector perceptron, - uint64_t history) { +int64_t PerceptronPredictor::getDotProduct( + const std::vector& perceptron, uint64_t history) { int64_t Pout = perceptron[globalHistoryLength_]; for (int i = 0; i < globalHistoryLength_; i++) { // Get branch direction for ith entry in the history From 1b194a700eba699e896d4144d66fbe98b7e1be2a Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:07:32 +0000 Subject: [PATCH 17/20] doc changes --- docs/sphinx/developer/components/branchPred.rst | 4 ++-- docs/sphinx/user/configuring_simeng.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/developer/components/branchPred.rst b/docs/sphinx/developer/components/branchPred.rst index 0bed7ef3da..6fbeca9fd5 100644 --- a/docs/sphinx/developer/components/branchPred.rst +++ b/docs/sphinx/developer/components/branchPred.rst @@ -31,7 +31,7 @@ Branch Target Buffer (BTB) If the supplied branch type is ``Unconditional``, then the predicted direction is overridden to be taken. If the supplied branch type is ``Conditional`` and the predicted direction is not taken, then the predicted target is overridden to be the next sequential instruction. Return Address Stack (RAS) - Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for use by a proceeding Return instruction. + Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by its corresponding Return instruction. Static Prediction Based on the chosen static prediction method of "always taken" or "always not taken", the n-bit saturating counter value in the initial entries of the BTB structure are filled with the weakest variant of taken or not-taken respectively. @@ -51,4 +51,4 @@ Branch Target Buffer (BTB) If the supplied branch type is ``Unconditional``, then the predicted direction is overridden to be taken. If the supplied branch type is ``Conditional`` and the predicted direction is not taken, then the predicted target is overridden to be the next sequential instruction. Return Address Stack (RAS) - Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for use by a proceeding Return instruction. \ No newline at end of file + Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by its corresponding Return instruction. \ No newline at end of file diff --git a/docs/sphinx/user/configuring_simeng.rst b/docs/sphinx/user/configuring_simeng.rst index fe080aa7dc..a1cacc8b5a 100644 --- a/docs/sphinx/user/configuring_simeng.rst +++ b/docs/sphinx/user/configuring_simeng.rst @@ -149,7 +149,7 @@ Type The type of branch predictor that is used, the options are ``Generic``, and ``Perceptron``. Both types of predictor use a branch target buffer with each entry containing a direction prediction mechanism and a target address. The direction predictor used in ``Generic`` is a saturating counter, and in ``Perceptron`` it is a perceptron. BTB-Tag-Bits - The number of bits used to denote an entry in the Branch Target Buffer (BTB). For example, a ``bits`` value of 12 could denote 4096 entries with the calculation 1 << ``bits``. + The number of bits used to index the entries in the Branch Target Buffer (BTB). For example, a ``bits`` value of 12 would index 4096 entries. The number of indices in the BTB is obtained from the calculation: 1 << ``bits``. Saturating-Count-Bits Only needed for a ``Generic`` predictor. The number of bits used in the saturating counter value. From 5413b637878aa6651eca5364052ed68ab9331039 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:16:58 +0000 Subject: [PATCH 18/20] doc changes --- docs/sphinx/developer/components/branchPred.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/developer/components/branchPred.rst b/docs/sphinx/developer/components/branchPred.rst index 6fbeca9fd5..ba91fe98d9 100644 --- a/docs/sphinx/developer/components/branchPred.rst +++ b/docs/sphinx/developer/components/branchPred.rst @@ -31,7 +31,7 @@ Branch Target Buffer (BTB) If the supplied branch type is ``Unconditional``, then the predicted direction is overridden to be taken. If the supplied branch type is ``Conditional`` and the predicted direction is not taken, then the predicted target is overridden to be the next sequential instruction. Return Address Stack (RAS) - Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by its corresponding Return instruction. + Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by the Branch-and-Link instruction's corresponding Return instruction. Static Prediction Based on the chosen static prediction method of "always taken" or "always not taken", the n-bit saturating counter value in the initial entries of the BTB structure are filled with the weakest variant of taken or not-taken respectively. @@ -51,4 +51,4 @@ Branch Target Buffer (BTB) If the supplied branch type is ``Unconditional``, then the predicted direction is overridden to be taken. If the supplied branch type is ``Conditional`` and the predicted direction is not taken, then the predicted target is overridden to be the next sequential instruction. Return Address Stack (RAS) - Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by its corresponding Return instruction. \ No newline at end of file + Identified through the supplied branch type, Return instructions pop values off of the RAS to get their branch target whilst Branch-and-Link instructions push values onto the RAS, for later use by the Branch-and-Link instruction's corresponding Return instruction. \ No newline at end of file From 5f2b5b8187b8899d249ad7c07fe3703d44304258 Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:19:52 +0000 Subject: [PATCH 19/20] Changes docs --- docs/sphinx/user/configuring_simeng.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/user/configuring_simeng.rst b/docs/sphinx/user/configuring_simeng.rst index a1cacc8b5a..39ee8cc97b 100644 --- a/docs/sphinx/user/configuring_simeng.rst +++ b/docs/sphinx/user/configuring_simeng.rst @@ -149,7 +149,7 @@ Type The type of branch predictor that is used, the options are ``Generic``, and ``Perceptron``. Both types of predictor use a branch target buffer with each entry containing a direction prediction mechanism and a target address. The direction predictor used in ``Generic`` is a saturating counter, and in ``Perceptron`` it is a perceptron. BTB-Tag-Bits - The number of bits used to index the entries in the Branch Target Buffer (BTB). For example, a ``bits`` value of 12 would index 4096 entries. The number of indices in the BTB is obtained from the calculation: 1 << ``bits``. + The number of bits used to index the entries in the Branch Target Buffer (BTB). The number of entries in the BTB is obtained from the calculation: 1 << ``bits``. For example, a ``bits`` value of 12 would result in a BTB with 4096 entries. Saturating-Count-Bits Only needed for a ``Generic`` predictor. The number of bits used in the saturating counter value. From 526f509e13d4d7d66929283f9e0ebcd1e4cfd5ff Mon Sep 17 00:00:00 2001 From: Alex Cockrean <84676155+ABenC377@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:20:11 +0000 Subject: [PATCH 20/20] Changes docs --- docs/sphinx/user/configuring_simeng.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/user/configuring_simeng.rst b/docs/sphinx/user/configuring_simeng.rst index 39ee8cc97b..e60a0a7372 100644 --- a/docs/sphinx/user/configuring_simeng.rst +++ b/docs/sphinx/user/configuring_simeng.rst @@ -149,7 +149,7 @@ Type The type of branch predictor that is used, the options are ``Generic``, and ``Perceptron``. Both types of predictor use a branch target buffer with each entry containing a direction prediction mechanism and a target address. The direction predictor used in ``Generic`` is a saturating counter, and in ``Perceptron`` it is a perceptron. BTB-Tag-Bits - The number of bits used to index the entries in the Branch Target Buffer (BTB). The number of entries in the BTB is obtained from the calculation: 1 << ``bits``. For example, a ``bits`` value of 12 would result in a BTB with 4096 entries. + The number of bits used to index the entries in the Branch Target Buffer (BTB). The number of entries in the BTB is obtained from the calculation: 1 << ``bits``. For example, a ``bits`` value of 12 would result in a BTB with 4096 entries. Saturating-Count-Bits Only needed for a ``Generic`` predictor. The number of bits used in the saturating counter value.