cpu: Added the Multiperspective Perceptron Predictor (8KB and 64KB)

Described by the following article:
  Jiménez, D. "Multiperspective perceptron predictor."
  Championship Branch Prediction (CBP-5) (2016).

Change-Id: Iaa68ead7696e0b6ba05b4417d0322e8053e10d30
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/15495
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Ilias Vougioukas <ilias.vougioukas@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py
index 9c59574..967489a 100644
--- a/src/cpu/pred/BranchPredictor.py
+++ b/src/cpu/pred/BranchPredictor.py
@@ -475,3 +475,85 @@
     tage = TAGE_SC_L_TAGE_8KB()
     loop_predictor = TAGE_SC_L_8KB_LoopPredictor()
     statistical_corrector = TAGE_SC_L_8KB_StatisticalCorrector()
+
+class MultiperspectivePerceptron(BranchPredictor):
+    type = 'MultiperspectivePerceptron'
+    cxx_class = 'MultiperspectivePerceptron'
+    cxx_header = 'cpu/pred/multiperspective_perceptron.hh'
+    abstract = True
+
+    num_filter_entries = Param.Int("Number of filter entries")
+    num_local_histories = Param.Int("Number of local history entries")
+    local_history_length = Param.Int(11,
+        "Length in bits of each history entry")
+
+    block_size = Param.Int(21,
+        "number of ghist bits in a 'block'; this is the width of an initial "
+        "hash of ghist")
+    pcshift = Param.Int(-10, "Shift for hashing PC")
+    threshold = Param.Int(1, "Threshold for deciding low/high confidence")
+    bias0 = Param.Int(-5,
+        "Bias perceptron output this much on all-bits-zero local history")
+    bias1 = Param.Int(5,
+        "Bias perceptron output this much on all-bits-one local history")
+    biasmostly0 = Param.Int(-1,
+        "Bias perceptron output this much on almost-all-bits-zero local "
+        "history")
+    biasmostly1 = Param.Int(1,
+        "Bias perceptron output this much on almost-all-bits-one local "
+        "history")
+    nbest = Param.Int(20,
+        "Use this many of the top performing tables on a low-confidence "
+        "branch")
+    tunebits = Param.Int(24, "Number of bits in misprediction counters")
+    hshift = Param.Int(-6,
+        "How much to shift initial feauture hash before XORing with PC bits")
+    imli_mask1 = Param.UInt64(
+        "Which tables should have their indices hashed with the first IMLI "
+        "counter")
+    imli_mask4 = Param.UInt64(
+        "Which tables should have their indices hashed with the fourth IMLI "
+        "counter")
+    recencypos_mask = Param.UInt64(
+        "Which tables should have their indices hashed with the recency "
+        "position")
+    fudge = Param.Float(0.245, "Fudge factor to multiply by perceptron output")
+    n_sign_bits = Param.Int(2, "Number of sign bits per magnitude")
+    pcbit = Param.Int(2, "Bit from the PC to use for hashing global history")
+    decay = Param.Int(0, "Whether and how often to decay a random weight")
+    record_mask = Param.Int(191,
+        "Which histories are updated with filtered branch outcomes")
+    hash_taken = Param.Bool(False,
+        "Hash the taken/not taken value with a PC bit")
+    tuneonly = Param.Bool(True,
+        "If true, only count mispredictions of low-confidence branches")
+    extra_rounds = Param.Int(1,
+        "Number of extra rounds of training a single weight on a "
+        "low-confidence prediction")
+    speed = Param.Int(9, "Adaptive theta learning speed")
+    initial_theta = Param.Int(10, "Initial theta")
+    budgetbits = Param.Int("Hardware budget in bits")
+    speculative_update = Param.Bool(False,
+        "Use speculative update for histories")
+
+class MultiperspectivePerceptron8KB(MultiperspectivePerceptron):
+    type = 'MultiperspectivePerceptron8KB'
+    cxx_class = 'MultiperspectivePerceptron8KB'
+    cxx_header = 'cpu/pred/multiperspective_perceptron_8KB.hh'
+    budgetbits = 8192 * 8 + 2048
+    num_local_histories = 48
+    num_filter_entries = 0
+    imli_mask1 = 0x6
+    imli_mask4 = 0x4400
+    recencypos_mask = 0x100000090
+
+class MultiperspectivePerceptron64KB(MultiperspectivePerceptron):
+    type = 'MultiperspectivePerceptron64KB'
+    cxx_class = 'MultiperspectivePerceptron64KB'
+    cxx_header = 'cpu/pred/multiperspective_perceptron_64KB.hh'
+    budgetbits = 65536 * 8 + 2048
+    num_local_histories = 510
+    num_filter_entries = 18025
+    imli_mask1 = 0xc1000
+    imli_mask4 = 0x80008000
+    recencypos_mask = 0x100000090
diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript
index 7365861..27821a4 100644
--- a/src/cpu/pred/SConscript
+++ b/src/cpu/pred/SConscript
@@ -48,6 +48,9 @@
 Source('tage.cc')
 Source('loop_predictor.cc')
 Source('ltage.cc')
+Source('multiperspective_perceptron.cc')
+Source('multiperspective_perceptron_8KB.cc')
+Source('multiperspective_perceptron_64KB.cc')
 Source('statistical_corrector.cc')
 Source('tage_sc_l.cc')
 Source('tage_sc_l_8KB.cc')
diff --git a/src/cpu/pred/multiperspective_perceptron.cc b/src/cpu/pred/multiperspective_perceptron.cc
new file mode 100644
index 0000000..47bbb02
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron.cc
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+ /*
+  * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+  */
+
+#include "cpu/pred/multiperspective_perceptron.hh"
+
+#include "debug/Branch.hh"
+
+int
+MultiperspectivePerceptron::xlat[] =
+    {1,3,4,5,7,8,9,11,12,14,15,17,19,21,23,25,27,29,32,34,37,41,45,49,53,58,63,
+     69,76,85,94,106,};
+int
+MultiperspectivePerceptron::xlat4[] =
+    {0,4,5,7,9,11,12,14,16,17,19,22,28,33,39,45,};
+
+MultiperspectivePerceptron::ThreadData::ThreadData(int num_filters,
+        int n_local_histories, int local_history_length, int assoc,
+        const std::vector<std::vector<int>> &blurrypath_bits, int path_length,
+        int ghist_length, int block_size,
+        const std::vector<std::vector<std::vector<bool>>> &acyclic_bits,
+        const std::vector<int> &modhist_indices,
+        const std::vector<int> &modhist_lengths,
+        const std::vector<int> &modpath_indices,
+        const std::vector<int> &modpath_lengths,
+        const std::vector<int> &table_sizes, int n_sign_bits)
+      : filterTable(num_filters), acyclic_histories(acyclic_bits.size()),
+        acyclic2_histories(acyclic_bits.size()),
+        blurrypath_histories(blurrypath_bits.size()),
+        ghist_words(ghist_length/block_size+1, 0),
+        path_history(path_length, 0), imli_counter(4,0),
+        localHistories(n_local_histories, local_history_length),
+        recency_stack(assoc), last_ghist_bit(false), occupancy(0)
+{
+    for (int i = 0; i < blurrypath_bits.size(); i+= 1) {
+        blurrypath_histories[i].resize(blurrypath_bits[i].size());
+    }
+
+    for (int i = 0; i < acyclic_bits.size(); i += 1) {
+        acyclic_histories[i].resize(acyclic_bits[i].size());
+        acyclic2_histories[i].resize(acyclic_bits[i].size());
+    }
+
+    int max_modhist_idx = -1;
+    for (auto &elem : modhist_indices) {
+        max_modhist_idx = (max_modhist_idx < elem) ? elem : max_modhist_idx;
+    }
+    if (max_modhist_idx >= 0) {
+        mod_histories.resize(max_modhist_idx + 1);
+    }
+    for (int i = 0; i < modhist_lengths.size(); i+= 1) {
+        mod_histories[modhist_indices[i]].resize(modhist_lengths[i]);
+    }
+
+    int max_modpath_idx = -1;
+    for (auto &elem : modpath_indices) {
+        max_modpath_idx = (max_modpath_idx < elem) ? elem : max_modpath_idx;
+    }
+    if (max_modpath_idx >= 0) {
+        modpath_histories.resize(max_modpath_idx + 1);
+    }
+    for (int i = 0; i < modpath_lengths.size(); i+= 1) {
+        modpath_histories[modpath_indices[i]].resize(modpath_lengths[i]);
+    }
+
+    for (int i = 0; i < table_sizes.size(); i += 1) {
+        mpreds.push_back(0);
+        tables.push_back(std::vector<short int>(table_sizes[i]));
+        sign_bits.push_back(std::vector<std::array<bool, 2>>(table_sizes[i]));
+        for (int j = 0; j < table_sizes[i]; j += 1) {
+            for (int k = 0; k < n_sign_bits; k += 1) {
+                sign_bits[i][j][k] = (i & 1) | (k & 1);
+            }
+        }
+    }
+}
+
+MultiperspectivePerceptron::MultiperspectivePerceptron(
+    const MultiperspectivePerceptronParams *p) : BPredUnit(p),
+    blockSize(p->block_size), pcshift(p->pcshift), threshold(p->threshold),
+    bias0(p->bias0), bias1(p->bias1), biasmostly0(p->biasmostly0),
+    biasmostly1(p->biasmostly1), nbest(p->nbest), tunebits(p->tunebits),
+    hshift(p->hshift), imli_mask1(p->imli_mask1), imli_mask4(p->imli_mask4),
+    recencypos_mask(p->recencypos_mask), fudge(p->fudge),
+    n_sign_bits(p->n_sign_bits), pcbit(p->pcbit), decay(p->decay),
+    record_mask(p->record_mask), hash_taken(p->hash_taken),
+    tuneonly(p->tuneonly), extra_rounds(p->extra_rounds), speed(p->speed),
+    budgetbits(p->budgetbits), speculative_update(p->speculative_update),
+    threadData(p->numThreads, nullptr), doing_local(false),
+    doing_recency(false), assoc(0), ghist_length(1), modghist_length(1),
+    path_length(1), randSeed(0xdeadbeef), thresholdCounter(0),
+    theta(p->initial_theta), imli_counter_bits(4), modhist_indices(),
+    modhist_lengths(), modpath_indices(), modpath_lengths()
+{
+    fatal_if(speculative_update, "Speculative update not implemented");
+}
+
+void
+MultiperspectivePerceptron::init()
+{
+    createSpecs();
+
+    for (auto &spec : specs) {
+        // initial assignation of values
+        table_sizes.push_back(spec->size);
+    }
+
+    // Update bit requirements and runtime values
+    for (auto &spec : specs) {
+        spec->setBitRequirements();
+    }
+    const MultiperspectivePerceptronParams *p =
+        static_cast<const MultiperspectivePerceptronParams *>(params());
+
+    computeBits(p->num_filter_entries, p->num_local_histories,
+                p->local_history_length);
+
+    for (int i = 0; i < threadData.size(); i += 1) {
+        threadData[i] = new ThreadData(p->num_filter_entries,
+                                       p->num_local_histories,
+                                       p->local_history_length, assoc,
+                                       blurrypath_bits, path_length,
+                                       ghist_length, blockSize, acyclic_bits,
+                                       modhist_indices, modhist_lengths,
+                                       modpath_indices, modpath_lengths,
+                                       table_sizes, n_sign_bits);
+    }
+}
+
+void
+MultiperspectivePerceptron::computeBits(int num_filter_entries,
+        int nlocal_histories, int local_history_length) {
+    int totalbits = 0;
+    for (auto &imli_bits : imli_counter_bits) {
+        totalbits += imli_bits;
+    }
+    totalbits += ghist_length;
+    totalbits += path_length * 16;
+    totalbits += (threshold >= 0) ? (tunebits * specs.size()) : 0;
+    for (auto &len : modhist_lengths) {
+        totalbits += len;
+    }
+    for (auto &len : modpath_lengths) {
+        totalbits += 16 * len;
+    }
+    totalbits += doing_local ? (nlocal_histories * local_history_length) : 0;
+    totalbits += doing_recency ? (assoc * 16) : 0;
+
+    for (auto &bv : blurrypath_bits) {
+        for (auto &bve : bv) {
+            totalbits += bve;
+        }
+    }
+    totalbits += num_filter_entries * 2;
+
+    for (auto &abi : acyclic_bits) {
+        for (auto &abj : abi) {
+            for (auto abk : abj) {
+                totalbits += abk;
+            }
+        }
+    }
+
+    int remaining = budgetbits - totalbits;
+
+    // count the tables that have already been assigned sizes
+    int num_sized = 0;
+    for (int i = 0; i < specs.size(); i +=1) {
+        if (table_sizes[i] != 0) {
+            int sz = table_sizes[i] * (specs[i]->width + (n_sign_bits - 1));
+            totalbits += sz;
+            remaining -= sz;
+            num_sized += 1;
+        }
+    }
+
+    // whatever is left, we divide among the rest of the tables
+    int table_size_bits = (remaining / (specs.size()-num_sized));
+    for (int i = 0; i < specs.size(); i += 1) {
+        // if a table doesn't have a size yet, give it one and count those bits
+        if (!table_sizes[i]) {
+            int my_table_size = table_size_bits /
+                (specs[i]->width + (n_sign_bits - 1)); // extra sign bits
+            table_sizes[i] = my_table_size;
+            totalbits += my_table_size * (specs[i]->width + (n_sign_bits - 1));
+        }
+    }
+
+    DPRINTF(Branch, "%d bits of metadata so far, %d left out of "
+            "%d total budget\n", totalbits, remaining, budgetbits);
+    DPRINTF(Branch, "table size is %d bits, %d entries for 5 bit, %d entries "
+            "for 6 bit\n", table_size_bits,
+            table_size_bits / (5 + (n_sign_bits - 1)),
+            table_size_bits / (6 + (n_sign_bits - 1)));
+    DPRINTF(Branch, "%d total bits (%0.2fKB)\n", totalbits,
+            totalbits / 8192.0);
+}
+
+void
+MultiperspectivePerceptron::findBest(ThreadID tid,
+                                     std::vector<int> &best_preds) const
+{
+    if (threshold < 0) {
+        return;
+    }
+    struct BestPair {
+        int index;
+        int mpreds;
+        bool operator<(BestPair const &bp) const
+        {
+            return mpreds < bp.mpreds;
+        }
+    };
+    std::vector<BestPair> pairs(best_preds.size());
+    for (int i = 0; i < best_preds.size(); i += 1) {
+        pairs[i].index = i;
+        pairs[i].mpreds = threadData[tid]->mpreds[i];
+    }
+    std::sort(pairs.begin(), pairs.end());
+    for (int i = 0; i < (std::min(nbest, (int) best_preds.size())); i += 1) {
+        best_preds[i] = pairs[i].index;
+    }
+}
+
+unsigned int
+MultiperspectivePerceptron::getIndex(ThreadID tid, const MPPBranchInfo &bi,
+                                     const HistorySpec &spec, int index) const
+{
+    unsigned int g = spec.getHash(tid, bi.getPC(), bi.getPC2(), index);
+    unsigned long long int h = g;
+     // shift the hash from the feature to xor with the hashed PC
+    if (hshift < 0) {
+        h <<= -hshift;
+        h ^= bi.getPC2();
+    } else {
+        h <<= hshift;
+        h ^= bi.getHPC();
+    }
+    // xor in the imli counter(s) and/or recency position based on the masks
+    if ((1ull<<index) & imli_mask1) {
+        h ^= threadData[tid]->imli_counter[0];
+    }
+    if ((1ull<<index) & imli_mask4) {
+        h ^= threadData[tid]->imli_counter[3];
+    }
+    if (doing_recency) {
+        if ((1ull<<index) & recencypos_mask) {
+            h ^= RECENCYPOS::hash(threadData[tid]->recency_stack, table_sizes,
+                    bi.getPC2(), 31, index);
+        }
+    }
+    h %= table_sizes[index];
+    return h;
+}
+
+int
+MultiperspectivePerceptron::computeOutput(ThreadID tid, MPPBranchInfo &bi)
+{
+    // list of best predictors
+    std::vector<int> best_preds(specs.size(), -1);
+
+    // initialize sum
+    bi.yout = 0;
+
+    // bias the prediction by whether the local history is
+    // one of four distinctive patterns
+    int lhist = threadData[tid]->localHistories[bi.getPC()];
+    int history_len = threadData[tid]->localHistories.getLocalHistoryLength();
+    if (lhist == 0) {
+        bi.yout = bias0;
+    } else if (lhist == ((1<<history_len)-1)) {
+        bi.yout = bias1;
+    } else if (lhist == (1<<(history_len-1))) {
+        bi.yout = biasmostly0;
+    } else if (lhist == ((1<<(history_len-1))-1)) {
+        bi.yout = biasmostly1;
+    }
+    // find the best subset of features to use in case of a low-confidence
+    // branch
+    findBest(tid, best_preds);
+
+    // begin computation of the sum for low-confidence branch
+    int bestval = 0;
+
+    for (int i = 0; i < specs.size(); i += 1) {
+        HistorySpec const &spec = *specs[i];
+        // get the hash to index the table
+        unsigned int hashed_idx = getIndex(tid, bi, spec, i);
+        // add the weight; first get the weight's magnitude
+        int counter = threadData[tid]->tables[i][hashed_idx];
+        // get the sign
+        bool sign =
+          threadData[tid]->sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits];
+        // apply the transfer function and multiply by a coefficient
+        int weight = spec.coeff * ((spec.width == 5) ?
+                                   xlat4[counter] : xlat[counter]);
+        // apply the sign
+        int val = sign ? -weight : weight;
+        // add the value
+        bi.yout += val;
+        // if this is one of those good features, add the value to bestval
+        if (threshold >= 0) {
+            for (int j = 0;
+                 j < std::min(nbest, (int) best_preds.size());
+                 j += 1)
+            {
+                if (best_preds[j] == i) {
+                    bestval += val;
+                    break;
+                }
+            }
+        }
+    }
+    // apply a fudge factor to affect when training is triggered
+    bi.yout *= fudge;
+    return bestval;
+}
+
+void
+MultiperspectivePerceptron::satIncDec(bool taken, bool &sign, int &counter,
+                                      int max_weight) const
+{
+    if (taken) {
+        // increment sign/magnitude
+        if (sign) {
+            // go toward 0 away from negative max weight
+            if (counter == 0) {
+                sign = false; // moved to positive 0
+            } else {
+                counter -= 1;
+            }
+        } else {
+            // go toward max weight away from 0
+            if (counter < max_weight) {
+                counter += 1;
+            }
+        }
+    } else {
+        // decrement sign/magnitude
+        if (sign) {
+            // go toward negative max weight down from 0
+            if (counter < max_weight) {
+                counter += 1;
+            }
+        } else {
+            // go toward 0 away from max weight
+            if (counter == 0) {
+                sign = true; // negative 0
+            } else {
+                counter -= 1;
+            }
+        }
+    }
+}
+
+void
+MultiperspectivePerceptron::train(ThreadID tid, MPPBranchInfo &bi, bool taken)
+{
+    std::vector<std::vector<short int>> &tables = threadData[tid]->tables;
+    std::vector<std::vector<std::array<bool, 2>>> &sign_bits =
+            threadData[tid]->sign_bits;
+    std::vector<int> &mpreds = threadData[tid]->mpreds;
+    // was the prediction correct?
+    bool correct = (bi.yout >= 1) == taken;
+    // what is the magnitude of yout?
+    int abs_yout = abs(bi.yout);
+    // keep track of mispredictions per table
+    if (threshold >= 0) if (!tuneonly || (abs_yout <= threshold)) {
+        bool halve = false;
+
+        // for each table, figure out if there was a misprediction
+        for (int i = 0; i < specs.size(); i += 1) {
+            HistorySpec const &spec = *specs[i];
+            // get the hash to index the table
+            unsigned int hashed_idx = getIndex(tid, bi, spec, i);
+            bool sign = sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits];
+            int counter = tables[i][hashed_idx];
+            int weight = spec.coeff * ((spec.width == 5) ?
+                                       xlat4[counter] : xlat[counter]);
+            if (sign) weight = -weight;
+            bool pred = weight >= 1;
+            if (pred != taken) {
+                mpreds[i] += 1;
+                if (mpreds[i] == (1 << tunebits) - 1) {
+                    halve = true;
+                }
+            }
+        }
+        // if we reach the maximum counter value, halve all the counters
+        if (halve) {
+            for (int i = 0; i < specs.size(); i += 1) {
+                mpreds[i] /= 2;
+            }
+        }
+    }
+    // if the branch was predicted incorrectly or the correct
+    // prediction was weak, update the weights
+    bool do_train = !correct || (abs_yout <= theta);
+    if (!do_train) return;
+
+    // adaptive theta training, adapted from O-GEHL
+    if (!correct) {
+        thresholdCounter += 1;
+        if (thresholdCounter >= speed) {
+            theta += 1;
+            thresholdCounter = 0;
+        }
+    }
+    if (correct && abs_yout < theta) {
+        thresholdCounter -= 1;
+        if (thresholdCounter <= -speed) {
+            theta -= 1;
+            thresholdCounter = 0;
+        }
+    }
+
+    // train the weights, computing what the value of yout
+    // would have been if these updates had been applied before
+    int newyout = 0;
+    for (int i = 0; i < specs.size(); i += 1) {
+        HistorySpec const &spec = *specs[i];
+        // get the magnitude
+        unsigned int hashed_idx = getIndex(tid, bi, spec, i);
+        int counter = tables[i][hashed_idx];
+        // get the sign
+        bool sign = sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits];
+        // increment/decrement if taken/not taken
+        satIncDec(taken, sign, counter, (1 << (spec.width - 1)) - 1);
+        // update the magnitude and sign
+        tables[i][hashed_idx] = counter;
+        sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits] = sign;
+        int weight = ((spec.width == 5) ? xlat4[counter] : xlat[counter]);
+        // update the new version of yout
+        if (sign) {
+            newyout -= weight;
+        } else {
+            newyout += weight;
+        }
+    }
+
+    // if the prediction still would have been incorrect even
+    // with the updated weights, update some more weights to
+    // try to fix the problem
+    if ((newyout >= 1) != taken) {
+        if (extra_rounds != -1) {
+            int round_counter = 0;
+            bool found;
+            do {
+                // udpate a random weight
+                int besti = -1;
+                int nrand = rand_r(&randSeed) % specs.size();
+                int pout;
+                found = false;
+                for (int j = 0; j < specs.size(); j += 1) {
+                    int i = (nrand + j) % specs.size();
+                    HistorySpec const &spec = *specs[i];
+                    unsigned int hashed_idx = getIndex(tid, bi, spec, i);
+                    int counter = tables[i][hashed_idx];
+                    bool sign =
+                        sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits];
+                    int weight = ((spec.width == 5) ?
+                            xlat4[counter] : xlat[counter]);
+                    int signed_weight = sign ? -weight : weight;
+                    pout = newyout - signed_weight;
+                    if ((pout >= 1) == taken) {
+                        // we have found a weight that if we blow
+                        // it away will help!
+                        besti = i;
+                        break;
+                    }
+                }
+                if (besti != -1) {
+                    int i = besti;
+                    HistorySpec const &spec = *specs[i];
+                    unsigned int hashed_idx = getIndex(tid, bi, spec, i);
+                    int counter = tables[i][hashed_idx];
+                    bool sign =
+                        sign_bits[i][hashed_idx][bi.getHPC() % n_sign_bits];
+                    if (counter > 1) {
+                        counter--;
+                        tables[i][hashed_idx] = counter;
+                    }
+                    int weight = ((spec.width == 5) ?
+                            xlat4[counter] : xlat[counter]);
+                    int signed_weight = sign ? -weight : weight;
+                    int out = pout + signed_weight;
+                    round_counter += 1;
+                    if ((out >= 1) != taken) {
+                        found = true;
+                    }
+                }
+            } while (found && round_counter < extra_rounds);
+        }
+    }
+}
+
+void
+MultiperspectivePerceptron::uncondBranch(ThreadID tid, Addr pc,
+                                         void * &bp_history)
+{
+    MPPBranchInfo *bi = new MPPBranchInfo(pc, pcshift, false);
+    std::vector<unsigned int> &ghist_words = threadData[tid]->ghist_words;
+
+    bp_history = (void *)bi;
+    unsigned short int pc2 = pc >> 2;
+    bool ab = !(pc & (1<<pcbit));
+    for (int i = 0; i < ghist_length / blockSize + 1; i += 1) {
+        bool ab_new = (ghist_words[i] >> (blockSize - 1)) & 1;
+        ghist_words[i] <<= 1;
+        ghist_words[i] |= ab;
+        ghist_words[i] &= (1 << blockSize) - 1;
+        ab = ab_new;
+    }
+    memmove(&threadData[tid]->path_history[1],
+            &threadData[tid]->path_history[0],
+            sizeof(unsigned short int) * (path_length - 1));
+    threadData[tid]->path_history[0] = pc2;
+}
+
+bool
+MultiperspectivePerceptron::lookup(ThreadID tid, Addr instPC,
+                                   void * &bp_history)
+{
+    MPPBranchInfo *bi = new MPPBranchInfo(instPC, pcshift, true);
+    bp_history = (void *)bi;
+
+    bool use_static = false;
+
+    if (!threadData[tid]->filterTable.empty()) {
+        unsigned int findex =
+            bi->getHashFilter(threadData[tid]->last_ghist_bit) %
+            threadData[tid]->filterTable.size();
+        FilterEntry &f = threadData[tid]->filterTable[findex];
+        if (f.alwaysNotTakenSoFar()) {
+            bi->filtered = true;
+            bi->prediction = false;
+            return false;
+        } else if (f.alwaysTakenSoFar()) {
+            bi->filtered = true;
+            bi->prediction = true;
+            return true;
+        }
+        if (f.neverSeen()) {
+            use_static = true;
+        }
+    }
+
+    int bestval = computeOutput(tid, *bi);
+    if (use_static) {
+        bi->prediction = false;
+    } else {
+        if (abs(bi->yout) <= threshold) {
+            bi->prediction = (bestval >= 1);
+        } else {
+            bi->prediction = (bi->yout >= 1);
+        }
+    }
+
+    return bi->prediction;
+}
+
+void
+MultiperspectivePerceptron::update(ThreadID tid, Addr instPC, bool taken,
+                                   void *bp_history, bool squashed,
+                                   const StaticInstPtr & inst,
+                                   Addr corrTarget)
+{
+    assert(bp_history);
+    MPPBranchInfo *bi = static_cast<MPPBranchInfo*>(bp_history);
+    assert(corrTarget != MaxAddr);
+    if (squashed) {
+        //delete bi;
+        return;
+    }
+
+    if (bi->isUnconditional()) {
+        delete bi;
+        return;
+    }
+
+    bool do_train = true;
+
+    if (!threadData[tid]->filterTable.empty()) {
+        int findex = bi->getHashFilter(threadData[tid]->last_ghist_bit) %
+                                       threadData[tid]->filterTable.size();
+        FilterEntry &f = threadData[tid]->filterTable[findex];
+
+        // compute this first, so we don't not train on the
+        // first time a branch is seen.
+        bool transition = false;
+        if (f.alwaysNotTakenSoFar() || f.alwaysTakenSoFar()) {
+            do_train = false;
+        }
+        if (taken) {
+            if (f.alwaysNotTakenSoFar()) {
+                transition = true;
+            }
+            f.seenTaken = true;
+        } else {
+            if (f.alwaysTakenSoFar()) {
+                transition = true;
+            }
+            f.seenUntaken = true;
+        }
+        // is this the first time time the branch has gone both ways?
+        if (transition) {
+            threadData[tid]->occupancy += 1;
+        }
+        // for every new dynamic branch, when there ar
+        // more than 'decay' number of branches in the
+        // filter, blow a random filter entry away
+        if (decay && transition &&
+            ((threadData[tid]->occupancy > decay) || (decay == 1))) {
+            int rnd = rand_r(&randSeed) % threadData[tid]->filterTable.size();
+            FilterEntry &frand = threadData[tid]->filterTable[rnd];
+            if (frand.seenTaken && frand.seenUntaken) {
+                threadData[tid]->occupancy -= 1;
+            }
+            frand.seenTaken = false;
+            frand.seenUntaken = false;
+        }
+    }
+
+    if (do_train) {
+        train(tid, *bi, taken);
+    }
+
+#define RECORD_FILTERED_IMLI      1
+#define RECORD_FILTERED_GHIST     2
+#define RECORD_FILTERED_PATH      4
+#define RECORD_FILTERED_ACYCLIC   8
+#define RECORD_FILTERED_MOD      16
+#define RECORD_FILTERED_BLURRY   32
+// should never record a filtered local branch - duh!
+#define RECORD_FILTERED_LOCAL    64
+#define RECORD_FILTERED_RECENCY 128
+
+    // four different styles of IMLI
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_IMLI)) {
+        unsigned int target = corrTarget;
+        if (target < bi->getPC()) {
+            if (taken) {
+                threadData[tid]->imli_counter[0] += 1;
+            } else {
+                threadData[tid]->imli_counter[0] = 0;
+            }
+            if (!taken) {
+                threadData[tid]->imli_counter[1] += 1;
+            } else {
+                threadData[tid]->imli_counter[1] = 0;
+            }
+        } else {
+            if (taken) {
+                threadData[tid]->imli_counter[2] += 1;
+            } else {
+                threadData[tid]->imli_counter[2] = 0;
+            }
+            if (!taken) {
+                threadData[tid]->imli_counter[3] += 1;
+            } else {
+                threadData[tid]->imli_counter[3] = 0;
+            }
+        }
+    }
+
+    bool hashed_taken = hash_taken ? (taken ^ !!(bi->getPC() & (1<<pcbit)))
+                                   : taken;
+    // record into ghist
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_GHIST)) {
+        bool ab = hashed_taken;
+        assert(threadData[tid]->ghist_words.size() > 0);
+        for (int i = 0; i < ghist_length / blockSize + 1; i += 1) {
+            unsigned int a = threadData[tid]->ghist_words[i];
+            bool ab_new = (a >> (blockSize - 1)) & 1;
+            a <<= 1;
+            a |= ab;
+            ab = ab_new;
+            a &= (1 << blockSize) - 1;
+            threadData[tid]->ghist_words[i] = a;
+        }
+    }
+
+    // record into path history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_PATH)) {
+        assert(threadData[tid]->path_history.size() > 0);
+        memmove(&threadData[tid]->path_history[1],
+                &threadData[tid]->path_history[0],
+                sizeof(unsigned short int) * (path_length - 1));
+        threadData[tid]->path_history[0] = bi->getPC2();
+    }
+
+    // record into acyclic history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_ACYCLIC)) {
+        threadData[tid]->updateAcyclic(hashed_taken, bi->getHPC());
+    }
+
+    // record into modulo path history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_MOD)) {
+        for (int ii = 0; ii < modpath_indices.size(); ii += 1) {
+            int i = modpath_indices[ii];
+            if (bi->getHPC() % (i + 2) == 0) {
+                memmove(&threadData[tid]->modpath_histories[i][1],
+                        &threadData[tid]->modpath_histories[i][0],
+                        sizeof(unsigned short int) * (modpath_lengths[ii]-1));
+                threadData[tid]->modpath_histories[i][0] = bi->getPC2();
+            }
+        }
+    }
+
+    // update blurry history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_BLURRY)) {
+        std::vector<std::vector<unsigned int>> &blurrypath_histories =
+             threadData[tid]->blurrypath_histories;
+
+        for (int i = 0; i < blurrypath_histories.size(); i += 1)
+        {
+            if (blurrypath_histories[i].size() > 0) {
+                unsigned int z = bi->getPC() >> i;
+                if (blurrypath_histories[i][0] != z) {
+                    memmove(&blurrypath_histories[i][1],
+                            &blurrypath_histories[i][0],
+                            sizeof(unsigned int) *
+                            (blurrypath_histories[i].size() - 1));
+                    blurrypath_histories[i][0] = z;
+                }
+            }
+        }
+    }
+
+    // record into modulo pattern history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_MOD)) {
+        for (int ii = 0; ii < modhist_indices.size(); ii += 1) {
+            int i = modhist_indices[ii];
+            if (bi->getHPC() % (i + 2) == 0) {
+                for (int j = modhist_lengths[ii] - 1; j > 0; j -= 1) {
+                    threadData[tid]->mod_histories[i][j] =
+                        threadData[tid]->mod_histories[i][j-1];
+                }
+                threadData[tid]->mod_histories[i][0] = hashed_taken;
+            }
+        }
+    }
+
+    // insert this PC into the recency stack
+    if (doing_recency) {
+        if (!bi->filtered || (record_mask & RECORD_FILTERED_RECENCY)) {
+            threadData[tid]->insertRecency(bi->getPC2(), assoc);
+        }
+    }
+
+    // record into a local history
+    if (!bi->filtered || (record_mask & RECORD_FILTERED_LOCAL)) {
+        threadData[tid]->localHistories.update(bi->getPC(), hashed_taken);
+    }
+
+    // update last ghist bit, used to index filter
+    threadData[tid]->last_ghist_bit = taken;
+
+    delete bi;
+}
+
+void
+MultiperspectivePerceptron::btbUpdate(ThreadID tid, Addr branch_pc,
+                                      void* &bp_history)
+{
+}
+
+void
+MultiperspectivePerceptron::squash(ThreadID tid, void *bp_history)
+{
+    assert(bp_history);
+    MPPBranchInfo *bi = static_cast<MPPBranchInfo*>(bp_history);
+    delete bi;
+}
diff --git a/src/cpu/pred/multiperspective_perceptron.hh b/src/cpu/pred/multiperspective_perceptron.hh
new file mode 100644
index 0000000..e81bd8e
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron.hh
@@ -0,0 +1,1027 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+/*
+ * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+ */
+
+#ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__
+#define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__
+
+#include <array>
+#include <vector>
+
+#include "cpu/pred/bpred_unit.hh"
+#include "params/MultiperspectivePerceptron.hh"
+
+class MultiperspectivePerceptron : public BPredUnit
+{
+  protected:
+    /**
+     * Branch information data
+     */
+    class MPPBranchInfo {
+        /** pc of the branch */
+        const unsigned int pc;
+        /** pc of the branch, shifted 2 bits to the right */
+        const unsigned short int pc2;
+        /** pc of the branch, hashed */
+        const unsigned short int hpc;
+        /** Whether this is a conditional branch */
+        const bool condBranch;
+
+        /**
+         * PC Hash functions
+         */
+        static inline unsigned int hash1(unsigned int a)
+        {
+            a = (a ^ 0xdeadbeef) + (a<<4);
+            a = a ^ (a>>10);
+            a = a + (a<<7);
+            a = a ^ (a>>13);
+            return a;
+        }
+
+        static inline unsigned int hash2(unsigned int key)
+        {
+            int c2 = 0x27d4eb2d; // a prime or an odd constant
+            key = (key ^ 61) ^ (key >> 16);
+            key = key + (key << 3);
+            key = key ^ (key >> 4);
+            key = key * c2;
+            key = key ^ (key >> 15);
+            return key;
+        }
+
+        static inline unsigned int hash(unsigned int key, unsigned int i)
+        {
+            return hash2(key) * i + hash1(key);
+        }
+
+        static inline unsigned int hashPC(unsigned int pc, int pcshift)
+        {
+            if (pcshift < 0) {
+                return hash(pc, -pcshift);
+            } else if (pcshift < 11) {
+                unsigned int x = pc;
+                x ^= (pc >> pcshift);
+                return x;
+            } else {
+                return pc >> (pcshift-11);
+            }
+        }
+
+      public:
+        /** Whether this branch has been filtered by the prefetcher */
+        bool filtered;
+        /** Result of the prediction (true is taken) */
+        bool prediction;
+        /** Score of the perceptron */
+        int yout;
+
+        MPPBranchInfo(Addr _pc, int pcshift, bool cb) : pc((unsigned int)_pc),
+        pc2(pc >> 2), hpc(hashPC(pc, pcshift)), condBranch(cb),
+        filtered(false), prediction(false), yout(0)
+        { }
+
+        unsigned int getPC() const
+        {
+            return pc;
+        }
+        unsigned short int getPC2() const
+        {
+            return pc2;
+        }
+        unsigned short int getHPC() const
+        {
+            return hpc;
+        }
+        unsigned int getHashFilter(bool last_ghist_bit) const
+        {
+            return last_ghist_bit ^ hpc;
+        }
+        bool isUnconditional() const
+        {
+            return !condBranch;
+        }
+    };
+
+    /**
+     * Entry of the branch filter
+     */
+    struct FilterEntry {
+        /** Has this branch been taken at least once? */
+        bool seenTaken;
+        /** Has this branch been not taken at least once? */
+        bool seenUntaken;
+
+        FilterEntry() : seenTaken(false), seenUntaken(false) {}
+
+        /** Whether this branch has always been observed as not taken */
+        bool alwaysNotTakenSoFar() const {
+            return seenUntaken & !seenTaken;
+        }
+        /** Whether this branch has always been observed as taken */
+        bool alwaysTakenSoFar() const {
+            return seenTaken & !seenUntaken;
+        }
+        /** Whether this branch has been observed before */
+        bool neverSeen() const {
+            return !seenTaken && !seenUntaken;
+        }
+    };
+
+
+    /**
+     * Local history entries, each enty contains the history of directions
+     * taken by a given branch.
+     */
+    class LocalHistories {
+        /** The array of histories */
+        std::vector<unsigned int> localHistories;
+        /** Size in bits of each history entry */
+        const int localHistoryLength;
+
+        /** Index function given the pc of the branch */
+        unsigned int index(Addr pc) const {
+            return (pc >> 2) % localHistories.size();
+        }
+        public:
+        LocalHistories(int nlocal_histories, int histo_len) :
+            localHistories(nlocal_histories), localHistoryLength(histo_len) {}
+
+        /** Obtains the local history entry of a given branch */
+        unsigned int operator[](Addr pc) const
+        {
+            return localHistories[index(pc)];
+        }
+
+        /** Adds a history bit to the local history entry of a given branch */
+        void update(Addr pc, bool value)
+        {
+            assert(localHistories.size() > 0);
+            unsigned int &pos = localHistories[index(pc)];
+            pos <<= 1;
+            pos |= value;
+            pos &= ((1<<localHistoryLength)-1);
+        }
+
+        /** Returns the number of bits of each local history entry */
+        int getLocalHistoryLength() const
+        {
+            return localHistoryLength;
+        }
+
+        /** Size in bits required by all history entries */
+        int getSize() const
+        {
+            return localHistoryLength * localHistories.size();
+        }
+    };
+
+    /**
+     * Base class to implement the predictor tables.
+     */
+    struct HistorySpec {
+        /** First parameter */
+        const int p1;
+        /** Second parameter */
+        const int p2;
+        /** Third parameter */
+        const int p3;
+        /** Coefficient of the feature, models the accuracy of the feature */
+        const double coeff;
+        /** Pre-assigned size in bits assigned to this feature */
+        const int size;
+        /** Width of the table in bits  */
+        const int width;
+        /** Reference to the branch predictor class */
+        MultiperspectivePerceptron &mpp;
+
+        HistorySpec(int _p1, int _p2, int _p3, double _coeff, int _size,
+                int _width, MultiperspectivePerceptron &_mpp) : p1(_p1),
+        p2(_p2), p3(_p3), coeff(_coeff), size(_size), width(_width),
+        mpp(_mpp)
+        {}
+
+        /**
+         * Gets the hash to index the table, using the pc of the branch,
+         * and the index of the table.
+         * @param tid Thread ID of the branch
+         * @param pc address of the branch
+         * @param pc2 address of the branch shifted 2 bits to the right
+         * @param t integer index of the table
+         * @result resulting hash value that will be used to index the table
+         */
+        virtual unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t)
+            const = 0;
+        /**
+         * Sets the size requirements of the table, used when initializing
+         * to set the proper size of the tables
+         */
+        virtual void setBitRequirements() const {}
+    };
+
+    /** Predictor parameters */
+    const int blockSize;
+    const int pcshift;
+    const int threshold;
+    const int bias0;
+    const int bias1;
+    const int biasmostly0;
+    const int biasmostly1;
+    const int nbest;
+    const int tunebits;
+    const int hshift;
+    const unsigned long long int imli_mask1;
+    const unsigned long long int imli_mask4;
+    const unsigned long long int recencypos_mask;
+    const double fudge;
+    const int n_sign_bits;
+    const int pcbit;
+    const int decay;
+    const unsigned int record_mask;
+    const bool hash_taken;
+    const bool tuneonly;
+    const int extra_rounds;
+    const int speed;
+    const int budgetbits;
+    const bool speculative_update;
+
+    /** Transfer function for 6-width tables */
+    static int xlat[];
+    /** Transfer function for 5-width tables */
+    static int xlat4[];
+
+    /** History data is kept for each thread */
+    struct ThreadData {
+        ThreadData(int num_filter, int n_local_histories,
+            int local_history_length, int assoc,
+            const std::vector<std::vector<int>> &blurrypath_bits,
+            int path_length, int ghist_length, int block_size,
+            const std::vector<std::vector<std::vector<bool>>> &acyclic_bits,
+            const std::vector<int> &modhist_indices,
+            const std::vector<int> &modhist_lengths,
+            const std::vector<int> &modpath_indices,
+            const std::vector<int> &modpath_lengths,
+            const std::vector<int> &table_sizes, int n_sign_bits);
+
+        std::vector<FilterEntry> filterTable;
+        std::vector<std::vector<bool>> acyclic_histories;
+        std::vector<std::vector<unsigned int>> acyclic2_histories;
+
+        void updateAcyclic(bool hashed_taken, unsigned int hpc) {
+            for (int i = 0; i < acyclic_histories.size(); i += 1) {
+                if (acyclic_histories[i].size() > 0) {
+                    acyclic_histories[i][hpc%(i+2)] = hashed_taken;
+                    acyclic2_histories[i][hpc%(i+2)] = hpc;
+                }
+            }
+        }
+
+        std::vector<std::vector<unsigned int>> blurrypath_histories;
+        std::vector<unsigned int> ghist_words;
+        std::vector<std::vector<unsigned short int>> modpath_histories;
+        std::vector<std::vector<bool>> mod_histories;
+        std::vector<unsigned short int> path_history;
+        std::vector<unsigned int> imli_counter;
+        LocalHistories localHistories;
+        std::vector<unsigned int short> recency_stack;
+
+        void insertRecency(unsigned int pc, int assoc) {
+            int i = 0;
+            for (i = 0; i < assoc; i += 1) {
+                if (recency_stack[i] == pc) {
+                    break;
+                }
+            }
+            if (i == assoc) {
+                i = assoc-1;
+                recency_stack[i] = pc;
+            }
+            int j;
+            unsigned int b = recency_stack[i];
+            for (j = i; j >= 1; j -= 1) {
+                recency_stack[j] = recency_stack[j-1];
+            }
+            recency_stack[0] = b;
+        }
+
+        bool last_ghist_bit;
+        int occupancy;
+
+        std::vector<int> mpreds;
+        std::vector<std::vector<short int>> tables;
+        std::vector<std::vector<std::array<bool, 2>>> sign_bits;
+    };
+    std::vector<ThreadData *> threadData;
+
+    /** Predictor tables */
+    std::vector<HistorySpec *> specs;
+    std::vector<int> table_sizes;
+
+    /** runtime values and data used to count the size in bits */
+    bool doing_local;
+    bool doing_recency;
+    int assoc;
+    int ghist_length;
+    int modghist_length;
+    int path_length;
+    unsigned int randSeed;
+    int thresholdCounter;
+    int theta;
+    std::vector<int> imli_counter_bits;
+    std::vector<int> modhist_indices;
+    std::vector<int> modhist_lengths;
+    std::vector<int> modpath_indices;
+    std::vector<int> modpath_lengths;
+    std::vector<std::vector<int>> blurrypath_bits;
+    std::vector<std::vector<std::vector<bool>>> acyclic_bits;
+
+    /** Auxiliary function for MODHIST and GHISTMODPATH features */
+    void insertModhistSpec(int p1, int p2) {
+        int j = insert(modhist_indices, p1);
+        if (modhist_lengths.size() < (j + 1)) {
+            modhist_lengths.resize(j + 1);
+        }
+        if (modhist_lengths[j] < p2 + 1) {
+            modhist_lengths[j] = p2 + 1;
+        }
+        if (p2 >= modghist_length) {
+            modghist_length = p2 + 1;
+        }
+    }
+
+    /** Auxiliary function for MODPATH and GHISTMODPATH features */
+    void insertModpathSpec(int p1, int p2) {
+        int j = insert(modpath_indices, p1);
+        if (modpath_lengths.size() < (j + 1)) {
+            modpath_lengths.resize(j + 1);
+        }
+        if (modpath_lengths[j] < p2 + 1) {
+            modpath_lengths[j] = p2 + 1;
+        }
+        if (p2 >= path_length) {
+            path_length = p2 + 1;
+        }
+    }
+
+    /** Auxiliary function used by insertModhistSpec and insertModpathSpec*/
+    int insert(std::vector<int> &v, int x)
+    {
+        for (int i = 0; i < v.size(); i += 1) {
+            if (v[i] == x) {
+                return i;
+            }
+        }
+        v.push_back(x);
+        return v.size()-1;
+    }
+
+    /**
+     * Computes the size in bits of the structures needed to keep track
+     * of the history and the predictor tables and assigns the sizes of
+     * those tables that did not had their size specified.
+     * @param num_filter_entries number of entries of the filter
+     * @param nlocal_histories number of local history entries
+     * @param local_history_length size of each local history entry
+     */
+    void computeBits(int num_filter_entries, int nlocal_histories,
+            int local_history_length);
+
+    /**
+     * Creates the tables of the predictor
+     */
+    virtual void createSpecs() = 0;
+
+    /**
+     * Get the position index of a predictor table
+     * @param tid Thread ID of the branch
+     * @param bi branch informaiton data
+     * @param spec predictor table
+     * @param index integer index of the predictor table
+     * @result index to access the predictor table
+     */
+    unsigned int getIndex(ThreadID tid, const MPPBranchInfo &bi,
+            const HistorySpec &spec, int index) const;
+    /**
+     * Finds the best subset of features to use in case of a low-confidence
+     * branch, returns the result as an ordered vector of the indices to the
+     * predictor tables
+     * @param tid Thread ID of the branch
+     * @param vector to write the ordered list of indices of the best tables
+     */
+    void findBest(ThreadID tid, std::vector<int> &best_preds) const;
+
+    /**
+     * Computes the output of the predictor for a given branch and the
+     * resulting best value in case the prediction has low confidence
+     * @param tid Thread ID of the branch
+     * @param bi branch informaiton data
+     * @return resulting sum for low-confidence branch
+     */
+    int computeOutput(ThreadID tid, MPPBranchInfo &bi);
+
+    /**
+     * Trains the branch predictor with the given branch and direction
+     * @param tid Thread ID of the branch
+     * @param bi branch informaiton data
+     * @param taken whether the branch was taken
+     */
+    void train(ThreadID tid, MPPBranchInfo &bi, bool taken);
+
+    /**
+     * Auxiliary function to increase a table counter depending on the
+     * direction of the branch
+     * @param taken whether the branch was taken
+     * @param sign current sign of the table
+     * @param c current value of the table
+     * @param max_weight maximum value of the counter
+     */
+    void satIncDec(bool taken, bool &sign, int &c, int max_weight) const;
+
+    /** Add a table spec to the prefetcher */
+    void addSpec(HistorySpec *spec)
+    {
+        specs.push_back(spec);
+    }
+
+    /** Available features */
+
+    class GHIST : public HistorySpec {
+      public:
+        GHIST(int p1, int p2, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, 0, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            return hash(mpp.threadData[tid]->ghist_words, mpp.blockSize, p1,
+                        p2);
+        }
+
+        static unsigned int hash(const std::vector<unsigned int> &ghist_words,
+                int block_size, int start_pos, int end_pos)
+        {
+            int a = start_pos;
+            int b = end_pos;
+
+            unsigned int x = 0;
+            // am is the next multiple of block_size after a
+            int am = (((a/block_size)*block_size)+block_size);
+            // bm is the previous multiple of block_size before b
+            int bm = (b/block_size)*block_size;
+
+            // the 0th bit of ghist_words[a/block_size] is the most recent bit.
+            // so the number of bits between a and am is the number to shift
+            // right?
+
+            // start out x as remainder bits from the beginning:
+            // x = [ . . . . . b b b b b ]
+            x += ghist_words[a / block_size] >> (a-am);
+            // add in bits from the middle
+            for (int i=am; i<bm; i+=block_size) {
+                x += ghist_words[i / block_size];
+            }
+            // add in remainder bits from end:
+            // x += [ b b b b b . . . . . ]
+            unsigned int y = ghist_words[bm / block_size] & ((1<<(b - bm))-1);
+            x += y << (block_size - (b - bm));
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            if (mpp.ghist_length <= p2) {
+                mpp.ghist_length = p2 + 1;
+            }
+        }
+    };
+
+    class ACYCLIC : public HistorySpec {
+      public:
+        ACYCLIC(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int a = p1;
+            int shift = p2;
+            int style = p3;
+            std::vector<std::vector<bool>> &acyclic_histories =
+                mpp.threadData[tid]->acyclic_histories;
+            std::vector<std::vector<unsigned int>> &acyclic2_histories =
+                mpp.threadData[tid]->acyclic2_histories;
+
+            unsigned int x = 0;
+            if (style == -1) {
+                unsigned int k = 0;
+                for (int i = 0; i < a + 2; i += 1) {
+                    x ^= acyclic_histories[a][i] << k;
+                    k += 1;
+                    k %= mpp.blockSize;
+                }
+            } else {
+                for (int i = 0; i < a + 2; i += 1) {
+                    x <<= shift;
+                    x += acyclic2_histories[a][i];
+                }
+            }
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            if (mpp.acyclic_bits.size() < (p1 + 1)) {
+                mpp.acyclic_bits.resize(p1 + 1);
+            }
+            if (mpp.acyclic_bits[p1].size() < (p1 + 2)) {
+                mpp.acyclic_bits[p1].resize(p1 + 2, std::vector<bool>(2));
+            }
+            for (int j = 0; j < p1 + 2; j += 1) {
+                mpp.acyclic_bits[p1][j][!p3] = true;
+            }
+        }
+    };
+
+    class MODHIST : public HistorySpec {
+      public:
+        MODHIST(int p1, int p2, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, 0, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int a = p1;
+            int b = p2;
+            std::vector<std::vector<bool>> &mod_histories =
+                mpp.threadData[tid]->mod_histories;
+
+            unsigned int x = 0, k = 0;
+            for (int i = 0; i < b; i += 1) {
+                x ^= mod_histories[a][i] << k;
+                k += 1;
+                k %= mpp.blockSize;
+            }
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            mpp.insertModhistSpec(p1, p2);
+        }
+    };
+
+    class BIAS : public HistorySpec {
+      public:
+        BIAS(double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(0, 0, 0, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            return 0;
+        }
+    };
+
+
+    class RECENCY : public HistorySpec {
+      public:
+        RECENCY(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int depth = p1;
+            int shift = p2;
+            int style = p3;
+            std::vector<unsigned int short> &recency_stack =
+                mpp.threadData[tid]->recency_stack;
+
+            if (style == -1) {
+                unsigned int x = 0;
+                for (int i = 0; i < depth; i += 1) {
+                    x <<= shift;
+                    x += recency_stack[i];
+                }
+                return x;
+            } else {
+                unsigned int x = 0, k = 0;
+                for (int i = 0; i < depth; i += 1) {
+                    x ^= (!!(recency_stack[i] & (1 << shift))) << k;
+                    k += 1;
+                    k %= mpp.blockSize;
+                }
+                return x;
+            }
+        }
+        void setBitRequirements() const override
+        {
+            if (mpp.assoc < p1) {
+                mpp.assoc = p1;
+            }
+            mpp.doing_recency = true;
+        }
+    };
+
+    class IMLI : public HistorySpec {
+        public:
+            IMLI(int p1, double coeff, int size, int width,
+                    MultiperspectivePerceptron &mpp)
+                : HistorySpec(p1, 0, 0, coeff, size, width, mpp)
+            {}
+
+            unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+                override
+                {
+                    assert(p1 >= 1);
+                    assert(p1 <= 4);
+                    return mpp.threadData[tid]->imli_counter[p1-1];
+                }
+
+            void setBitRequirements() const override
+            {
+                mpp.imli_counter_bits[p1 - 1] = 32;
+            }
+    };
+
+    class PATH : public HistorySpec {
+      public:
+        PATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int depth = p1;
+            int shift = p2;
+            int style = p3;
+            std::vector<unsigned short int> &path_history =
+                mpp.threadData[tid]->path_history;
+
+            if (style == -1) {
+                unsigned int x = 0;
+                for (int i = 0; i < depth; i += 1) {
+                    x <<= shift;
+                    x += path_history[i];
+                }
+                return x;
+            } else {
+                unsigned int x = 0;
+                int bm = (depth / mpp.blockSize) * mpp.blockSize;
+                for (int i = 0; i < bm; i += mpp.blockSize) {
+                    for (int j = 0; j < mpp.blockSize; j += 1) {
+                        x ^= (!!(path_history[i + j] & (1 << shift))) << j;
+                    }
+                }
+                int k = 0;
+                for (int i = bm; i < depth; i += 1) {
+                    x ^= (!!(path_history[i] & (1 << shift))) << k++;
+                }
+                return x;
+            }
+        }
+        void setBitRequirements() const override
+        {
+            if (mpp.path_length <= p1) {
+                mpp.path_length = p1 + 1;
+            }
+        }
+    };
+
+    class LOCAL : public HistorySpec {
+      public:
+        LOCAL(int p1, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, 0, 0, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            unsigned int x = mpp.threadData[tid]->localHistories[pc];
+            if (p1 != -1) {
+                x &= ((1 << p1) - 1);
+            }
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            mpp.doing_local = true;
+        }
+    };
+
+    class MODPATH : public HistorySpec {
+      public:
+        MODPATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int a = p1;
+            int depth = p2;
+            int shift = p3;
+
+            unsigned int x = 0;
+            for (int i=0; i<depth; i += 1) {
+                x <<= shift;
+                x += mpp.threadData[tid]->modpath_histories[a][i];
+            }
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            mpp.insertModpathSpec(p1, p2);
+        }
+    };
+
+    class GHISTPATH : public HistorySpec {
+      public:
+        GHISTPATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int depth = p1;
+            int shift = p2;
+            int style = p3;
+            std::vector<unsigned int> &ghist_words =
+                mpp.threadData[tid]->ghist_words;
+            std::vector<unsigned short int> &path_history =
+                mpp.threadData[tid]->path_history;
+
+            if (style == -1) {
+                unsigned int x = 0;
+                int bm = (depth / mpp.blockSize) * mpp.blockSize;
+                unsigned int w;
+                for (int i = 0; i < bm; i += mpp.blockSize) {
+                    w = ghist_words[i / mpp.blockSize];
+                    for (int j = 0; j < mpp.blockSize; j += 1) {
+                        x <<= shift;
+                        x += (path_history[i + j] << 1) | (w & 1);
+                        w >>= 1;
+                    }
+                }
+                w = ghist_words[bm / mpp.blockSize];
+                for (int i = bm; i < depth; i += 1) {
+                    x <<= shift;
+                    x += (path_history[i] << 1) | (w & 1);
+                    w >>= 1;
+                }
+                return x;
+            } else {
+                unsigned int x = 0;
+                int bm = (depth / mpp.blockSize) * mpp.blockSize;
+                unsigned int w = 0;
+                for (int i = 0; i < bm; i += mpp.blockSize) {
+                    w = ghist_words[i / mpp.blockSize];
+                    for (int j = 0; j < mpp.blockSize; j += 1) {
+                        x ^= (!!(path_history[i + j] & (1 << shift))) << j;
+                        x ^= (w & 1) << j;
+                        w >>= 1;
+                    }
+                }
+                w = ghist_words[bm/mpp.blockSize];
+                int k = 0;
+                for (int i = bm; i < depth; i += 1) {
+                    x ^= (!!(path_history[i] & (1 << shift))) << k;
+                    x ^= (w & 1) << k;
+                    w >>= 1;
+                    k += 1;
+                }
+                return x;
+            }
+        }
+
+        void setBitRequirements() const override
+        {
+            if (mpp.ghist_length <= p1) {
+                mpp.ghist_length = p1 + 1;
+            }
+            if (mpp.path_length <= p1) {
+                mpp.path_length = p1 + 1;
+            }
+        }
+    };
+
+    class GHISTMODPATH : public HistorySpec {
+      public:
+        GHISTMODPATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int a = p1;
+            int depth = p2;
+            int shift = p3;
+            std::vector<std::vector<unsigned short int>> &modpath_histories =
+                mpp.threadData[tid]->modpath_histories;
+            std::vector<std::vector<bool>> &mod_histories =
+                mpp.threadData[tid]->mod_histories;
+
+            unsigned int x = 0;
+            for (int i = 0; i < depth; i += 1) {
+                x <<= shift;
+                x += (modpath_histories[a][i] << 1) | mod_histories[a][i];
+            }
+            return x;
+        }
+        void setBitRequirements() const override
+        {
+            mpp.insertModhistSpec(p1, p2);
+            mpp.insertModpathSpec(p1, p2);
+        }
+    };
+
+    class BLURRYPATH : public HistorySpec {
+      public:
+        BLURRYPATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int scale = p1;
+            int depth = p2;
+            int shiftdelta = p3;
+
+            if (shiftdelta == -1) shiftdelta = 0;
+            int sdint = shiftdelta >> 2;
+            int sdfrac = shiftdelta & 3;
+            unsigned int x = 0;
+            int shift = 0;
+            int count = 0;
+            for (int i = 0; i < depth; i += 1) {
+                x += mpp.threadData[tid]->blurrypath_histories[scale][i] >>
+                    shift;
+                count += 1;
+                if (count == sdfrac) {
+                    shift += sdint;
+                    count = 0;
+                }
+            }
+            return x;
+
+        }
+        void setBitRequirements() const override
+        {
+            if (mpp.blurrypath_bits.size() < (p1 + 1)) {
+                mpp.blurrypath_bits.resize(p1 + 1);
+            }
+            if (mpp.blurrypath_bits[p1].size() < p2) {
+                mpp.blurrypath_bits[p1].resize(p2);
+            }
+            for (int j = 0; j < p2; j += 1) {
+                mpp.blurrypath_bits[p1][j] = 32 - p1;
+            }
+        }
+    };
+
+    class RECENCYPOS : public HistorySpec {
+      public:
+        RECENCYPOS(int p1, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, 0, 0, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            return hash(mpp.threadData[tid]->recency_stack, mpp.table_sizes,
+                    pc2, p1, t);
+        }
+
+        static unsigned int hash(
+            const std::vector<unsigned int short> &recency_stack,
+            const std::vector<int> &table_sizes, unsigned short int pc, int l,
+            int t)
+        {
+            // search for the PC
+
+            for (int i = 0; i < l; i += 1) {
+                if (recency_stack[i] == pc) {
+                    return i * table_sizes[t] / l;
+                }
+            }
+
+            // return last index in table on a miss
+
+            return table_sizes[t] - 1;
+        }
+
+        void setBitRequirements() const override
+        {
+            if (mpp.assoc < p1) {
+                mpp.assoc = p1;
+            }
+            mpp.doing_recency = true;
+        }
+    };
+
+    class SGHISTPATH : public HistorySpec {
+      public:
+        SGHISTPATH(int p1, int p2, int p3, double coeff, int size, int width,
+                MultiperspectivePerceptron &mpp)
+            : HistorySpec(p1, p2, p3, coeff, size, width, mpp)
+        {}
+
+        unsigned int getHash(ThreadID tid, Addr pc, Addr pc2, int t) const
+            override
+        {
+            int a = p1;
+            int b = p2;
+            int shift = p3;
+            std::vector<unsigned int> &ghist_words =
+                mpp.threadData[tid]->ghist_words;
+            std::vector<unsigned short int> &path_history =
+                mpp.threadData[tid]->path_history;
+
+            unsigned int x = 0;
+            int bm = (b / mpp.blockSize) * mpp.blockSize;
+            unsigned int w;
+            for (int i = a; i < bm; i += mpp.blockSize) {
+                w = ghist_words[i / mpp.blockSize];
+                for (int j = 0; j < mpp.blockSize; j += 1) {
+                    x <<= shift;
+                    x += (path_history[i+j] << 1) | (w & 1);
+                    w >>= 1;
+                }
+            }
+            w = ghist_words[bm / mpp.blockSize];
+            for (int i = bm; i < b; i += 1) {
+                x <<= shift;
+                x += (path_history[i] << 1) | (w & 1);
+                w >>= 1;
+            }
+            return x;
+        }
+    };
+
+    public:
+    MultiperspectivePerceptron(const MultiperspectivePerceptronParams *params);
+
+    void init() override;
+
+    void uncondBranch(ThreadID tid, Addr pc, void * &bp_history) override;
+    void squash(ThreadID tid, void *bp_history) override;
+    bool lookup(ThreadID tid, Addr instPC, void * &bp_history) override;
+    void update(ThreadID tid, Addr instPC, bool taken,
+            void *bp_history, bool squashed,
+            const StaticInstPtr & inst,
+            Addr corrTarget = MaxAddr) override;
+    void btbUpdate(ThreadID tid, Addr branch_addr, void* &bp_history) override;
+};
+#endif//__CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_HH__
diff --git a/src/cpu/pred/multiperspective_perceptron_64KB.cc b/src/cpu/pred/multiperspective_perceptron_64KB.cc
new file mode 100644
index 0000000..a5d7241
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron_64KB.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+/*
+ * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+ * - 64KB version
+ */
+
+#include "cpu/pred/multiperspective_perceptron_64KB.hh"
+
+MultiperspectivePerceptron64KB::MultiperspectivePerceptron64KB(
+        const MultiperspectivePerceptron64KBParams *p)
+    : MultiperspectivePerceptron(p)
+{
+}
+
+void
+MultiperspectivePerceptron64KB::createSpecs() {
+    addSpec(new ACYCLIC(10, -1, -1, 1.0, 0, 6, *this));
+    addSpec(new BLURRYPATH(10, 7, -1, 1.0, 0, 6, *this));
+    addSpec(new GHIST(0, 19, 1.3125, 0, 6, *this));
+    addSpec(new GHIST(0, 65, 0.85, 0, 6, *this));
+    addSpec(new GHIST(111, 222, 1.0, 0, 6, *this));
+    addSpec(new GHIST(115, 206, 1.0, 0, 6, *this));
+    addSpec(new GHIST(190, 336, 1.0, 0, 6, *this));
+    addSpec(new GHIST(21, 64, 1.0, 0, 6, *this));
+    addSpec(new GHIST(48, 119, 1.0, 0, 6, *this));
+    addSpec(new GHIST(67, 203, 0.75, 0, 6, *this));
+    addSpec(new GHIST(75, 150, 1.0, 0, 6, *this));
+    addSpec(new GHISTMODPATH(1, 5, 4, 1.45, 1913, 6, *this));
+    addSpec(new GHISTMODPATH(3, 13, 1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(105, 4, 0, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(11, 2, -1, 1.25, 0, 6, *this));
+    addSpec(new GHISTPATH(15, 4, -1, 1.125, 0, 6, *this));
+    addSpec(new GHISTPATH(23, 4, -1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(31, 1, -1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(32, 2, -1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(36, 4, -1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(51, 1, 0, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(7, 1, -1, 1.5, 0, 6, *this));
+    addSpec(new GHISTPATH(72, 1, -1, 1.0, 0, 6, *this));
+    addSpec(new GHISTPATH(86, 4, 0, 1.0, 0, 6, *this));
+    addSpec(new IMLI(1, 1.8125, 0, 6, *this));
+    addSpec(new IMLI(4, 1.78125, 1536, 6, *this));
+    addSpec(new LOCAL(-1, 1.0, 1024, 6, *this));
+    addSpec(new LOCAL(-1, 2.0, 3467, 6, *this));
+    addSpec(new MODHIST(1, 16, 0.9375, 0, 6, *this));
+    addSpec(new MODPATH(3, 20, 1, 1.0, 0, 6, *this));
+    addSpec(new PATH(13, 2, -1, 1.4375, 0, 6, *this));
+    addSpec(new PATH(27, 5, -1, 1.0, 0, 6, *this));
+    addSpec(new RECENCY(10, 3, -1, 0.55, 1024, 6, *this));
+    addSpec(new RECENCY(14, 4, -1, 1.3125, 0, 6, *this));
+    addSpec(new RECENCYPOS(31, 1.5, 0, 6, *this));
+    addSpec(new SGHISTPATH(1, 2, 5, 1.25, 768, 6, *this));
+    addSpec(new SGHISTPATH(1, 5, 2, 1.3125, 972, 6, *this));
+}
+
+    MultiperspectivePerceptron64KB*
+MultiperspectivePerceptron64KBParams::create()
+{
+    return new MultiperspectivePerceptron64KB(this);
+}
diff --git a/src/cpu/pred/multiperspective_perceptron_64KB.hh b/src/cpu/pred/multiperspective_perceptron_64KB.hh
new file mode 100644
index 0000000..a87020b
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron_64KB.hh
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+/*
+ * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+ * - 64KB version
+ */
+
+#ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__
+#define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__
+
+#include "cpu/pred/multiperspective_perceptron.hh"
+#include "params/MultiperspectivePerceptron64KB.hh"
+
+class MultiperspectivePerceptron64KB : public MultiperspectivePerceptron {
+    void createSpecs() override;
+    public:
+    MultiperspectivePerceptron64KB(
+            const MultiperspectivePerceptron64KBParams *p);
+};
+
+#endif // __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_64KB_HH__
diff --git a/src/cpu/pred/multiperspective_perceptron_8KB.cc b/src/cpu/pred/multiperspective_perceptron_8KB.cc
new file mode 100644
index 0000000..832e172
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron_8KB.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+/*
+ * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+ * - 8KB version
+ */
+
+#include "cpu/pred/multiperspective_perceptron_8KB.hh"
+
+MultiperspectivePerceptron8KB::MultiperspectivePerceptron8KB(
+        const MultiperspectivePerceptron8KBParams *p)
+    : MultiperspectivePerceptron(p)
+{
+}
+
+void
+MultiperspectivePerceptron8KB::createSpecs() {
+    addSpec(new BIAS(2.40625, 0, 6, *this));
+    addSpec(new GHIST(0, 19, 1.4375, 0, 6, *this));
+    addSpec(new GHIST(0, 65, 1.0, 0, 6, *this));
+    addSpec(new GHIST(21, 64, 1.0, 0, 6, *this));
+    addSpec(new GHIST(75, 150, 1.0625, 0, 6, *this));
+    addSpec(new GHISTMODPATH(0, 7, 3, 1.625, 0, 6, *this));
+    addSpec(new GHISTPATH(11, 2, -1, 1.25, 0, 6, *this));
+    addSpec(new GHISTPATH(15, 4, -1, 1.125, 0, 6, *this));
+    addSpec(new GHISTPATH(31, 1, -1, 1.40625, 0, 6, *this));
+    addSpec(new GHISTPATH(7, 1, -1, 1.5, 600, 6, *this));
+    addSpec(new IMLI(4, 1.28125, 375, 6, *this));
+    addSpec(new LOCAL(-1, 1.5625, 512, 6, *this));
+    addSpec(new RECENCY(14, 4, -1, 1.25, 0, 6, *this));
+    addSpec(new RECENCYPOS(31, 1.875, 0, 6, *this));
+    addSpec(new SGHISTPATH(0, 4, 3, 1.65625, 0, 6, *this));
+    addSpec(new SGHISTPATH(1, 2, 5, 2.53125, 0, 5, *this));
+}
+
+    MultiperspectivePerceptron8KB*
+MultiperspectivePerceptron8KBParams::create()
+{
+    return new MultiperspectivePerceptron8KB(this);
+}
diff --git a/src/cpu/pred/multiperspective_perceptron_8KB.hh b/src/cpu/pred/multiperspective_perceptron_8KB.hh
new file mode 100644
index 0000000..032ecdf
--- /dev/null
+++ b/src/cpu/pred/multiperspective_perceptron_8KB.hh
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 Texas A&M University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author: Daniel A. Jiménez
+ *  Adapted to gem5 by: Javier Bueno Hedo
+ *
+ */
+
+/*
+ * Multiperspective Perceptron Predictor (by Daniel A. Jiménez)
+ * - 8KB version
+ */
+
+#ifndef __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__
+#define __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__
+
+#include "cpu/pred/multiperspective_perceptron.hh"
+#include "params/MultiperspectivePerceptron8KB.hh"
+
+class MultiperspectivePerceptron8KB : public MultiperspectivePerceptron {
+    void createSpecs() override;
+    public:
+    MultiperspectivePerceptron8KB(
+            const MultiperspectivePerceptron8KBParams *p);
+};
+
+#endif // __CPU_PRED_MULTIPERSPECTIVE_PERCEPTRON_8KB_HH__