configs: Added an option for choosing branch predictor type

Added the parameter "--bp-type" to set the branch predictor type
Added the parameter "--list-bp-types" to list all the available branch
predictor types

Change-Id: Ia6aae90c784aef359b6d8233c8383cd7a871aca1
Signed-off-by: Pau Cabre <pau.cabre@metempsy.com>
Reviewed-on: https://gem5-review.googlesource.com/c/14015
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
diff --git a/configs/common/BPConfig.py b/configs/common/BPConfig.py
new file mode 100644
index 0000000..5e5b92f
--- /dev/null
+++ b/configs/common/BPConfig.py
@@ -0,0 +1,87 @@
+# Copyright (c) 2018 Metempsy Technology Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# 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;
+# neither the name of the copyright holders 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
+# OWNER 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.
+#
+# Authors: Pau Cabre
+
+# This file is a copy of MemConfig.py / CpuConfig.py, but modified to
+# hanle branch predictors instead of memory controllers / CPUs
+
+from __future__ import print_function
+
+from m5 import fatal
+import m5.objects
+import inspect
+import sys
+from textwrap import TextWrapper
+
+# Dictionary of mapping names of real branch predictor models to classes.
+_bp_classes = {}
+
+
+def is_bp_class(cls):
+    """Determine if a class is a branch predictor that can be instantiated"""
+
+    # We can't use the normal inspect.isclass because the ParamFactory
+    # and ProxyFactory classes have a tendency to confuse it.
+    try:
+        return issubclass(cls, m5.objects.BranchPredictor) and \
+            not cls.abstract
+    except (TypeError, AttributeError):
+        return False
+
+def get(name):
+    """Get a BP class from a user provided class name or alias."""
+
+    try:
+        bp_class = _bp_classes[name]
+        return bp_class
+    except KeyError:
+        print("%s is not a valid BP model." % (name,))
+        sys.exit(1)
+
+def print_bp_list():
+    """Print a list of available BP classes."""
+
+    print("Available BranchPredictor classes:")
+    doc_wrapper = TextWrapper(initial_indent="\t\t", subsequent_indent="\t\t")
+    for name, cls in _bp_classes.items():
+        print("\t%s" % name)
+
+        # Try to extract the class documentation from the class help
+        # string.
+        doc = inspect.getdoc(cls)
+        if doc:
+            for line in doc_wrapper.wrap(doc):
+                print(line)
+
+def bp_names():
+    """Return a list of valid Branch Predictor names."""
+    return _bp_classes.keys()
+
+# Add all BPs in the object hierarchy.
+for name, cls in inspect.getmembers(m5.objects, is_bp_class):
+    _bp_classes[name] = cls
+
diff --git a/configs/common/Options.py b/configs/common/Options.py
index c36dc38..536da44 100644
--- a/configs/common/Options.py
+++ b/configs/common/Options.py
@@ -44,6 +44,7 @@
 from common.Benchmarks import *
 
 from common import CpuConfig
+from common import BPConfig
 from common import MemConfig
 from common import PlatformConfig
 
@@ -51,6 +52,10 @@
     CpuConfig.print_cpu_list()
     sys.exit(0)
 
+def _listBPTypes(option, opt, value, parser):
+    BPConfig.print_bp_list()
+    sys.exit(0)
+
 def _listMemTypes(option, opt, value, parser):
     MemConfig.print_mem_list()
     sys.exit(0)
@@ -146,6 +151,15 @@
     parser.add_option("--cpu-type", type="choice", default="AtomicSimpleCPU",
                       choices=CpuConfig.cpu_names(),
                       help = "type of cpu to run with")
+    parser.add_option("--list-bp-types",
+                      action="callback", callback=_listBPTypes,
+                      help="List available branch predictor types")
+    parser.add_option("--bp-type", type="choice", default=None,
+                      choices=BPConfig.bp_names(),
+                      help = """
+                      type of branch predictor to run with
+                      (if not set, use the default branch predictor of
+                      the selected CPU)""")
     parser.add_option("--checker", action="store_true");
     parser.add_option("--cpu-clock", action="store", type="string",
                       default='2GHz',
diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py
index 23a7783..19bd962 100644
--- a/configs/common/Simulation.py
+++ b/configs/common/Simulation.py
@@ -46,6 +46,7 @@
 from os.path import join as joinpath
 
 from common import CpuConfig
+from common import BPConfig
 from common import MemConfig
 
 import m5
@@ -478,6 +479,9 @@
             # Add checker cpu if selected
             if options.checker:
                 switch_cpus[i].addCheckerCpu()
+            if options.bp_type:
+                bpClass = BPConfig.get(options.bp_type)
+                switch_cpus[i].branchPred = bpClass()
 
         # If elastic tracing is enabled attach the elastic trace probe
         # to the switch CPUs
diff --git a/configs/common/cores/arm/HPI.py b/configs/common/cores/arm/HPI.py
index a6f77af..2efb7df 100644
--- a/configs/common/cores/arm/HPI.py
+++ b/configs/common/cores/arm/HPI.py
@@ -1443,7 +1443,7 @@
 
     enableIdling = True
 
-    branchPred = HPI_BP(numThreads = Parent.numThreads)
+    branchPred = HPI_BP()
 
     itb = HPI_ITB()
     dtb = HPI_DTB()
diff --git a/configs/example/fs.py b/configs/example/fs.py
index 3997ed7..3fdf151 100644
--- a/configs/example/fs.py
+++ b/configs/example/fs.py
@@ -63,6 +63,7 @@
 from common import CacheConfig
 from common import MemConfig
 from common import CpuConfig
+from common import BPConfig
 from common.Caches import *
 from common import Options
 
@@ -199,6 +200,9 @@
                 test_sys.cpu[i].addSimPointProbe(options.simpoint_interval)
             if options.checker:
                 test_sys.cpu[i].addCheckerCpu()
+            if options.bp_type:
+                bpClass = BPConfig.get(options.bp_type)
+                test_sys.cpu[i].branchPred = bpClass()
             test_sys.cpu[i].createThreads()
 
         # If elastic tracing is enabled when not restoring from checkpoint and
diff --git a/configs/example/se.py b/configs/example/se.py
index f12d4a9..8403066 100644
--- a/configs/example/se.py
+++ b/configs/example/se.py
@@ -61,6 +61,7 @@
 from common import Simulation
 from common import CacheConfig
 from common import CpuConfig
+from common import BPConfig
 from common import MemConfig
 from common.Caches import *
 from common.cpu2000 import *
@@ -233,6 +234,10 @@
     if options.checker:
         system.cpu[i].addCheckerCpu()
 
+    if options.bp_type:
+        bpClass = BPConfig.get(options.bp_type)
+        system.cpu[i].branchPred = bpClass()
+
     system.cpu[i].createThreads()
 
 if options.ruby:
diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py
index 9f35165..1eeecde 100644
--- a/src/cpu/pred/BranchPredictor.py
+++ b/src/cpu/pred/BranchPredictor.py
@@ -29,6 +29,7 @@
 
 from m5.SimObject import SimObject
 from m5.params import *
+from m5.proxy import *
 
 class BranchPredictor(SimObject):
     type = 'BranchPredictor'
@@ -36,7 +37,7 @@
     cxx_header = "cpu/pred/bpred_unit.hh"
     abstract = True
 
-    numThreads = Param.Unsigned(1, "Number of threads")
+    numThreads = Param.Unsigned(Parent.numThreads, "Number of threads")
     BTBEntries = Param.Unsigned(4096, "Number of BTB entries")
     BTBTagSize = Param.Unsigned(16, "Size of the BTB tags, in bits")
     RASSize = Param.Unsigned(16, "RAS size")