summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build_opts/ALPHA2
-rw-r--r--build_opts/ARM2
-rw-r--r--configs/common/CpuConfig.py1
-rw-r--r--src/base/trace.hh14
-rw-r--r--src/cpu/SConscript2
-rw-r--r--src/cpu/TimingExpr.py176
-rw-r--r--src/cpu/minor/MinorCPU.py274
-rw-r--r--src/cpu/minor/SConscript73
-rw-r--r--src/cpu/minor/SConsopts45
-rw-r--r--src/cpu/minor/activity.cc66
-rw-r--r--src/cpu/minor/activity.hh72
-rw-r--r--src/cpu/minor/buffers.hh653
-rw-r--r--src/cpu/minor/cpu.cc362
-rw-r--r--src/cpu/minor/cpu.hh197
-rw-r--r--src/cpu/minor/decode.cc295
-rw-r--r--src/cpu/minor/decode.hh138
-rw-r--r--src/cpu/minor/dyn_inst.cc227
-rw-r--r--src/cpu/minor/dyn_inst.hh281
-rw-r--r--src/cpu/minor/exec_context.hh350
-rw-r--r--src/cpu/minor/execute.cc1736
-rw-r--r--src/cpu/minor/execute.hh321
-rw-r--r--src/cpu/minor/fetch1.cc676
-rw-r--r--src/cpu/minor/fetch1.hh381
-rw-r--r--src/cpu/minor/fetch2.cc543
-rw-r--r--src/cpu/minor/fetch2.hh184
-rw-r--r--src/cpu/minor/func_unit.cc242
-rw-r--r--src/cpu/minor/func_unit.hh268
-rw-r--r--src/cpu/minor/lsq.cc1614
-rw-r--r--src/cpu/minor/lsq.hh722
-rw-r--r--src/cpu/minor/pipe_data.cc294
-rw-r--r--src/cpu/minor/pipe_data.hh288
-rw-r--r--src/cpu/minor/pipeline.cc250
-rw-r--r--src/cpu/minor/pipeline.hh145
-rw-r--r--src/cpu/minor/scoreboard.cc323
-rw-r--r--src/cpu/minor/scoreboard.hh145
-rw-r--r--src/cpu/minor/stats.cc87
-rw-r--r--src/cpu/minor/stats.hh88
-rw-r--r--src/cpu/minor/trace.hh75
-rw-r--r--src/cpu/pred/SConscript3
-rw-r--r--src/cpu/static_inst.hh5
-rw-r--r--src/cpu/timing_expr.cc248
-rw-r--r--src/cpu/timing_expr.hh216
-rw-r--r--src/doc/inside-minor.doxygen1091
-rw-r--r--src/sim/SConscript2
-rw-r--r--src/sim/TickedObject.py43
-rw-r--r--src/sim/ticked_object.cc116
-rw-r--r--src/sim/ticked_object.hh191
-rwxr-xr-xutil/minorview.py107
-rw-r--r--util/minorview/__init__.py36
-rw-r--r--util/minorview/blobs.py461
-rw-r--r--util/minorview/colours.py68
-rw-r--r--util/minorview/minor.pic154
-rw-r--r--util/minorview/model.py1109
-rw-r--r--util/minorview/parse.py109
-rw-r--r--util/minorview/point.py78
-rw-r--r--util/minorview/view.py524
56 files changed, 16170 insertions, 3 deletions
diff --git a/build_opts/ALPHA b/build_opts/ALPHA
index 384394091..4c327fa00 100644
--- a/build_opts/ALPHA
+++ b/build_opts/ALPHA
@@ -1,4 +1,4 @@
TARGET_ISA = 'alpha'
SS_COMPATIBLE_FP = 1
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU,InOrderCPU'
+CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU,InOrderCPU,MinorCPU'
PROTOCOL = 'MI_example'
diff --git a/build_opts/ARM b/build_opts/ARM
index 9c27c0d27..b175fd0ad 100644
--- a/build_opts/ARM
+++ b/build_opts/ARM
@@ -1,3 +1,3 @@
TARGET_ISA = 'arm'
-CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU'
+CPU_MODELS = 'AtomicSimpleCPU,TimingSimpleCPU,O3CPU,MinorCPU'
PROTOCOL = 'MI_example'
diff --git a/configs/common/CpuConfig.py b/configs/common/CpuConfig.py
index e12b646d8..c2663ee98 100644
--- a/configs/common/CpuConfig.py
+++ b/configs/common/CpuConfig.py
@@ -51,6 +51,7 @@ _cpu_aliases_all = [
("timing", "TimingSimpleCPU"),
("atomic", "AtomicSimpleCPU"),
("inorder", "InOrderCPU"),
+ ("minor", "MinorCPU"),
("detailed", "DerivO3CPU"),
("kvm", ("ArmKvmCPU", "X86KvmCPU")),
]
diff --git a/src/base/trace.hh b/src/base/trace.hh
index dbeffdc8b..eb0ab9dae 100644
--- a/src/base/trace.hh
+++ b/src/base/trace.hh
@@ -72,6 +72,20 @@ struct StringWrap
inline const std::string &name() { return Trace::DefaultName; }
+// Interface for things with names. (cf. SimObject but without other
+// functionality). This is useful when using DPRINTF
+class Named
+{
+ protected:
+ const std::string _name;
+
+ public:
+ Named(const std::string &name_) : _name(name_) { }
+
+ public:
+ const std::string &name() const { return _name; }
+};
+
//
// DPRINTF is a debugging trace facility that allows one to
// selectively enable tracing statements. To use DPRINTF, there must
diff --git a/src/cpu/SConscript b/src/cpu/SConscript
index ca9c6a791..1ea92114a 100644
--- a/src/cpu/SConscript
+++ b/src/cpu/SConscript
@@ -106,6 +106,7 @@ SimObject('ExeTracer.py')
SimObject('IntelTrace.py')
SimObject('IntrControl.py')
SimObject('NativeTrace.py')
+SimObject('TimingExpr.py')
Source('activity.cc')
Source('base.cc')
@@ -123,6 +124,7 @@ Source('static_inst.cc')
Source('simple_thread.cc')
Source('thread_context.cc')
Source('thread_state.cc')
+Source('timing_expr.cc')
if env['TARGET_ISA'] == 'sparc':
SimObject('LegionTrace.py')
diff --git a/src/cpu/TimingExpr.py b/src/cpu/TimingExpr.py
new file mode 100644
index 000000000..6a9d6f95c
--- /dev/null
+++ b/src/cpu/TimingExpr.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2013-2014 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+from m5.params import *
+from m5.SimObject import SimObject
+
+# These classes define an expression language over uint64_t with only
+# a few operators. This can be used to form expressions for the extra
+# delay required in variable execution time instructions.
+#
+# Expressions, in evaluation, will have access to the ThreadContext and
+# a StaticInst
+
+class TimingExpr(SimObject):
+ type = 'TimingExpr'
+ cxx_header = 'cpu/timing_expr.hh'
+ abstract = True;
+
+class TimingExprLiteral(TimingExpr):
+ """Literal 64 bit unsigned value"""
+ type = 'TimingExprLiteral'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ value = Param.UInt64("literal value")
+
+ def set_params(self, value):
+ self.value = value
+ return self
+
+class TimingExpr0(TimingExprLiteral):
+ """Convenient 0"""
+ value = 0
+
+class TimingExprSrcReg(TimingExpr):
+ """Find the source register number from the current inst"""
+ type = 'TimingExprSrcReg'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ # index = Param.Unsigned("index into inst src regs")
+ index = Param.Unsigned("index into inst src regs")
+
+ def set_params(self, index):
+ self.index = index
+ return self
+
+class TimingExprReadIntReg(TimingExpr):
+ """Read an architectural register"""
+ type = 'TimingExprReadIntReg'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ reg = Param.TimingExpr("register raw index to read")
+
+ def set_params(self, reg):
+ self.reg = reg
+ return self
+
+class TimingExprLet(TimingExpr):
+ """Block of declarations"""
+ type = 'TimingExprLet'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ defns = VectorParam.TimingExpr("expressions for bindings")
+ expr = Param.TimingExpr("body expression")
+
+ def set_params(self, defns, expr):
+ self.defns = defns
+ self.expr = expr
+ return self
+
+class TimingExprRef(TimingExpr):
+ """Value of a bound sub-expression"""
+ type = 'TimingExprRef'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ index = Param.Unsigned("expression index")
+
+ def set_params(self, index):
+ self.index = index
+ return self
+
+class TimingExprOp(Enum):
+ vals = [
+ 'timingExprAdd', 'timingExprSub',
+ 'timingExprUMul', 'timingExprUDiv',
+ 'timingExprSMul', 'timingExprSDiv',
+ 'timingExprUCeilDiv', # Unsigned divide rounding up
+ 'timingExprEqual', 'timingExprNotEqual',
+ 'timingExprULessThan',
+ 'timingExprUGreaterThan',
+ 'timingExprSLessThan',
+ 'timingExprSGreaterThan',
+ 'timingExprInvert',
+ 'timingExprNot',
+ 'timingExprAnd',
+ 'timingExprOr',
+ 'timingExprSizeInBits',
+ 'timingExprSignExtend32To64',
+ 'timingExprAbs'
+ ]
+
+class TimingExprUn(TimingExpr):
+ """Unary operator"""
+ type = 'TimingExprUn'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ op = Param.TimingExprOp("operator")
+ arg = Param.TimingExpr("expression")
+
+ def set_params(self, op, arg):
+ self.op = op
+ self.arg = arg
+ return self
+
+class TimingExprBin(TimingExpr):
+ """Binary operator"""
+ type = 'TimingExprBin'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ op = Param.TimingExprOp("operator")
+ left = Param.TimingExpr("LHS expression")
+ right = Param.TimingExpr("RHS expression")
+
+ def set_params(self, op, left, right):
+ self.op = op
+ self.left = left
+ self.right = right
+ return self
+
+class TimingExprIf(TimingExpr):
+ """If-then-else operator"""
+ type = 'TimingExprIf'
+ cxx_header = 'cpu/timing_expr.hh'
+
+ cond = Param.TimingExpr("condition expression")
+ trueExpr = Param.TimingExpr("true expression")
+ falseExpr = Param.TimingExpr("false expression")
+
+ def set_params(self, cond, trueExpr, falseExpr):
+ self.cond = cond
+ self.trueExpr = trueExpr
+ self.falseExpr = falseExpr
+ return self
diff --git a/src/cpu/minor/MinorCPU.py b/src/cpu/minor/MinorCPU.py
new file mode 100644
index 000000000..07953cf5a
--- /dev/null
+++ b/src/cpu/minor/MinorCPU.py
@@ -0,0 +1,274 @@
+# Copyright (c) 2012-2014 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2007 The Regents of The University of Michigan
+# 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: Gabe Black
+# Nathan Binkert
+# Andrew Bardsley
+
+from m5.defines import buildEnv
+from m5.params import *
+from m5.proxy import *
+from m5.SimObject import SimObject
+from BaseCPU import BaseCPU
+from DummyChecker import DummyChecker
+from BranchPredictor import BranchPredictor
+from TimingExpr import TimingExpr
+
+from FuncUnit import OpClass
+
+class MinorOpClass(SimObject):
+ """Boxing of OpClass to get around build problems and provide a hook for
+ future additions to OpClass checks"""
+
+ type = 'MinorOpClass'
+ cxx_header = "cpu/minor/func_unit.hh"
+
+ opClass = Param.OpClass("op class to match")
+
+class MinorOpClassSet(SimObject):
+ """A set of matchable op classes"""
+
+ type = 'MinorOpClassSet'
+ cxx_header = "cpu/minor/func_unit.hh"
+
+ opClasses = VectorParam.MinorOpClass([], "op classes to be matched."
+ " An empty list means any class")
+
+class MinorFUTiming(SimObject):
+ type = 'MinorFUTiming'
+ cxx_header = "cpu/minor/func_unit.hh"
+
+ mask = Param.UInt64(0, "mask for testing ExtMachInst")
+ match = Param.UInt64(0, "match value for testing ExtMachInst:"
+ " (ext_mach_inst & mask) == match")
+ suppress = Param.Bool(False, "if true, this inst. is not executed by"
+ " this FU")
+ extraCommitLat = Param.Cycles(0, "extra cycles to stall commit for"
+ " this inst.")
+ extraCommitLatExpr = Param.TimingExpr(NULL, "extra cycles as a"
+ " run-time evaluated expression")
+ extraAssumedLat = Param.Cycles(0, "extra cycles to add to scoreboard"
+ " retire time for this insts dest registers once it leaves the"
+ " functional unit. For mem refs, if this is 0, the result's time"
+ " is marked as unpredictable and no forwarding can take place.")
+ srcRegsRelativeLats = VectorParam.Cycles("the maximum number of cycles"
+ " after inst. issue that each src reg can be available for this"
+ " inst. to issue")
+ opClasses = Param.MinorOpClassSet(MinorOpClassSet(),
+ "op classes to be considered for this decode. An empty set means any"
+ " class")
+ description = Param.String('', "description string of the decoding/inst."
+ " class")
+
+def minorMakeOpClassSet(op_classes):
+ """Make a MinorOpClassSet from a list of OpClass enum value strings"""
+ def boxOpClass(op_class):
+ return MinorOpClass(opClass=op_class)
+
+ return MinorOpClassSet(opClasses=map(boxOpClass, op_classes))
+
+class MinorFU(SimObject):
+ type = 'MinorFU'
+ cxx_header = "cpu/minor/func_unit.hh"
+
+ opClasses = Param.MinorOpClassSet(MinorOpClassSet(), "type of operations"
+ " allowed on this functional unit")
+ opLat = Param.Cycles(1, "latency in cycles")
+ issueLat = Param.Cycles(1, "cycles until another instruction can be"
+ " issued")
+ timings = VectorParam.MinorFUTiming([], "extra decoding rules")
+
+ cantForwardFromFUIndices = VectorParam.Unsigned([],
+ "list of FU indices from which this FU can't receive and early"
+ " (forwarded) result")
+
+class MinorFUPool(SimObject):
+ type = 'MinorFUPool'
+ cxx_header = "cpu/minor/func_unit.hh"
+
+ funcUnits = VectorParam.MinorFU("functional units")
+
+class MinorDefaultIntFU(MinorFU):
+ opClasses = minorMakeOpClassSet(['IntAlu'])
+ timings = [MinorFUTiming(description="Int",
+ srcRegsRelativeLats=[2])]
+ opLat = 3
+
+class MinorDefaultIntMulFU(MinorFU):
+ opClasses = minorMakeOpClassSet(['IntMult'])
+ timings = [MinorFUTiming(description='Mul',
+ srcRegsRelativeLats=[0])]
+ opLat = 3
+
+class MinorDefaultIntDivFU(MinorFU):
+ opClasses = minorMakeOpClassSet(['IntDiv'])
+ issueLat = 9
+ opLat = 9
+
+class MinorDefaultFloatSimdFU(MinorFU):
+ opClasses = minorMakeOpClassSet([
+ 'FloatAdd', 'FloatCmp', 'FloatCvt', 'FloatMult', 'FloatDiv',
+ 'FloatSqrt',
+ 'SimdAdd', 'SimdAddAcc', 'SimdAlu', 'SimdCmp', 'SimdCvt',
+ 'SimdMisc', 'SimdMult', 'SimdMultAcc', 'SimdShift', 'SimdShiftAcc',
+ 'SimdSqrt', 'SimdFloatAdd', 'SimdFloatAlu', 'SimdFloatCmp',
+ 'SimdFloatCvt', 'SimdFloatDiv', 'SimdFloatMisc', 'SimdFloatMult',
+ 'SimdFloatMultAcc', 'SimdFloatSqrt'])
+ timings = [MinorFUTiming(description='FloatSimd',
+ srcRegsRelativeLats=[2])]
+ opLat = 6
+
+class MinorDefaultMemFU(MinorFU):
+ opClasses = minorMakeOpClassSet(['MemRead', 'MemWrite'])
+ timings = [MinorFUTiming(description='Mem',
+ srcRegsRelativeLats=[1], extraAssumedLat=2)]
+ opLat = 1
+
+class MinorDefaultMiscFU(MinorFU):
+ opClasses = minorMakeOpClassSet(['IprAccess', 'InstPrefetch'])
+ opLat = 1
+
+class MinorDefaultFUPool(MinorFUPool):
+ funcUnits = [MinorDefaultIntFU(), MinorDefaultIntFU(),
+ MinorDefaultIntMulFU(), MinorDefaultIntDivFU(),
+ MinorDefaultFloatSimdFU(), MinorDefaultMemFU(),
+ MinorDefaultMiscFU()]
+
+class MinorCPU(BaseCPU):
+ type = 'MinorCPU'
+ cxx_header = "cpu/minor/cpu.hh"
+
+ @classmethod
+ def memory_mode(cls):
+ return 'timing'
+
+ @classmethod
+ def require_caches(cls):
+ return True
+
+ @classmethod
+ def support_take_over(cls):
+ return True
+
+ fetch1FetchLimit = Param.Unsigned(1,
+ "Number of line fetches allowable in flight at once")
+ fetch1LineSnapWidth = Param.Unsigned(0,
+ "Fetch1 'line' fetch snap size in bytes"
+ " (0 means use system cache line size)")
+ fetch1LineWidth = Param.Unsigned(0,
+ "Fetch1 maximum fetch size in bytes (0 means use system cache"
+ " line size)")
+ fetch1ToFetch2ForwardDelay = Param.Cycles(1,
+ "Forward cycle delay from Fetch1 to Fetch2 (1 means next cycle)")
+ fetch1ToFetch2BackwardDelay = Param.Cycles(1,
+ "Backward cycle delay from Fetch2 to Fetch1 for branch prediction"
+ " signalling (0 means in the same cycle, 1 mean the next cycle)")
+
+ fetch2InputBufferSize = Param.Unsigned(2,
+ "Size of input buffer to Fetch2 in cycles-worth of insts.")
+ fetch2ToDecodeForwardDelay = Param.Cycles(1,
+ "Forward cycle delay from Fetch2 to Decode (1 means next cycle)")
+ fetch2CycleInput = Param.Bool(True,
+ "Allow Fetch2 to cross input lines to generate full output each"
+ " cycle")
+
+ decodeInputBufferSize = Param.Unsigned(3,
+ "Size of input buffer to Decode in cycles-worth of insts.")
+ decodeToExecuteForwardDelay = Param.Cycles(1,
+ "Forward cycle delay from Decode to Execute (1 means next cycle)")
+ decodeInputWidth = Param.Unsigned(2,
+ "Width (in instructions) of input to Decode (and implicitly"
+ " Decode's own width)")
+ decodeCycleInput = Param.Bool(True,
+ "Allow Decode to pack instructions from more than one input cycle"
+ " to fill its output each cycle")
+
+ executeInputWidth = Param.Unsigned(2,
+ "Width (in instructions) of input to Execute")
+ executeCycleInput = Param.Bool(True,
+ "Allow Execute to use instructions from more than one input cycle"
+ " each cycle")
+ executeIssueLimit = Param.Unsigned(2,
+ "Number of issuable instructions in Execute each cycle")
+ executeMemoryIssueLimit = Param.Unsigned(1,
+ "Number of issuable memory instructions in Execute each cycle")
+ executeCommitLimit = Param.Unsigned(2,
+ "Number of committable instructions in Execute each cycle")
+ executeMemoryCommitLimit = Param.Unsigned(1,
+ "Number of committable memory references in Execute each cycle")
+ executeInputBufferSize = Param.Unsigned(7,
+ "Size of input buffer to Execute in cycles-worth of insts.")
+ executeMemoryWidth = Param.Unsigned(0,
+ "Width (and snap) in bytes of the data memory interface. (0 mean use"
+ " the system cacheLineSize)")
+ executeMaxAccessesInMemory = Param.Unsigned(2,
+ "Maximum number of concurrent accesses allowed to the memory system"
+ " from the dcache port")
+ executeLSQMaxStoreBufferStoresPerCycle = Param.Unsigned(2,
+ "Maximum number of stores that the store buffer can issue per cycle")
+ executeLSQRequestsQueueSize = Param.Unsigned(1,
+ "Size of LSQ requests queue (address translation queue)")
+ executeLSQTransfersQueueSize = Param.Unsigned(2,
+ "Size of LSQ transfers queue (memory transaction queue)")
+ executeLSQStoreBufferSize = Param.Unsigned(5,
+ "Size of LSQ store buffer")
+ executeBranchDelay = Param.Cycles(1,
+ "Delay from Execute deciding to branch and Fetch1 reacting"
+ " (1 means next cycle)")
+
+ executeFuncUnits = Param.MinorFUPool(MinorDefaultFUPool(),
+ "FUlines for this processor")
+
+ executeSetTraceTimeOnCommit = Param.Bool(True,
+ "Set inst. trace times to be commit times")
+ executeSetTraceTimeOnIssue = Param.Bool(False,
+ "Set inst. trace times to be issue times")
+
+ executeAllowEarlyMemoryIssue = Param.Bool(True,
+ "Allow mem refs to be issued to the LSQ before reaching the head of"
+ " the in flight insts queue")
+
+ enableIdling = Param.Bool(True,
+ "Enable cycle skipping when the processor is idle\n");
+
+ branchPred = Param.BranchPredictor(BranchPredictor(
+ numThreads = Parent.numThreads), "Branch Predictor")
+
+ def addCheckerCpu(self):
+ print "Checker not yet supported by MinorCPU"
+ exit(1)
diff --git a/src/cpu/minor/SConscript b/src/cpu/minor/SConscript
new file mode 100644
index 000000000..2234f9a8d
--- /dev/null
+++ b/src/cpu/minor/SConscript
@@ -0,0 +1,73 @@
+# -*- mode:python -*-
+
+# Copyright (c) 2013-2014 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2006 The Regents of The University of Michigan
+# 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: Nathan Binkert
+# Andrew Bardsley
+
+Import('*')
+
+if 'MinorCPU' in env['CPU_MODELS']:
+ SimObject('MinorCPU.py')
+
+ Source('activity.cc')
+ Source('cpu.cc')
+ Source('decode.cc')
+ Source('dyn_inst.cc')
+ Source('execute.cc')
+ Source('fetch1.cc')
+ Source('fetch2.cc')
+ Source('func_unit.cc')
+ Source('lsq.cc')
+ Source('pipe_data.cc')
+ Source('pipeline.cc')
+ Source('scoreboard.cc')
+ Source('stats.cc')
+
+ DebugFlag('MinorCPU', 'Minor CPU-level events')
+ DebugFlag('MinorExecute', 'Minor Execute stage')
+ DebugFlag('MinorInterrupt', 'Minor interrupt handling')
+ DebugFlag('MinorMem', 'Minor memory accesses')
+ DebugFlag('MinorScoreboard', 'Minor Execute register scoreboard')
+ DebugFlag('MinorTrace', 'MinorTrace cycle-by-cycle state trace')
+ DebugFlag('MinorTiming', 'Extra timing for instructions')
+
+ CompoundFlag('Minor', [
+ 'MinorCPU', 'MinorExecute', 'MinorInterrupt', 'MinorMem',
+ 'MinorScoreboard'])
diff --git a/src/cpu/minor/SConsopts b/src/cpu/minor/SConsopts
new file mode 100644
index 000000000..68c420779
--- /dev/null
+++ b/src/cpu/minor/SConsopts
@@ -0,0 +1,45 @@
+# -*- mode:python -*-
+
+# Copyright (c) 2012-2014 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+Import('*')
+
+CpuModel('MinorCPU', 'minor_cpu_exec.cc',
+ '#include "cpu/minor/exec_context.hh"',
+ { 'CPU_exec_context': 'Minor::ExecContext' },
+ default=True)
diff --git a/src/cpu/minor/activity.cc b/src/cpu/minor/activity.cc
new file mode 100644
index 000000000..8e322d3e7
--- /dev/null
+++ b/src/cpu/minor/activity.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <sstream>
+
+#include "cpu/minor/activity.hh"
+#include "cpu/minor/trace.hh"
+
+namespace Minor
+{
+
+void
+MinorActivityRecorder::minorTrace() const
+{
+ std::ostringstream stages;
+ unsigned int num_stages = getNumStages();
+
+ unsigned int stage_index = 0;
+ while (stage_index < num_stages) {
+ stages << (getStageActive(stage_index) ? '1' : 'E');
+
+ stage_index++;
+ if (stage_index != num_stages)
+ stages << ',';
+ }
+
+ MINORTRACE("activity=%d stages=%s\n", getActivityCount(), stages.str());
+}
+
+}
diff --git a/src/cpu/minor/activity.hh b/src/cpu/minor/activity.hh
new file mode 100644
index 000000000..e38c476c0
--- /dev/null
+++ b/src/cpu/minor/activity.hh
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * ActivityRecoder from cpu/activity.h wrapped to provide evaluate and
+ * minorTrace.
+ */
+
+#ifndef __CPU_MINOR_ACTIVITY_HH__
+#define __CPU_MINOR_ACTIVITY_HH__
+
+#include "cpu/activity.hh"
+
+namespace Minor
+{
+
+/** ActivityRecorder with a Ticked interface */
+class MinorActivityRecorder : public ActivityRecorder
+{
+ public:
+ /** Ticked interface */
+ void evaluate() { advance(); }
+ void minorTrace() const;
+
+ public:
+ MinorActivityRecorder(const std::string &name, int num_stages,
+ int longest_latency) :
+ ActivityRecorder(name, num_stages, longest_latency, 0)
+ { }
+};
+
+}
+
+#endif /* __CPU_MINOR_ACTIVITY_HH__ */
diff --git a/src/cpu/minor/buffers.hh b/src/cpu/minor/buffers.hh
new file mode 100644
index 000000000..f4ae91a70
--- /dev/null
+++ b/src/cpu/minor/buffers.hh
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Classes for buffer, queue and FIFO behaviour.
+ */
+
+#ifndef __CPU_MINOR_BUFFERS_HH__
+#define __CPU_MINOR_BUFFERS_HH__
+
+#include <iostream>
+#include <queue>
+#include <sstream>
+
+#include "cpu/minor/trace.hh"
+#include "cpu/activity.hh"
+#include "cpu/timebuf.hh"
+
+namespace Minor
+{
+
+/** Interface class for data with reporting/tracing facilities. This
+ * interface doesn't actually have to be used as other classes which need
+ * this interface uses templating rather than inheritance but it's provided
+ * here to document the interface needed by those classes. */
+class ReportIF
+{
+ public:
+ /** Print the data in a format suitable to be the value in "name=value"
+ * trace lines */
+ virtual void reportData(std::ostream &os) const = 0;
+
+ virtual ~ReportIF() { }
+};
+
+/** Interface class for data with 'bubble' values. This interface doesn't
+ * actually have to be used as other classes which need this interface uses
+ * templating rather than inheritance but it's provided here to document
+ * the interface needed by those classes. */
+class BubbleIF
+{
+ public:
+ virtual bool isBubble() const = 0;
+};
+
+/** ...ReportTraits are trait classes with the same functionality as
+ * ReportIF, but with elements explicitly passed into the report...
+ * functions. */
+
+/** Allow a template using ReportTraits to call report... functions of
+ * ReportIF-bearing elements themselves */
+template <typename ElemType> /* ElemType should implement ReportIF */
+class ReportTraitsAdaptor
+{
+ public:
+ static void
+ reportData(std::ostream &os, const ElemType &elem)
+ { elem.reportData(os); }
+};
+
+/** A similar adaptor but for elements held by pointer
+ * ElemType should implement ReportIF */
+template <typename PtrType>
+class ReportTraitsPtrAdaptor
+{
+ public:
+ static void
+ reportData(std::ostream &os, const PtrType &elem)
+ { elem->reportData(os); }
+};
+
+/** ... BubbleTraits are trait classes to add BubbleIF interface
+ * functionality to templates which process elements which don't necessarily
+ * implement BubbleIF themselves */
+
+/** Default behaviour, no bubbles */
+template <typename ElemType>
+class NoBubbleTraits
+{
+ public:
+ static bool isBubble(const ElemType &) { return false; }
+ static ElemType bubble() { assert(false); }
+};
+
+/** Pass on call to the element */
+template <typename ElemType>
+class BubbleTraitsAdaptor
+{
+ public:
+ static bool isBubble(const ElemType &elem)
+ { return elem.isBubble(); }
+
+ static ElemType bubble() { return ElemType::bubble(); }
+};
+
+/** Pass on call to the element where the element is a pointer */
+template <typename PtrType, typename ElemType>
+class BubbleTraitsPtrAdaptor
+{
+ public:
+ static bool isBubble(const PtrType &elem)
+ { return elem->isBubble(); }
+
+ static PtrType bubble() { return ElemType::bubble(); }
+};
+
+/** TimeBuffer with MinorTrace and Named interfaces */
+template <typename ElemType,
+ typename ReportTraits = ReportTraitsAdaptor<ElemType>,
+ typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
+class MinorBuffer : public Named, public TimeBuffer<ElemType>
+{
+ protected:
+ /** The range of elements that should appear in trace lines */
+ int reportLeft, reportRight;
+
+ /** Name to use for the data in a MinorTrace line */
+ std::string dataName;
+
+ public:
+ MinorBuffer(const std::string &name,
+ const std::string &data_name,
+ int num_past, int num_future,
+ int report_left = -1, int report_right = -1) :
+ Named(name), TimeBuffer<ElemType>(num_past, num_future),
+ reportLeft(report_left), reportRight(report_right),
+ dataName(data_name)
+ { }
+
+ public:
+ /* Is this buffer full of only bubbles */
+ bool
+ empty() const
+ {
+ bool ret = true;
+
+ for (int i = -this->past; i <= this->future; i++) {
+ if (!BubbleTraits::isBubble((*this)[i]))
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ /** Report buffer states from 'slot' 'from' to 'to'. For example 0,-1
+ * will produce two slices with current (just assigned) and last (one
+ * advance() old) slices with the current (0) one on the left.
+ * Reverse the numbers to change the order of slices */
+ void
+ minorTrace() const
+ {
+ std::ostringstream data;
+
+ int step = (reportLeft > reportRight ? -1 : 1);
+ int end = reportRight + step;
+ int i = reportLeft;
+
+ while (i != end) {
+ const ElemType &datum = (*this)[i];
+
+ ReportTraits::reportData(data, datum);
+ i += step;
+ if (i != end)
+ data << ',';
+ }
+
+ MINORTRACE("%s=%s\n", dataName, data.str());
+ }
+};
+
+/** Wraps a MinorBuffer with Input/Output interfaces to ensure that units
+ * within the model can only see the right end of buffers between them. */
+template <typename Data>
+class Latch
+{
+ public:
+ typedef MinorBuffer<Data> Buffer;
+
+ protected:
+ /** Delays, in cycles, writing data into the latch and seeing it on the
+ * latched wires */
+ Cycles delay;
+
+ Buffer buffer;
+
+ public:
+ /** forward/backwardDelay specify the delay from input to output in each
+ * direction. These arguments *must* be >= 1 */
+ Latch(const std::string &name,
+ const std::string &data_name,
+ Cycles delay_ = Cycles(1),
+ bool report_backwards = false) :
+ delay(delay_),
+ buffer(name, data_name, delay_, 0, (report_backwards ? -delay_ : 0),
+ (report_backwards ? 0 : -delay_))
+ { }
+
+ public:
+ /** Encapsulate wires on either input or output of the latch.
+ * forward/backward correspond to data direction relative to the
+ * pipeline. Latched and Immediate specify delay for backward data.
+ * Immediate data is available to earlier stages *during* the cycle it
+ * is written */
+ class Input
+ {
+ public:
+ typename Buffer::wire inputWire;
+
+ public:
+ Input(typename Buffer::wire input_wire) :
+ inputWire(input_wire)
+ { }
+ };
+
+ class Output
+ {
+ public:
+ typename Buffer::wire outputWire;
+
+ public:
+ Output(typename Buffer::wire output_wire) :
+ outputWire(output_wire)
+ { }
+ };
+
+ bool empty() const { return buffer.empty(); }
+
+ /** An interface to just the input of the buffer */
+ Input input() { return Input(buffer.getWire(0)); }
+
+ /** An interface to just the output of the buffer */
+ Output output() { return Output(buffer.getWire(-delay)); }
+
+ void minorTrace() const { buffer.minorTrace(); }
+
+ void evaluate() { buffer.advance(); }
+};
+
+/** A pipeline simulating class that will stall (not advance when advance()
+ * is called) if a non-bubble value lies at the far end of the pipeline.
+ * The user can clear the stall before calling advance to unstall the
+ * pipeline. */
+template <typename ElemType,
+ typename ReportTraits,
+ typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
+class SelfStallingPipeline : public MinorBuffer<ElemType, ReportTraits>
+{
+ protected:
+ /** Wire at the input end of the pipeline (for convenience) */
+ typename TimeBuffer<ElemType>::wire pushWire;
+ /** Wire at the output end of the pipeline (for convenience) */
+ typename TimeBuffer<ElemType>::wire popWire;
+
+ public:
+ /** If true, advance will not advance the pipeline */
+ bool stalled;
+
+ /** The number of slots with non-bubbles in them */
+ unsigned int occupancy;
+
+ public:
+ SelfStallingPipeline(const std::string &name,
+ const std::string &data_name,
+ unsigned depth) :
+ MinorBuffer<ElemType, ReportTraits>
+ (name, data_name, depth, 0, -1, -depth),
+ pushWire(this->getWire(0)),
+ popWire(this->getWire(-depth)),
+ stalled(false),
+ occupancy(0)
+ {
+ assert(depth > 0);
+
+ /* Write explicit bubbles to get around the case where the default
+ * constructor for the element type isn't good enough */
+ for (unsigned i = 0; i <= depth; i++)
+ (*this)[-i] = BubbleTraits::bubble();
+ }
+
+ public:
+ /** Write an element to the back of the pipeline. This doesn't cause
+ * the pipeline to advance until advance is called. Pushing twice
+ * without advance-ing will just cause an overwrite of the last push's
+ * data. */
+ void push(ElemType &elem)
+ {
+ assert(!alreadyPushed());
+ *pushWire = elem;
+ if (!BubbleTraits::isBubble(elem))
+ occupancy++;
+ }
+
+ /** Peek at the end element of the pipe */
+ ElemType &front() { return *popWire; }
+
+ const ElemType &front() const { return *popWire; }
+
+ /** Have we already pushed onto this pipe without advancing */
+ bool alreadyPushed() { return !BubbleTraits::isBubble(*pushWire); }
+
+ /** There's data (not a bubble) at the end of the pipe */
+ bool isPopable() { return !BubbleTraits::isBubble(front()); }
+
+ /** Try to advance the pipeline. If we're stalled, don't advance. If
+ * we're not stalled, advance then check to see if we become stalled
+ * (a non-bubble at the end of the pipe) */
+ void
+ advance()
+ {
+ bool data_at_end = isPopable();
+
+ if (!stalled) {
+ TimeBuffer<ElemType>::advance();
+ /* If there was data at the end of the pipe that has now been
+ * advanced out of the pipe, we've lost data */
+ if (data_at_end)
+ occupancy--;
+ /* Is there data at the end of the pipe now? */
+ stalled = isPopable();
+ /* Insert a bubble into the empty input slot to make sure that
+ * element is correct in the case where the default constructor
+ * for ElemType doesn't produce a bubble */
+ ElemType bubble = BubbleTraits::bubble();
+ *pushWire = bubble;
+ }
+ }
+};
+
+/** Base class for space reservation requestable objects */
+class Reservable
+{
+ public:
+ /** Can a slot be reserved? */
+ virtual bool canReserve() const = 0;
+
+ /** Reserve a slot in whatever structure this is attached to */
+ virtual void reserve() = 0;
+
+ /** Free a reserved slot */
+ virtual void freeReservation() = 0;
+};
+
+/** Wrapper for a queue type to act as a pipeline stage input queue.
+ * Handles capacity management, bubble value suppression and provides
+ * reporting.
+ *
+ * In an ideal world, ElemType would be derived from ReportIF and BubbleIF,
+ * but here we use traits and allow the Adaptors ReportTraitsAdaptor and
+ * BubbleTraitsAdaptor to work on data which *does* directly implement
+ * those interfaces. */
+template <typename ElemType,
+ typename ReportTraits = ReportTraitsAdaptor<ElemType>,
+ typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
+class Queue : public Named, public Reservable
+{
+ private:
+ std::deque<ElemType> queue;
+
+ /** Number of slots currently reserved for future (reservation
+ * respecting) pushes */
+ unsigned int numReservedSlots;
+
+ /** Need this here as queues usually don't have a limited capacity */
+ unsigned int capacity;
+
+ /** Name to use for the data in MinorTrace */
+ std::string dataName;
+
+ public:
+ Queue(const std::string &name, const std::string &data_name,
+ unsigned int capacity_) :
+ Named(name),
+ numReservedSlots(0),
+ capacity(capacity_),
+ dataName(data_name)
+ { }
+
+ virtual ~Queue() { }
+
+ public:
+ /** Push an element into the buffer if it isn't a bubble. Bubbles are
+ * just discarded. It is assummed that any push into a queue with
+ * reserved space intends to take that space */
+ void
+ push(ElemType &data)
+ {
+ if (!BubbleTraits::isBubble(data)) {
+ freeReservation();
+ queue.push_back(data);
+
+ if (queue.size() > capacity) {
+ warn("%s: No space to push data into queue of capacity"
+ " %u, pushing anyway\n", name(), capacity);
+ }
+
+ }
+ }
+
+ /** Clear all allocated space. Be careful how this is used */
+ void clearReservedSpace() { numReservedSlots = 0; }
+
+ /** Clear a single reserved slot */
+ void freeReservation()
+ {
+ if (numReservedSlots != 0)
+ numReservedSlots--;
+ }
+
+ /** Reserve space in the queue for future pushes. Enquiries about space
+ * in the queue using unreservedRemainingSpace will only tell about
+ * space which is not full and not reserved. */
+ void
+ reserve()
+ {
+ /* Check reservable space */
+ if (unreservedRemainingSpace() == 0)
+ warn("%s: No space is reservable in queue", name());
+
+ numReservedSlots++;
+ }
+
+ bool canReserve() const { return unreservedRemainingSpace() != 0; }
+
+ /** Number of slots available in an empty buffer */
+ unsigned int totalSpace() const { return capacity; }
+
+ /** Number of slots already occupied in this buffer */
+ unsigned int occupiedSpace() const { return queue.size(); }
+
+ /** Number of slots which are reserved. */
+ unsigned int reservedSpace() const { return numReservedSlots; }
+
+ /** Number of slots yet to fill in this buffer. This doesn't include
+ * reservation. */
+ unsigned int
+ remainingSpace() const
+ {
+ int ret = capacity - queue.size();
+
+ return (ret < 0 ? 0 : ret);
+ }
+
+ /** Like remainingSpace but does not count reserved spaces */
+ unsigned int
+ unreservedRemainingSpace() const
+ {
+ int ret = capacity - (queue.size() + numReservedSlots);
+
+ return (ret < 0 ? 0 : ret);
+ }
+
+ /** Head value. Like std::queue::front */
+ ElemType &front() { return queue.front(); }
+
+ const ElemType &front() const { return queue.front(); }
+
+ /** Pop the head item. Like std::queue::pop */
+ void pop() { queue.pop_front(); }
+
+ /** Is the queue empty? */
+ bool empty() const { return queue.empty(); }
+
+ void
+ minorTrace() const
+ {
+ std::ostringstream data;
+ /* If we become over-full, totalSpace() can actually be smaller than
+ * occupiedSpace(). Handle this */
+ unsigned int num_total = (occupiedSpace() > totalSpace() ?
+ occupiedSpace() : totalSpace());
+
+ unsigned int num_reserved = reservedSpace();
+ unsigned int num_occupied = occupiedSpace();
+
+ int num_printed = 1;
+ /* Bodge to rotate queue to report elements */
+ while (num_printed <= num_occupied) {
+ ReportTraits::reportData(data, queue[num_printed - 1]);
+ num_printed++;
+
+ if (num_printed <= num_total)
+ data << ',';
+ }
+
+ int num_printed_reserved = 1;
+ /* Show reserved slots */
+ while (num_printed_reserved <= num_reserved &&
+ num_printed <= num_total)
+ {
+ data << 'R';
+ num_printed_reserved++;
+ num_printed++;
+
+ if (num_printed <= num_total)
+ data << ',';
+ }
+
+ /* And finally pad with empty slots (if there are any) */
+ while (num_printed <= num_total) {
+ num_printed++;
+
+ if (num_printed <= num_total)
+ data << ',';
+ }
+
+ MINORTRACE("%s=%s\n", dataName, data.str());
+ }
+};
+
+/** Like a Queue but with a restricted interface and a setTail function
+ * which, when the queue is empty, just takes a reference to the pushed
+ * item as the single element. Calling pushTail will push that element
+ * onto the queue.
+ *
+ * The purpose of this class is to allow the faster operation of queues of
+ * items which usually don't get deeper than one item and for which the copy
+ * associated with a push is expensive enough to want to avoid
+ *
+ * The intended use case is the input buffer for pipeline stages, hence the
+ * class name */
+template <typename ElemType,
+ typename ReportTraits = ReportTraitsAdaptor<ElemType>,
+ typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
+class InputBuffer : public Reservable
+{
+ protected:
+ /** Underlying queue */
+ mutable Queue<ElemType, ReportTraits, BubbleTraits> queue;
+
+ /** Pointer to the single element (if not NULL) */
+ mutable ElemType *elementPtr;
+
+ public:
+ InputBuffer(const std::string &name, const std::string &data_name,
+ unsigned int capacity_) :
+ queue(name, data_name, capacity_),
+ elementPtr(NULL)
+ { }
+
+ public:
+ /** Set the tail of the queue, this is like push but needs
+ * to be followed by pushTail for the new tail to make its
+ * way into the queue proper */
+ void
+ setTail(ElemType &new_element)
+ {
+ assert(!elementPtr);
+ if (!BubbleTraits::isBubble(new_element)) {
+ if (queue.empty())
+ elementPtr = &new_element;
+ else
+ queue.push(new_element);
+ }
+ }
+
+ /** No single element or queue entries */
+ bool empty() const { return !elementPtr && queue.empty(); }
+
+ /** Return the element, or the front of the queue */
+ const ElemType &front() const
+ { return (elementPtr ? *elementPtr : queue.front()); }
+
+ ElemType &front()
+ { return (elementPtr ? *elementPtr : queue.front()); }
+
+ /** Pop either the head, or if none, the head of the queue */
+ void
+ pop()
+ {
+ if (elementPtr) {
+ /* A popped element was expected to be pushed into queue
+ * and so take a reserved space */
+ elementPtr = NULL;
+ queue.freeReservation();
+ } else {
+ queue.pop();
+ }
+ }
+
+ /** Push the single element (if any) into the queue proper. If the
+ * element's reference points to a transient object, remember to
+ * always do this before the end of that object's life */
+ void
+ pushTail() const
+ {
+ if (elementPtr)
+ queue.push(*elementPtr);
+ elementPtr = NULL;
+ }
+
+ /** Report elements */
+ void
+ minorTrace() const
+ {
+ pushTail();
+ queue.minorTrace();
+ }
+
+ /** Reservable interface, passed on to queue */
+ bool canReserve() const { return queue.canReserve(); }
+ void reserve() { queue.reserve(); }
+ void freeReservation() { queue.freeReservation(); }
+
+ /** Like remainingSpace but does not count reserved spaces */
+ unsigned int
+ unreservedRemainingSpace()
+ {
+ pushTail();
+ return queue.unreservedRemainingSpace();
+ }
+};
+
+}
+
+#endif /* __CPU_MINOR_BUFFERS_HH__ */
diff --git a/src/cpu/minor/cpu.cc b/src/cpu/minor/cpu.cc
new file mode 100644
index 000000000..f7007f6ff
--- /dev/null
+++ b/src/cpu/minor/cpu.cc
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2012-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "arch/utility.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/minor/fetch1.hh"
+#include "cpu/minor/pipeline.hh"
+#include "debug/Drain.hh"
+#include "debug/MinorCPU.hh"
+#include "debug/Quiesce.hh"
+
+MinorCPU::MinorCPU(MinorCPUParams *params) :
+ BaseCPU(params),
+ drainManager(NULL)
+{
+ /* This is only written for one thread at the moment */
+ Minor::MinorThread *thread;
+
+ if (FullSystem) {
+ thread = new Minor::MinorThread(this, 0, params->system, params->itb,
+ params->dtb, params->isa[0]);
+ } else {
+ /* thread_id 0 */
+ thread = new Minor::MinorThread(this, 0, params->system,
+ params->workload[0], params->itb, params->dtb, params->isa[0]);
+ }
+
+ threads.push_back(thread);
+ threadActivateEvents.push_back(new ThreadActivateEvent(*this, 0));
+
+ thread->setStatus(ThreadContext::Halted);
+
+ ThreadContext *tc = thread->getTC();
+
+ if (params->checker) {
+ fatal("The Minor model doesn't support checking (yet)\n");
+ }
+
+ threadContexts.push_back(tc);
+
+ Minor::MinorDynInst::init();
+
+ pipeline = new Minor::Pipeline(*this, *params);
+ activityRecorder = pipeline->getActivityRecorder();
+}
+
+MinorCPU::~MinorCPU()
+{
+ delete pipeline;
+
+ for (ThreadID thread_id = 0; thread_id < threads.size(); thread_id++) {
+ delete threads[thread_id];
+ delete threadActivateEvents[thread_id];
+ }
+}
+
+void
+MinorCPU::init()
+{
+ BaseCPU::init();
+
+ if (!params()->switched_out &&
+ system->getMemoryMode() != Enums::timing)
+ {
+ fatal("The Minor CPU requires the memory system to be in "
+ "'timing' mode.\n");
+ }
+
+ /* Initialise the ThreadContext's memory proxies */
+ for (ThreadID thread_id = 0; thread_id < threads.size(); thread_id++) {
+ ThreadContext *tc = getContext(thread_id);
+
+ tc->initMemProxies(tc);
+ }
+
+ /* Initialise CPUs (== threads in the ISA) */
+ if (FullSystem && !params()->switched_out) {
+ for (ThreadID thread_id = 0; thread_id < threads.size(); thread_id++)
+ {
+ ThreadContext *tc = getContext(thread_id);
+
+ /* Initialize CPU, including PC */
+ TheISA::initCPU(tc, cpuId());
+ }
+ }
+}
+
+/** Stats interface from SimObject (by way of BaseCPU) */
+void
+MinorCPU::regStats()
+{
+ BaseCPU::regStats();
+ stats.regStats(name(), *this);
+ pipeline->regStats();
+}
+
+void
+MinorCPU::serializeThread(std::ostream &os, ThreadID thread_id)
+{
+ threads[thread_id]->serialize(os);
+}
+
+void
+MinorCPU::unserializeThread(Checkpoint *cp, const std::string &section,
+ ThreadID thread_id)
+{
+ if (thread_id != 0)
+ fatal("Trying to load more than one thread into a MinorCPU\n");
+
+ threads[thread_id]->unserialize(cp, section);
+}
+
+void
+MinorCPU::serialize(std::ostream &os)
+{
+ pipeline->serialize(os);
+ BaseCPU::serialize(os);
+}
+
+void
+MinorCPU::unserialize(Checkpoint *cp, const std::string &section)
+{
+ pipeline->unserialize(cp, section);
+ BaseCPU::unserialize(cp, section);
+}
+
+Addr
+MinorCPU::dbg_vtophys(Addr addr)
+{
+ /* Note that this gives you the translation for thread 0 */
+ panic("No implementation for vtophy\n");
+
+ return 0;
+}
+
+void
+MinorCPU::wakeup()
+{
+ DPRINTF(Drain, "MinorCPU wakeup\n");
+
+ for (auto i = threads.begin(); i != threads.end(); i ++) {
+ if ((*i)->status() == ThreadContext::Suspended)
+ (*i)->activate();
+ }
+
+ DPRINTF(Drain,"Suspended Processor awoke\n");
+}
+
+void
+MinorCPU::startup()
+{
+ DPRINTF(MinorCPU, "MinorCPU startup\n");
+
+ BaseCPU::startup();
+
+ for (auto i = threads.begin(); i != threads.end(); i ++)
+ (*i)->startup();
+}
+
+unsigned int
+MinorCPU::drain(DrainManager *drain_manager)
+{
+ DPRINTF(Drain, "MinorCPU drain\n");
+
+ drainManager = drain_manager;
+
+ /* Need to suspend all threads and wait for Execute to idle.
+ * Tell Fetch1 not to fetch */
+ unsigned int ret = pipeline->drain(drain_manager);
+
+ if (ret == 0)
+ DPRINTF(Drain, "MinorCPU drained\n");
+ else
+ DPRINTF(Drain, "MinorCPU not finished draining\n");
+
+ return ret;
+}
+
+void
+MinorCPU::signalDrainDone()
+{
+ DPRINTF(Drain, "MinorCPU drain done\n");
+ setDrainState(Drainable::Drained);
+ drainManager->signalDrainDone();
+ drainManager = NULL;
+}
+
+void
+MinorCPU::drainResume()
+{
+ assert(getDrainState() == Drainable::Drained ||
+ getDrainState() == Drainable::Running);
+
+ if (switchedOut()) {
+ DPRINTF(Drain, "drainResume while switched out. Ignoring\n");
+ return;
+ }
+
+ DPRINTF(Drain, "MinorCPU drainResume\n");
+
+ if (!system->isTimingMode()) {
+ fatal("The Minor CPU requires the memory system to be in "
+ "'timing' mode.\n");
+ }
+
+ wakeup();
+ pipeline->drainResume();
+
+ setDrainState(Drainable::Running);
+}
+
+void
+MinorCPU::memWriteback()
+{
+ DPRINTF(Drain, "MinorCPU memWriteback\n");
+}
+
+void
+MinorCPU::switchOut()
+{
+ DPRINTF(MinorCPU, "MinorCPU switchOut\n");
+
+ assert(!switchedOut());
+ BaseCPU::switchOut();
+
+ /* Check that the CPU is drained? */
+ activityRecorder->reset();
+}
+
+void
+MinorCPU::takeOverFrom(BaseCPU *old_cpu)
+{
+ DPRINTF(MinorCPU, "MinorCPU takeOverFrom\n");
+
+ BaseCPU::takeOverFrom(old_cpu);
+
+ /* Don't think I need to do anything here */
+}
+
+void
+MinorCPU::activateContext(ThreadID thread_id, Cycles delay)
+{
+ DPRINTF(MinorCPU, "ActivateContext thread: %d delay: %d\n",
+ thread_id, delay);
+
+ if (!threadActivateEvents[thread_id]->scheduled()) {
+ schedule(threadActivateEvents[thread_id], clockEdge(delay));
+ }
+}
+
+void
+MinorCPU::ThreadActivateEvent::process()
+{
+ DPRINTFS(MinorCPU, (&cpu), "Activating thread: %d\n", thread_id);
+
+ /* Do some cycle accounting. lastStopped is reset to stop the
+ * wakeup call on the pipeline from adding the quiesce period
+ * to BaseCPU::numCycles */
+ cpu.stats.quiesceCycles += cpu.pipeline->cyclesSinceLastStopped();
+ cpu.pipeline->resetLastStopped();
+
+ /* Wake up the thread, wakeup the pipeline tick */
+ cpu.threads[thread_id]->activate();
+ cpu.wakeupOnEvent(Minor::Pipeline::CPUStageId);
+ cpu.pipeline->wakeupFetch();
+}
+
+void
+MinorCPU::suspendContext(ThreadID thread_id)
+{
+ DPRINTF(MinorCPU, "SuspendContext %d\n", thread_id);
+
+ threads[thread_id]->suspend();
+}
+
+void
+MinorCPU::wakeupOnEvent(unsigned int stage_id)
+{
+ DPRINTF(Quiesce, "Event wakeup from stage %d\n", stage_id);
+
+ /* Mark that some activity has taken place and start the pipeline */
+ activityRecorder->activateStage(stage_id);
+ pipeline->start();
+}
+
+MinorCPU *
+MinorCPUParams::create()
+{
+ numThreads = 1;
+ if (!FullSystem && workload.size() != 1)
+ panic("only one workload allowed");
+ return new MinorCPU(this);
+}
+
+MasterPort &MinorCPU::getInstPort()
+{
+ return pipeline->getInstPort();
+}
+
+MasterPort &MinorCPU::getDataPort()
+{
+ return pipeline->getDataPort();
+}
+
+Counter
+MinorCPU::totalInsts() const
+{
+ Counter ret = 0;
+
+ for (auto i = threads.begin(); i != threads.end(); i ++)
+ ret += (*i)->numInst;
+
+ return ret;
+}
+
+Counter
+MinorCPU::totalOps() const
+{
+ Counter ret = 0;
+
+ for (auto i = threads.begin(); i != threads.end(); i ++)
+ ret += (*i)->numOp;
+
+ return ret;
+}
diff --git a/src/cpu/minor/cpu.hh b/src/cpu/minor/cpu.hh
new file mode 100644
index 000000000..80f41b5d2
--- /dev/null
+++ b/src/cpu/minor/cpu.hh
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2012-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Top level definition of the Minor in-order CPU model
+ */
+
+#ifndef __CPU_MINOR_CPU_HH__
+#define __CPU_MINOR_CPU_HH__
+
+#include "cpu/minor/activity.hh"
+#include "cpu/minor/stats.hh"
+#include "cpu/base.hh"
+#include "cpu/simple_thread.hh"
+#include "params/MinorCPU.hh"
+
+namespace Minor
+{
+/** Forward declared to break the cyclic inclusion dependencies between
+ * pipeline and cpu */
+class Pipeline;
+
+/** Minor will use the SimpleThread state for now */
+typedef SimpleThread MinorThread;
+};
+
+/**
+ * MinorCPU is an in-order CPU model with four fixed pipeline stages:
+ *
+ * Fetch1 - fetches lines from memory
+ * Fetch2 - decomposes lines into macro-op instructions
+ * Decode - decomposes macro-ops into micro-ops
+ * Execute - executes those micro-ops
+ *
+ * This pipeline is carried in the MinorCPU::pipeline object.
+ * The exec_context interface is not carried by MinorCPU but by
+ * Minor::ExecContext objects
+ * created by Minor::Execute.
+ */
+class MinorCPU : public BaseCPU
+{
+ protected:
+ /** Event for delayed wakeup of a thread */
+ class ThreadActivateEvent : public Event
+ {
+ public:
+ MinorCPU &cpu;
+ ThreadID thread_id;
+
+ ThreadActivateEvent(MinorCPU &cpu_, ThreadID thread_id_) :
+ cpu(cpu_), thread_id(thread_id_)
+ { }
+
+ void process();
+ };
+
+ /** Events to wakeup each thread */
+ std::vector<ThreadActivateEvent *> threadActivateEvents;
+
+ /** pipeline is a container for the clockable pipeline stage objects.
+ * Elements of pipeline call TheISA to implement the model. */
+ Minor::Pipeline *pipeline;
+
+ public:
+ /** Activity recording for pipeline. This belongs to Pipeline but
+ * stages will access it through the CPU as the MinorCPU object
+ * actually mediates idling behaviour */
+ Minor::MinorActivityRecorder *activityRecorder;
+
+ /** These are thread state-representing objects for this CPU. If
+ * you need a ThreadContext for *any* reason, use
+ * threads[threadId]->getTC() */
+ std::vector<Minor::MinorThread *> threads;
+
+ public:
+ /** Provide a non-protected base class for Minor's Ports as derived
+ * classes are created by Fetch1 and Execute */
+ class MinorCPUPort : public MasterPort
+ {
+ public:
+ /** The enclosing cpu */
+ MinorCPU &cpu;
+
+ public:
+ MinorCPUPort(const std::string& name_, MinorCPU &cpu_)
+ : MasterPort(name_, &cpu_), cpu(cpu_)
+ { }
+
+ protected:
+ /** Snooping a coherence request, do nothing. */
+ virtual void recvTimingSnoopReq(PacketPtr pkt) { }
+ };
+
+ /** The DrainManager passed into drain that needs be signalled when
+ * draining is complete */
+ DrainManager *drainManager;
+
+ protected:
+ /** Return a reference to the data port. */
+ MasterPort &getDataPort();
+
+ /** Return a reference to the instruction port. */
+ MasterPort &getInstPort();
+
+ public:
+ MinorCPU(MinorCPUParams *params);
+
+ ~MinorCPU();
+
+ public:
+ /** Starting, waking and initialisation */
+ void init();
+ void startup();
+ void wakeup();
+
+ Addr dbg_vtophys(Addr addr);
+
+ /** Processor-specific statistics */
+ Minor::MinorStats stats;
+
+ /** Stats interface from SimObject (by way of BaseCPU) */
+ void regStats();
+
+ /** Simple inst count interface from BaseCPU */
+ Counter totalInsts() const;
+ Counter totalOps() const;
+
+ void serializeThread(std::ostream &os, ThreadID thread_id);
+ void unserializeThread(Checkpoint *cp, const std::string &section,
+ ThreadID thread_id);
+
+ /** Serialize pipeline data */
+ void serialize(std::ostream &os);
+ void unserialize(Checkpoint *cp, const std::string &section);
+
+ /** Drain interface */
+ unsigned int drain(DrainManager *drain_manager);
+ void drainResume();
+ /** Signal from Pipeline that MinorCPU should signal the DrainManager
+ * that a drain is complete and set its drainState */
+ void signalDrainDone();
+ void memWriteback();
+
+ /** Switching interface from BaseCPU */
+ void switchOut();
+ void takeOverFrom(BaseCPU *old_cpu);
+
+ /** Thread activation interface from BaseCPU. */
+ void activateContext(ThreadID thread_id, Cycles delay);
+ void suspendContext(ThreadID thread_id);
+
+ /** Interface for stages to signal that they have become active after
+ * a callback or eventq event where the pipeline itself may have
+ * already been idled. The stage argument should be from the
+ * enumeration Pipeline::StageId */
+ void wakeupOnEvent(unsigned int stage_id);
+};
+
+#endif /* __CPU_MINOR_CPU_HH__ */
diff --git a/src/cpu/minor/decode.cc b/src/cpu/minor/decode.cc
new file mode 100644
index 000000000..e380f0d2d
--- /dev/null
+++ b/src/cpu/minor/decode.cc
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "cpu/minor/decode.hh"
+#include "cpu/minor/pipeline.hh"
+#include "debug/Decode.hh"
+
+namespace Minor
+{
+
+Decode::Decode(const std::string &name,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardInstData>::Output inp_,
+ Latch<ForwardInstData>::Input out_,
+ Reservable &next_stage_input_buffer) :
+ Named(name),
+ cpu(cpu_),
+ inp(inp_),
+ out(out_),
+ nextStageReserve(next_stage_input_buffer),
+ outputWidth(params.executeInputWidth),
+ processMoreThanOneInput(params.decodeCycleInput),
+ inputBuffer(name + ".inputBuffer", "insts", params.decodeInputBufferSize),
+ inputIndex(0),
+ inMacroop(false),
+ execSeqNum(InstId::firstExecSeqNum)
+{
+ if (outputWidth < 1)
+ fatal("%s: executeInputWidth must be >= 1 (%d)\n", name, outputWidth);
+
+ if (params.decodeInputBufferSize < 1) {
+ fatal("%s: decodeInputBufferSize must be >= 1 (%d)\n", name,
+ params.decodeInputBufferSize);
+ }
+}
+
+const ForwardInstData *
+Decode::getInput()
+{
+ /* Get insts from the inputBuffer to work with */
+ if (!inputBuffer.empty()) {
+ const ForwardInstData &head = inputBuffer.front();
+
+ return (head.isBubble() ? NULL : &(inputBuffer.front()));
+ } else {
+ return NULL;
+ }
+}
+
+void
+Decode::popInput()
+{
+ if (!inputBuffer.empty())
+ inputBuffer.pop();
+
+ inputIndex = 0;
+ inMacroop = false;
+}
+
+#if TRACING_ON
+/** Add the tracing data to an instruction. This originates in
+ * decode because this is the first place that execSeqNums are known
+ * (these are used as the 'FetchSeq' in tracing data) */
+static void
+dynInstAddTracing(MinorDynInstPtr inst, StaticInstPtr static_inst,
+ MinorCPU &cpu)
+{
+ inst->traceData = cpu.getTracer()->getInstRecord(curTick(),
+ cpu.getContext(inst->id.threadId),
+ inst->staticInst, inst->pc, static_inst);
+
+ /* Use the execSeqNum as the fetch sequence number as this most closely
+ * matches the other processor models' idea of fetch sequence */
+ if (inst->traceData)
+ inst->traceData->setFetchSeq(inst->id.execSeqNum);
+}
+#endif
+
+void
+Decode::evaluate()
+{
+ inputBuffer.setTail(*inp.outputWire);
+ ForwardInstData &insts_out = *out.inputWire;
+
+ assert(insts_out.isBubble());
+
+ blocked = false;
+
+ if (!nextStageReserve.canReserve()) {
+ blocked = true;
+ } else {
+ const ForwardInstData *insts_in = getInput();
+
+ unsigned int output_index = 0;
+
+ /* Pack instructions into the output while we can. This may involve
+ * using more than one input line */
+ while (insts_in &&
+ inputIndex < insts_in->width() && /* Still more input */
+ output_index < outputWidth /* Still more output to fill */)
+ {
+ MinorDynInstPtr inst = insts_in->insts[inputIndex];
+
+ if (inst->isBubble()) {
+ /* Skip */
+ inputIndex++;
+ inMacroop = false;
+ } else {
+ StaticInstPtr static_inst = inst->staticInst;
+ /* Static inst of a macro-op above the output_inst */
+ StaticInstPtr parent_static_inst = NULL;
+ MinorDynInstPtr output_inst = inst;
+
+ if (inst->isFault()) {
+ DPRINTF(Decode, "Fault being passed: %d\n",
+ inst->fault->name());
+
+ inputIndex++;
+ inMacroop = false;
+ } else if (static_inst->isMacroop()) {
+ /* Generate a new micro-op */
+ StaticInstPtr static_micro_inst;
+
+ /* Set up PC for the next micro-op emitted */
+ if (!inMacroop) {
+ microopPC = inst->pc;
+ inMacroop = true;
+ }
+
+ /* Get the micro-op static instruction from the
+ * static_inst. */
+ static_micro_inst =
+ static_inst->fetchMicroop(microopPC.microPC());
+
+ output_inst = new MinorDynInst(inst->id);
+ output_inst->pc = microopPC;
+ output_inst->staticInst = static_micro_inst;
+ output_inst->fault = NoFault;
+
+ /* Allow a predicted next address only on the last
+ * microop */
+ if (static_micro_inst->isLastMicroop()) {
+ output_inst->predictedTaken = inst->predictedTaken;
+ output_inst->predictedTarget = inst->predictedTarget;
+ }
+
+ DPRINTF(Decode, "Microop decomposition inputIndex:"
+ " %d output_index: %d lastMicroop: %s microopPC:"
+ " %d.%d inst: %d\n",
+ inputIndex, output_index,
+ (static_micro_inst->isLastMicroop() ?
+ "true" : "false"),
+ microopPC.instAddr(), microopPC.microPC(),
+ *output_inst);
+
+ /* Acknowledge that the static_inst isn't mine, it's my
+ * parent macro-op's */
+ parent_static_inst = static_inst;
+
+ static_micro_inst->advancePC(microopPC);
+
+ /* Step input if this is the last micro-op */
+ if (static_micro_inst->isLastMicroop()) {
+ inputIndex++;
+ inMacroop = false;
+ }
+ } else {
+ /* Doesn't need decomposing, pass on instruction */
+ DPRINTF(Decode, "Passing on inst: %s inputIndex:"
+ " %d output_index: %d\n",
+ *output_inst, inputIndex, output_index);
+
+ parent_static_inst = static_inst;
+
+ /* Step input */
+ inputIndex++;
+ inMacroop = false;
+ }
+
+ /* Set execSeqNum of output_inst */
+ output_inst->id.execSeqNum = execSeqNum;
+ /* Add tracing */
+#if TRACING_ON
+ dynInstAddTracing(output_inst, parent_static_inst, cpu);
+#endif
+
+ /* Step to next sequence number */
+ execSeqNum++;
+
+ /* Correctly size the output before writing */
+ if(output_index == 0) insts_out.resize(outputWidth);
+ /* Push into output */
+ insts_out.insts[output_index] = output_inst;
+ output_index++;
+ }
+
+ /* Have we finished with the input? */
+ if (inputIndex == insts_in->width()) {
+ /* If we have just been producing micro-ops, we *must* have
+ * got to the end of that for inputIndex to be pushed past
+ * insts_in->width() */
+ assert(!inMacroop);
+ popInput();
+ insts_in = NULL;
+
+ if (processMoreThanOneInput) {
+ DPRINTF(Decode, "Wrapping\n");
+ insts_in = getInput();
+ }
+ }
+ }
+
+ /* The rest of the output (if any) should already have been packed
+ * with bubble instructions by insts_out's initialisation
+ *
+ * for (; output_index < outputWidth; output_index++)
+ * assert(insts_out.insts[output_index]->isBubble());
+ */
+ }
+
+ /* If we generated output, reserve space for the result in the next stage
+ * and mark the stage as being active this cycle */
+ if (!insts_out.isBubble()) {
+ /* Note activity of following buffer */
+ cpu.activityRecorder->activity();
+ nextStageReserve.reserve();
+ }
+
+ /* If we still have input to process and somewhere to put it,
+ * mark stage as active */
+ if (getInput() && nextStageReserve.canReserve())
+ cpu.activityRecorder->activateStage(Pipeline::DecodeStageId);
+
+ /* Make sure the input (if any left) is pushed */
+ inputBuffer.pushTail();
+}
+
+bool
+Decode::isDrained()
+{
+ return inputBuffer.empty() && (*inp.outputWire).isBubble();
+}
+
+void
+Decode::minorTrace() const
+{
+ std::ostringstream data;
+
+ if (blocked)
+ data << 'B';
+ else
+ (*out.inputWire).reportData(data);
+
+ MINORTRACE("insts=%s\n", data.str());
+ inputBuffer.minorTrace();
+}
+
+}
diff --git a/src/cpu/minor/decode.hh b/src/cpu/minor/decode.hh
new file mode 100644
index 000000000..fcc18fd44
--- /dev/null
+++ b/src/cpu/minor/decode.hh
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Decode collects macro-ops from Fetch2 and splits them into micro-ops
+ * passed to Execute.
+ */
+
+#ifndef __CPU_MINOR_DECODE_HH__
+#define __CPU_MINOR_DECODE_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/minor/pipe_data.hh"
+
+namespace Minor
+{
+
+/* Decode takes instructions from Fetch2 and decomposes them into micro-ops
+ * to feed to Execute. It generates a new sequence number for each
+ * instruction: execSeqNum.
+ */
+class Decode : public Named
+{
+ protected:
+ /** Pointer back to the containing CPU */
+ MinorCPU &cpu;
+
+ /** Input port carrying macro instructions from Fetch2 */
+ Latch<ForwardInstData>::Output inp;
+ /** Output port carrying micro-op decomposed instructions to Execute */
+ Latch<ForwardInstData>::Input out;
+
+ /** Interface to reserve space in the next stage */
+ Reservable &nextStageReserve;
+
+ /** Width of output of this stage/input of next in instructions */
+ unsigned int outputWidth;
+
+ /** If true, more than one input word can be processed each cycle if
+ * there is room in the output to contain its processed data */
+ bool processMoreThanOneInput;
+
+ public:
+ /* Public for Pipeline to be able to pass it to Fetch2 */
+ InputBuffer<ForwardInstData> inputBuffer;
+
+ protected:
+ /** Data members after this line are cycle-to-cycle state */
+
+ /** Index into the inputBuffer's head marking the start of unhandled
+ * instructions */
+ unsigned int inputIndex;
+
+ /** True when we're in the process of decomposing a micro-op and
+ * microopPC will be valid. This is only the case when there isn't
+ * sufficient space in Executes input buffer to take the whole of a
+ * decomposed instruction and some of that instructions micro-ops must
+ * be generated in a later cycle */
+ bool inMacroop;
+ TheISA::PCState microopPC;
+
+ /** Source of execSeqNums to number instructions. */
+ InstSeqNum execSeqNum;
+
+ /** Blocked indication for report */
+ bool blocked;
+
+ protected:
+ /** Get a piece of data to work on, or 0 if there is no data. */
+ const ForwardInstData *getInput();
+
+ /** Pop an element off the input buffer, if there are any */
+ void popInput();
+
+ public:
+ Decode(const std::string &name,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardInstData>::Output inp_,
+ Latch<ForwardInstData>::Input out_,
+ Reservable &next_stage_input_buffer);
+
+ public:
+ /** Pass on input/buffer data to the output if you can */
+ void evaluate();
+
+ void minorTrace() const;
+
+ /** Is this stage drained? For Decoed, draining is initiated by
+ * Execute halting Fetch1 causing Fetch2 to naturally drain
+ * into Decode and on to Execute which is responsible for
+ * actually killing instructions */
+ bool isDrained();
+};
+
+}
+
+#endif /* __CPU_MINOR_DECODE_HH__ */
diff --git a/src/cpu/minor/dyn_inst.cc b/src/cpu/minor/dyn_inst.cc
new file mode 100644
index 000000000..ab08e6b4a
--- /dev/null
+++ b/src/cpu/minor/dyn_inst.cc
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#include "arch/isa.hh"
+#include "arch/registers.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/minor/trace.hh"
+#include "cpu/base.hh"
+#include "cpu/reg_class.hh"
+#include "debug/MinorExecute.hh"
+#include "enums/OpClass.hh"
+
+namespace Minor
+{
+
+std::ostream &
+operator <<(std::ostream &os, const InstId &id)
+{
+ os << id.threadId << '/' << id.streamSeqNum << '.'
+ << id.predictionSeqNum << '/' << id.lineSeqNum;
+
+ /* Not all structures have fetch and exec sequence numbers */
+ if (id.fetchSeqNum != 0) {
+ os << '/' << id.fetchSeqNum;
+ if (id.execSeqNum != 0)
+ os << '.' << id.execSeqNum;
+ }
+
+ return os;
+}
+
+MinorDynInstPtr MinorDynInst::bubbleInst = NULL;
+
+void
+MinorDynInst::init()
+{
+ if (!bubbleInst) {
+ bubbleInst = new MinorDynInst();
+ assert(bubbleInst->isBubble());
+ /* Make bubbleInst immortal */
+ bubbleInst->incref();
+ }
+}
+
+bool
+MinorDynInst::isLastOpInInst() const
+{
+ assert(staticInst);
+ return !(staticInst->isMicroop() && !staticInst->isLastMicroop());
+}
+
+bool
+MinorDynInst::isNoCostInst() const
+{
+ return isInst() && staticInst->opClass() == No_OpClass;
+}
+
+void
+MinorDynInst::reportData(std::ostream &os) const
+{
+ if (isBubble())
+ os << "-";
+ else if (isFault())
+ os << "F;" << id;
+ else
+ os << id;
+}
+
+std::ostream &
+operator <<(std::ostream &os, const MinorDynInst &inst)
+{
+ os << inst.id << " pc: 0x"
+ << std::hex << inst.pc.instAddr() << std::dec << " (";
+
+ if (inst.isFault())
+ os << "fault: \"" << inst.fault->name() << '"';
+ else if (inst.staticInst)
+ os << inst.staticInst->getName();
+ else
+ os << "bubble";
+
+ os << ')';
+
+ return os;
+}
+
+/** Print a register in the form r<n>, f<n>, m<n>(<name>), z for integer,
+ * float, misc and zero registers given an 'architectural register number' */
+static void
+printRegName(std::ostream &os, TheISA::RegIndex reg)
+{
+ RegClass reg_class = regIdxToClass(reg);
+
+ switch (reg_class)
+ {
+ case MiscRegClass:
+ {
+ TheISA::RegIndex misc_reg = reg - TheISA::Misc_Reg_Base;
+
+ /* This is an ugly test because not all archs. have miscRegName */
+#if THE_ISA == ARM_ISA
+ os << 'm' << misc_reg << '(' << TheISA::miscRegName[misc_reg] <<
+ ')';
+#else
+ os << 'n' << misc_reg;
+#endif
+ }
+ break;
+ case FloatRegClass:
+ os << 'f' << static_cast<unsigned int>(reg - TheISA::FP_Reg_Base);
+ break;
+ case IntRegClass:
+ if (reg == TheISA::ZeroReg) {
+ os << 'z';
+ } else {
+ os << 'r' << static_cast<unsigned int>(reg);
+ }
+ break;
+ case CCRegClass:
+ os << 'c' << static_cast<unsigned int>(reg - TheISA::CC_Reg_Base);
+ }
+}
+
+void
+MinorDynInst::minorTraceInst(const Named &named_object) const
+{
+ if (isFault()) {
+ MINORINST(&named_object, "id=F;%s addr=0x%x fault=\"%s\"\n",
+ id, pc.instAddr(), fault->name());
+ } else {
+ unsigned int num_src_regs = staticInst->numSrcRegs();
+ unsigned int num_dest_regs = staticInst->numDestRegs();
+
+ std::ostringstream regs_str;
+
+ /* Format lists of src and dest registers for microops and
+ * 'full' instructions */
+ if (!staticInst->isMacroop()) {
+ regs_str << " srcRegs=";
+
+ unsigned int src_reg = 0;
+ while (src_reg < num_src_regs) {
+ printRegName(regs_str, staticInst->srcRegIdx(src_reg));
+
+ src_reg++;
+ if (src_reg != num_src_regs)
+ regs_str << ',';
+ }
+
+ regs_str << " destRegs=";
+
+ unsigned int dest_reg = 0;
+ while (dest_reg < num_dest_regs) {
+ printRegName(regs_str, staticInst->destRegIdx(dest_reg));
+
+ dest_reg++;
+ if (dest_reg != num_dest_regs)
+ regs_str << ',';
+ }
+
+#if THE_ISA == ARM_ISA
+ regs_str << " extMachInst=" << std::hex << std::setw(16)
+ << std::setfill('0') << staticInst->machInst << std::dec;
+#endif
+ }
+
+ std::ostringstream flags;
+ staticInst->printFlags(flags, " ");
+
+ MINORINST(&named_object, "id=%s addr=0x%x inst=\"%s\" class=%s"
+ " flags=\"%s\"%s%s\n",
+ id, pc.instAddr(),
+ (staticInst->opClass() == No_OpClass ?
+ "(invalid)" : staticInst->disassemble(0,NULL)),
+ Enums::OpClassStrings[staticInst->opClass()],
+ flags.str(),
+ regs_str.str(),
+ (predictedTaken ? " predictedTaken" : ""));
+ }
+}
+
+MinorDynInst::~MinorDynInst()
+{
+ if (traceData)
+ delete traceData;
+}
+
+}
diff --git a/src/cpu/minor/dyn_inst.hh b/src/cpu/minor/dyn_inst.hh
new file mode 100644
index 000000000..a30d68819
--- /dev/null
+++ b/src/cpu/minor/dyn_inst.hh
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * The dynamic instruction and instruction/line id (sequence numbers)
+ * definition for Minor. A spirited attempt is made here to not carry too
+ * much on this structure.
+ */
+
+#ifndef __CPU_MINOR_DYN_INST_HH__
+#define __CPU_MINOR_DYN_INST_HH__
+
+#include <iostream>
+
+#include "base/refcnt.hh"
+#include "cpu/minor/buffers.hh"
+#include "cpu/inst_seq.hh"
+#include "cpu/static_inst.hh"
+#include "cpu/timing_expr.hh"
+#include "sim/faults.hh"
+
+namespace Minor
+{
+
+class MinorDynInst;
+
+/** MinorDynInsts are currently reference counted. */
+typedef RefCountingPtr<MinorDynInst> MinorDynInstPtr;
+
+/** Id for lines and instructions. This includes all the relevant sequence
+ * numbers and thread ids for all stages of execution. */
+class InstId
+{
+ public:
+ /** First sequence numbers to use in initialisation of the pipeline and
+ * to be expected on the first line/instruction issued */
+ static const InstSeqNum firstStreamSeqNum = 1;
+ static const InstSeqNum firstPredictionSeqNum = 1;
+ static const InstSeqNum firstLineSeqNum = 1;
+ static const InstSeqNum firstFetchSeqNum = 1;
+ static const InstSeqNum firstExecSeqNum = 1;
+
+ public:
+ /** The thread to which this line/instruction belongs */
+ ThreadID threadId;
+
+ /** The 'stream' this instruction belongs to. Streams are interrupted
+ * (and sequence numbers increased) when Execute finds it wants to
+ * change the stream of instructions due to a branch. */
+ InstSeqNum streamSeqNum;
+
+ /** The predicted qualifier to stream, attached by Fetch2 as a
+ * consequence of branch prediction */
+ InstSeqNum predictionSeqNum;
+
+ /** Line sequence number. This is the sequence number of the fetched
+ * line from which this instruction was fetched */
+ InstSeqNum lineSeqNum;
+
+ /** Fetch sequence number. This is 0 for bubbles and an ascending
+ * sequence for the stream of all fetched instructions */
+ InstSeqNum fetchSeqNum;
+
+ /** 'Execute' sequence number. These are assigned after micro-op
+ * decomposition and form an ascending sequence (starting with 1) for
+ * post-micro-op decomposed instructions. */
+ InstSeqNum execSeqNum;
+
+ public:
+ /** Very boring default constructor */
+ InstId(
+ ThreadID thread_id = 0, InstSeqNum stream_seq_num = 0,
+ InstSeqNum prediction_seq_num = 0, InstSeqNum line_seq_num = 0,
+ InstSeqNum fetch_seq_num = 0, InstSeqNum exec_seq_num = 0) :
+ threadId(thread_id), streamSeqNum(stream_seq_num),
+ predictionSeqNum(prediction_seq_num), lineSeqNum(line_seq_num),
+ fetchSeqNum(fetch_seq_num), execSeqNum(exec_seq_num)
+ { }
+
+ public:
+ /* Equal if the thread and last set sequence number matches */
+ bool
+ operator== (const InstId &rhs)
+ {
+ /* If any of fetch and exec sequence number are not set
+ * they need to be 0, so a straight comparison is still
+ * fine */
+ bool ret = (threadId == rhs.threadId &&
+ lineSeqNum == rhs.lineSeqNum &&
+ fetchSeqNum == rhs.fetchSeqNum &&
+ execSeqNum == rhs.execSeqNum);
+
+ /* Stream and prediction *must* match if these are the same id */
+ if (ret) {
+ assert(streamSeqNum == rhs.streamSeqNum &&
+ predictionSeqNum == rhs.predictionSeqNum);
+ }
+
+ return ret;
+ }
+};
+
+/** Print this id in the usual slash-separated format expected by
+ * MinorTrace */
+std::ostream &operator <<(std::ostream &os, const InstId &id);
+
+class MinorDynInst;
+
+/** Print a short reference to this instruction. '-' for a bubble and a
+ * series of '/' separated sequence numbers for other instructions. The
+ * sequence numbers will be in the order: stream, prediction, line, fetch,
+ * exec with exec absent if it is 0. This is used by MinorTrace. */
+std::ostream &operator <<(std::ostream &os, const MinorDynInst &inst);
+
+/** Dynamic instruction for Minor.
+ * MinorDynInst implements the BubbleIF interface
+ * Has two separate notions of sequence number for pre/post-micro-op
+ * decomposition: fetchSeqNum and execSeqNum */
+class MinorDynInst : public RefCounted
+{
+ private:
+ /** A prototypical bubble instruction. You must call MinorDynInst::init
+ * to initialise this */
+ static MinorDynInstPtr bubbleInst;
+
+ public:
+ StaticInstPtr staticInst;
+
+ InstId id;
+
+ /** Trace information for this instruction's execution */
+ Trace::InstRecord *traceData;
+
+ /** The fetch address of this instruction */
+ TheISA::PCState pc;
+
+ /** This is actually a fault masquerading as an instruction */
+ Fault fault;
+
+ /** Tried to predict the destination of this inst (if a control
+ * instruction or a sys call) */
+ bool triedToPredict;
+
+ /** This instruction was predicted to change control flow and
+ * the following instructions will have a newer predictionSeqNum */
+ bool predictedTaken;
+
+ /** Predicted branch target */
+ TheISA::PCState predictedTarget;
+
+ /** Fields only set during execution */
+
+ /** FU this instruction is issued to */
+ unsigned int fuIndex;
+
+ /** This instruction is in the LSQ, not a functional unit */
+ bool inLSQ;
+
+ /** The instruction has been sent to the store buffer */
+ bool inStoreBuffer;
+
+ /** Can this instruction be executed out of order. In this model,
+ * this only happens with mem refs that need to be issued early
+ * to allow other instructions to fill the fetch delay */
+ bool canEarlyIssue;
+
+ /** execSeqNum of the latest inst on which this inst depends.
+ * This can be used as a sanity check for dependency ordering
+ * where slightly out of order execution is required (notably
+ * initiateAcc for memory ops) */
+ InstSeqNum instToWaitFor;
+
+ /** Extra delay at the end of the pipeline */
+ Cycles extraCommitDelay;
+ TimingExpr *extraCommitDelayExpr;
+
+ /** Once issued, extraCommitDelay becomes minimumCommitCycle
+ * to account for delay in absolute time */
+ Cycles minimumCommitCycle;
+
+ /** Flat register indices so that, when clearing the scoreboard, we
+ * have the same register indices as when the instruction was marked
+ * up */
+ TheISA::RegIndex flatDestRegIdx[TheISA::MaxInstDestRegs];
+
+ /** Effective address as set by ExecContext::setEA */
+ Addr ea;
+
+ public:
+ MinorDynInst(InstId id_ = InstId(), Fault fault_ = NoFault) :
+ staticInst(NULL), id(id_), traceData(NULL),
+ pc(TheISA::PCState(0)), fault(fault_),
+ triedToPredict(false), predictedTaken(false),
+ fuIndex(0), inLSQ(false), inStoreBuffer(false),
+ canEarlyIssue(false),
+ instToWaitFor(0), extraCommitDelay(Cycles(0)),
+ extraCommitDelayExpr(NULL), minimumCommitCycle(Cycles(0)),
+ ea(0)
+ { }
+
+ public:
+ /** The BubbleIF interface. */
+ bool isBubble() const { return id.fetchSeqNum == 0; }
+
+ /** There is a single bubble inst */
+ static MinorDynInstPtr bubble() { return bubbleInst; }
+
+ /** Is this a fault rather than instruction */
+ bool isFault() const { return fault != NoFault; }
+
+ /** Is this a real instruction */
+ bool isInst() const { return !isBubble() && !isFault(); }
+
+ /** Is this a real mem ref instruction */
+ bool isMemRef() const { return isInst() && staticInst->isMemRef(); }
+
+ /** Is this an instruction that can be executed `for free' and
+ * needn't spend time in an FU */
+ bool isNoCostInst() const;
+
+ /** Assuming this is not a fault, is this instruction either
+ * a whole instruction or the last microop from a macroop */
+ bool isLastOpInInst() const;
+
+ /** Initialise the class */
+ static void init();
+
+ /** Print (possibly verbose) instruction information for
+ * MinorTrace using the given Named object's name */
+ void minorTraceInst(const Named &named_object) const;
+
+ /** ReportIF interface */
+ void reportData(std::ostream &os) const;
+
+ ~MinorDynInst();
+};
+
+/** Print a summary of the instruction */
+std::ostream &operator <<(std::ostream &os, const MinorDynInst &inst);
+
+}
+
+#endif /* __CPU_MINOR_DYN_INST_HH__ */
diff --git a/src/cpu/minor/exec_context.hh b/src/cpu/minor/exec_context.hh
new file mode 100644
index 000000000..df909a95c
--- /dev/null
+++ b/src/cpu/minor/exec_context.hh
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * Copyright (c) 2013 Advanced Micro Devices, Inc.
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright (c) 2002-2005 The Regents of The University of Michigan
+ * 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: Steve Reinhardt
+ * Dave Greene
+ * Nathan Binkert
+ * Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * ExecContext bears the exec_context interface for Minor.
+ */
+
+#ifndef __CPU_MINOR_EXEC_CONTEXT_HH__
+#define __CPU_MINOR_EXEC_CONTEXT_HH__
+
+#include "cpu/minor/execute.hh"
+#include "cpu/minor/pipeline.hh"
+#include "cpu/base.hh"
+#include "cpu/simple_thread.hh"
+#include "debug/MinorExecute.hh"
+
+namespace Minor
+{
+
+/* Forward declaration of Execute */
+class Execute;
+
+/** ExecContext bears the exec_context interface for Minor. This nicely
+ * separates that interface from other classes such as Pipeline, MinorCPU
+ * and DynMinorInst and makes it easier to see what state is accessed by it.
+ */
+class ExecContext
+{
+ public:
+ MinorCPU &cpu;
+
+ /** ThreadState object, provides all the architectural state. */
+ SimpleThread &thread;
+
+ /** The execute stage so we can peek at its contents. */
+ Execute &execute;
+
+ /** Instruction for the benefit of memory operations and for PC */
+ MinorDynInstPtr inst;
+
+ ExecContext (
+ MinorCPU &cpu_,
+ SimpleThread &thread_, Execute &execute_,
+ MinorDynInstPtr inst_) :
+ cpu(cpu_),
+ thread(thread_),
+ execute(execute_),
+ inst(inst_)
+ {
+ DPRINTF(MinorExecute, "ExecContext setting PC: %s\n", inst->pc);
+ pcState(inst->pc);
+ setPredicate(true);
+ thread.setIntReg(TheISA::ZeroReg, 0);
+#if THE_ISA == ALPHA_ISA
+ thread.setFloatReg(TheISA::ZeroReg, 0.0);
+#endif
+ }
+
+ Fault
+ readMem(Addr addr, uint8_t *data, unsigned int size,
+ unsigned int flags)
+ {
+ execute.getLSQ().pushRequest(inst, true /* load */, data,
+ size, addr, flags, NULL);
+ return NoFault;
+ }
+
+ Fault
+ writeMem(uint8_t *data, unsigned int size, Addr addr,
+ unsigned int flags, uint64_t *res)
+ {
+ execute.getLSQ().pushRequest(inst, false /* store */, data,
+ size, addr, flags, res);
+ return NoFault;
+ }
+
+ uint64_t
+ readIntRegOperand(const StaticInst *si, int idx)
+ {
+ return thread.readIntReg(si->srcRegIdx(idx));
+ }
+
+ TheISA::FloatReg
+ readFloatRegOperand(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Reg_Base;
+ return thread.readFloatReg(reg_idx);
+ }
+
+ TheISA::FloatRegBits
+ readFloatRegOperandBits(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Reg_Base;
+ return thread.readFloatRegBits(reg_idx);
+ }
+
+ void
+ setIntRegOperand(const StaticInst *si, int idx, uint64_t val)
+ {
+ thread.setIntReg(si->destRegIdx(idx), val);
+ }
+
+ void
+ setFloatRegOperand(const StaticInst *si, int idx,
+ TheISA::FloatReg val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Reg_Base;
+ thread.setFloatReg(reg_idx, val);
+ }
+
+ void
+ setFloatRegOperandBits(const StaticInst *si, int idx,
+ TheISA::FloatRegBits val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::FP_Reg_Base;
+ thread.setFloatRegBits(reg_idx, val);
+ }
+
+ bool
+ readPredicate()
+ {
+ return thread.readPredicate();
+ }
+
+ void
+ setPredicate(bool val)
+ {
+ thread.setPredicate(val);
+ }
+
+ TheISA::PCState
+ pcState()
+ {
+ return thread.pcState();
+ }
+
+ void
+ pcState(const TheISA::PCState &val)
+ {
+ thread.pcState(val);
+ }
+
+ TheISA::MiscReg
+ readMiscRegNoEffect(int misc_reg)
+ {
+ return thread.readMiscRegNoEffect(misc_reg);
+ }
+
+ TheISA::MiscReg
+ readMiscReg(int misc_reg)
+ {
+ return thread.readMiscReg(misc_reg);
+ }
+
+ void
+ setMiscReg(int misc_reg, const TheISA::MiscReg &val)
+ {
+ thread.setMiscReg(misc_reg, val);
+ }
+
+ TheISA::MiscReg
+ readMiscRegOperand(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::Misc_Reg_Base;
+ return thread.readMiscReg(reg_idx);
+ }
+
+ void
+ setMiscRegOperand(const StaticInst *si, int idx,
+ const TheISA::MiscReg &val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::Misc_Reg_Base;
+ return thread.setMiscReg(reg_idx, val);
+ }
+
+ Fault
+ hwrei()
+ {
+#if THE_ISA == ALPHA_ISA
+ return thread.hwrei();
+#else
+ return NoFault;
+#endif
+ }
+
+ bool
+ simPalCheck(int palFunc)
+ {
+#if THE_ISA == ALPHA_ISA
+ return thread.simPalCheck(palFunc);
+#else
+ return false;
+#endif
+ }
+
+ void
+ syscall(int64_t callnum)
+ {
+ if (FullSystem)
+ panic("Syscall emulation isn't available in FS mode.\n");
+
+ thread.syscall(callnum);
+ }
+
+ ThreadContext *tcBase() { return thread.getTC(); }
+
+ /* @todo, should make stCondFailures persistent somewhere */
+ unsigned int readStCondFailures() { return 0; }
+ unsigned int
+ setStCondFailures(unsigned int st_cond_failures)
+ {
+ return 0;
+ }
+
+ int contextId() { return thread.contextId(); }
+ /* ISA-specific (or at least currently ISA singleton) functions */
+
+ /* X86: TLB twiddling */
+ void
+ demapPage(Addr vaddr, uint64_t asn)
+ {
+ thread.getITBPtr()->demapPage(vaddr, asn);
+ thread.getDTBPtr()->demapPage(vaddr, asn);
+ }
+
+ TheISA::CCReg
+ readCCRegOperand(const StaticInst *si, int idx)
+ {
+ int reg_idx = si->srcRegIdx(idx) - TheISA::CC_Reg_Base;
+ return thread.readCCReg(reg_idx);
+ }
+
+ void
+ setCCRegOperand(const StaticInst *si, int idx, TheISA::CCReg val)
+ {
+ int reg_idx = si->destRegIdx(idx) - TheISA::CC_Reg_Base;
+ thread.setCCReg(reg_idx, val);
+ }
+
+ void
+ demapInstPage(Addr vaddr, uint64_t asn)
+ {
+ thread.getITBPtr()->demapPage(vaddr, asn);
+ }
+
+ void
+ demapDataPage(Addr vaddr, uint64_t asn)
+ {
+ thread.getDTBPtr()->demapPage(vaddr, asn);
+ }
+
+ /* ALPHA/POWER: Effective address storage */
+ void setEA(Addr &ea)
+ {
+ inst->ea = ea;
+ }
+
+ BaseCPU *getCpuPtr() { return &cpu; }
+
+ /* POWER: Effective address storage */
+ Addr getEA()
+ {
+ return inst->ea;
+ }
+
+ /* MIPS: other thread register reading/writing */
+ uint64_t
+ readRegOtherThread(unsigned idx, ThreadID tid = InvalidThreadID)
+ {
+ SimpleThread *other_thread = (tid == InvalidThreadID
+ ? &thread : cpu.threads[tid]);
+
+ if (idx < TheISA::FP_Reg_Base) { /* Integer */
+ return other_thread->readIntReg(idx);
+ } else if (idx < TheISA::Misc_Reg_Base) { /* Float */
+ return other_thread->readFloatRegBits(idx
+ - TheISA::FP_Reg_Base);
+ } else { /* Misc */
+ return other_thread->readMiscReg(idx
+ - TheISA::Misc_Reg_Base);
+ }
+ }
+
+ void
+ setRegOtherThread(unsigned idx, const TheISA::MiscReg &val,
+ ThreadID tid = InvalidThreadID)
+ {
+ SimpleThread *other_thread = (tid == InvalidThreadID
+ ? &thread : cpu.threads[tid]);
+
+ if (idx < TheISA::FP_Reg_Base) { /* Integer */
+ return other_thread->setIntReg(idx, val);
+ } else if (idx < TheISA::Misc_Reg_Base) { /* Float */
+ return other_thread->setFloatRegBits(idx
+ - TheISA::FP_Reg_Base, val);
+ } else { /* Misc */
+ return other_thread->setMiscReg(idx
+ - TheISA::Misc_Reg_Base, val);
+ }
+ }
+};
+
+}
+
+#endif /* __CPU_MINOR_EXEC_CONTEXT_HH__ */
diff --git a/src/cpu/minor/execute.cc b/src/cpu/minor/execute.cc
new file mode 100644
index 000000000..2a009a154
--- /dev/null
+++ b/src/cpu/minor/execute.cc
@@ -0,0 +1,1736 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "arch/locked_mem.hh"
+#include "arch/registers.hh"
+#include "arch/utility.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/exec_context.hh"
+#include "cpu/minor/execute.hh"
+#include "cpu/minor/fetch1.hh"
+#include "cpu/minor/lsq.hh"
+#include "cpu/op_class.hh"
+#include "debug/Activity.hh"
+#include "debug/Branch.hh"
+#include "debug/Drain.hh"
+#include "debug/MinorExecute.hh"
+#include "debug/MinorInterrupt.hh"
+#include "debug/MinorMem.hh"
+#include "debug/MinorTrace.hh"
+#include "debug/PCEvent.hh"
+
+namespace Minor
+{
+
+Execute::Execute(const std::string &name_,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardInstData>::Output inp_,
+ Latch<BranchData>::Input out_) :
+ Named(name_),
+ inp(inp_),
+ out(out_),
+ cpu(cpu_),
+ issueLimit(params.executeIssueLimit),
+ memoryIssueLimit(params.executeMemoryIssueLimit),
+ commitLimit(params.executeCommitLimit),
+ memoryCommitLimit(params.executeMemoryCommitLimit),
+ processMoreThanOneInput(params.executeCycleInput),
+ fuDescriptions(*params.executeFuncUnits),
+ numFuncUnits(fuDescriptions.funcUnits.size()),
+ setTraceTimeOnCommit(params.executeSetTraceTimeOnCommit),
+ setTraceTimeOnIssue(params.executeSetTraceTimeOnIssue),
+ allowEarlyMemIssue(params.executeAllowEarlyMemoryIssue),
+ noCostFUIndex(fuDescriptions.funcUnits.size() + 1),
+ lsq(name_ + ".lsq", name_ + ".dcache_port",
+ cpu_, *this,
+ params.executeMaxAccessesInMemory,
+ params.executeMemoryWidth,
+ params.executeLSQRequestsQueueSize,
+ params.executeLSQTransfersQueueSize,
+ params.executeLSQStoreBufferSize,
+ params.executeLSQMaxStoreBufferStoresPerCycle),
+ scoreboard(name_ + ".scoreboard"),
+ inputBuffer(name_ + ".inputBuffer", "insts",
+ params.executeInputBufferSize),
+ inputIndex(0),
+ lastCommitWasEndOfMacroop(true),
+ instsBeingCommitted(params.executeCommitLimit),
+ streamSeqNum(InstId::firstStreamSeqNum),
+ lastPredictionSeqNum(InstId::firstPredictionSeqNum),
+ drainState(NotDraining)
+{
+ if (commitLimit < 1) {
+ fatal("%s: executeCommitLimit must be >= 1 (%d)\n", name_,
+ commitLimit);
+ }
+
+ if (issueLimit < 1) {
+ fatal("%s: executeCommitLimit must be >= 1 (%d)\n", name_,
+ issueLimit);
+ }
+
+ if (memoryIssueLimit < 1) {
+ fatal("%s: executeMemoryIssueLimit must be >= 1 (%d)\n", name_,
+ memoryIssueLimit);
+ }
+
+ if (memoryCommitLimit > commitLimit) {
+ fatal("%s: executeMemoryCommitLimit (%d) must be <="
+ " executeCommitLimit (%d)\n",
+ name_, memoryCommitLimit, commitLimit);
+ }
+
+ if (params.executeInputBufferSize < 1) {
+ fatal("%s: executeInputBufferSize must be >= 1 (%d)\n", name_,
+ params.executeInputBufferSize);
+ }
+
+ if (params.executeInputBufferSize < 1) {
+ fatal("%s: executeInputBufferSize must be >= 1 (%d)\n", name_,
+ params.executeInputBufferSize);
+ }
+
+ /* This should be large enough to count all the in-FU instructions
+ * which need to be accounted for in the inFlightInsts
+ * queue */
+ unsigned int total_slots = 0;
+
+ /* Make FUPipelines for each MinorFU */
+ for (unsigned int i = 0; i < numFuncUnits; i++) {
+ std::ostringstream fu_name;
+ MinorFU *fu_description = fuDescriptions.funcUnits[i];
+
+ /* Note the total number of instruction slots (for sizing
+ * the inFlightInst queue) and the maximum latency of any FU
+ * (for sizing the activity recorder) */
+ total_slots += fu_description->opLat;
+
+ fu_name << name_ << ".fu." << i;
+
+ FUPipeline *fu = new FUPipeline(fu_name.str(), *fu_description, cpu);
+
+ funcUnits.push_back(fu);
+ }
+
+ /** Check that there is a functional unit for all operation classes */
+ for (int op_class = No_OpClass + 1; op_class < Num_OpClass; op_class++) {
+ bool found_fu = false;
+ unsigned int fu_index = 0;
+
+ while (fu_index < numFuncUnits && !found_fu)
+ {
+ if (funcUnits[fu_index]->provides(
+ static_cast<OpClass>(op_class)))
+ {
+ found_fu = true;
+ }
+ fu_index++;
+ }
+
+ if (!found_fu) {
+ warn("No functional unit for OpClass %s\n",
+ Enums::OpClassStrings[op_class]);
+ }
+ }
+
+ inFlightInsts = new Queue<QueuedInst,
+ ReportTraitsAdaptor<QueuedInst> >(
+ name_ + ".inFlightInsts", "insts", total_slots);
+
+ inFUMemInsts = new Queue<QueuedInst,
+ ReportTraitsAdaptor<QueuedInst> >(
+ name_ + ".inFUMemInsts", "insts", total_slots);
+}
+
+const ForwardInstData *
+Execute::getInput()
+{
+ /* Get a line from the inputBuffer to work with */
+ if (!inputBuffer.empty()) {
+ const ForwardInstData &head = inputBuffer.front();
+
+ return (head.isBubble() ? NULL : &(inputBuffer.front()));
+ } else {
+ return NULL;
+ }
+}
+
+void
+Execute::popInput()
+{
+ if (!inputBuffer.empty())
+ inputBuffer.pop();
+
+ inputIndex = 0;
+}
+
+void
+Execute::tryToBranch(MinorDynInstPtr inst, Fault fault, BranchData &branch)
+{
+ ThreadContext *thread = cpu.getContext(inst->id.threadId);
+ const TheISA::PCState &pc_before = inst->pc;
+ TheISA::PCState target = thread->pcState();
+
+ /* Force a branch for SerializeAfter instructions at the end of micro-op
+ * sequence when we're not suspended */
+ bool force_branch = thread->status() != ThreadContext::Suspended &&
+ !inst->isFault() &&
+ inst->isLastOpInInst() &&
+ (inst->staticInst->isSerializeAfter() ||
+ inst->staticInst->isIprAccess());
+
+ DPRINTF(Branch, "tryToBranch before: %s after: %s%s\n",
+ pc_before, target, (force_branch ? " (forcing)" : ""));
+
+ /* Will we change the PC to something other than the next instruction? */
+ bool must_branch = pc_before != target ||
+ fault != NoFault ||
+ force_branch;
+
+ /* The reason for the branch data we're about to generate, set below */
+ BranchData::Reason reason = BranchData::NoBranch;
+
+ if (fault == NoFault)
+ {
+ TheISA::advancePC(target, inst->staticInst);
+ thread->pcState(target);
+
+ DPRINTF(Branch, "Advancing current PC from: %s to: %s\n",
+ pc_before, target);
+ }
+
+ if (inst->predictedTaken && !force_branch) {
+ /* Predicted to branch */
+ if (!must_branch) {
+ /* No branch was taken, change stream to get us back to the
+ * intended PC value */
+ DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x but"
+ " none happened inst: %s\n",
+ inst->pc.instAddr(), inst->predictedTarget.instAddr(), *inst);
+
+ reason = BranchData::BadlyPredictedBranch;
+ } else if (inst->predictedTarget == target) {
+ /* Branch prediction got the right target, kill the branch and
+ * carry on.
+ * Note that this information to the branch predictor might get
+ * overwritten by a "real" branch during this cycle */
+ DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x correctly"
+ " inst: %s\n",
+ inst->pc.instAddr(), inst->predictedTarget.instAddr(), *inst);
+
+ reason = BranchData::CorrectlyPredictedBranch;
+ } else {
+ /* Branch prediction got the wrong target */
+ DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x"
+ " but got the wrong target (actual: 0x%x) inst: %s\n",
+ inst->pc.instAddr(), inst->predictedTarget.instAddr(),
+ target.instAddr() *inst);
+
+ reason = BranchData::BadlyPredictedBranchTarget;
+ }
+ } else if (must_branch) {
+ /* Unpredicted branch */
+ DPRINTF(Branch, "Unpredicted branch from 0x%x to 0x%x inst: %s\n",
+ inst->pc.instAddr(), target.instAddr(), *inst);
+
+ reason = BranchData::UnpredictedBranch;
+ } else {
+ /* No branch at all */
+ reason = BranchData::NoBranch;
+ }
+
+ updateBranchData(reason, inst, target, branch);
+}
+
+void
+Execute::updateBranchData(
+ BranchData::Reason reason,
+ MinorDynInstPtr inst, const TheISA::PCState &target,
+ BranchData &branch)
+{
+ if (reason != BranchData::NoBranch) {
+ /* Bump up the stream sequence number on a real branch*/
+ if (BranchData::isStreamChange(reason))
+ streamSeqNum++;
+
+ /* Branches (even mis-predictions) don't change the predictionSeqNum,
+ * just the streamSeqNum */
+ branch = BranchData(reason, streamSeqNum,
+ /* Maintaining predictionSeqNum if there's no inst is just a
+ * courtesy and looks better on minorview */
+ (inst->isBubble() ? lastPredictionSeqNum
+ : inst->id.predictionSeqNum),
+ target, inst);
+
+ DPRINTF(Branch, "Branch data signalled: %s\n", branch);
+ }
+}
+
+void
+Execute::handleMemResponse(MinorDynInstPtr inst,
+ LSQ::LSQRequestPtr response, BranchData &branch, Fault &fault)
+{
+ ThreadID thread_id = inst->id.threadId;
+ ThreadContext *thread = cpu.getContext(thread_id);
+
+ ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
+
+ PacketPtr packet = response->packet;
+
+ bool is_load = inst->staticInst->isLoad();
+ bool is_store = inst->staticInst->isStore();
+ bool is_prefetch = inst->staticInst->isDataPrefetch();
+
+ /* If true, the trace's predicate value will be taken from the exec
+ * context predicate, otherwise, it will be set to false */
+ bool use_context_predicate = true;
+
+ if (response->fault != NoFault) {
+ /* Invoke memory faults. */
+ DPRINTF(MinorMem, "Completing fault from DTLB access: %s\n",
+ response->fault->name());
+
+ if (inst->staticInst->isPrefetch()) {
+ DPRINTF(MinorMem, "Not taking fault on prefetch: %s\n",
+ response->fault->name());
+
+ /* Don't assign to fault */
+ } else {
+ /* Take the fault raised during the TLB/memory access */
+ fault = response->fault;
+
+ fault->invoke(thread, inst->staticInst);
+ }
+ } else if (!packet) {
+ DPRINTF(MinorMem, "Completing failed request inst: %s\n",
+ *inst);
+ use_context_predicate = false;
+ } else if (packet->isError()) {
+ DPRINTF(MinorMem, "Trying to commit error response: %s\n",
+ *inst);
+
+ fatal("Received error response packet for inst: %s\n", *inst);
+ } else if (is_store || is_load || is_prefetch) {
+ assert(packet);
+
+ DPRINTF(MinorMem, "Memory response inst: %s addr: 0x%x size: %d\n",
+ *inst, packet->getAddr(), packet->getSize());
+
+ if (is_load && packet->getSize() > 0) {
+ DPRINTF(MinorMem, "Memory data[0]: 0x%x\n",
+ static_cast<unsigned int>(packet->getPtr<uint8_t>()[0]));
+ }
+
+ /* Complete the memory access instruction */
+ fault = inst->staticInst->completeAcc(packet, &context,
+ inst->traceData);
+
+ if (fault != NoFault) {
+ /* Invoke fault created by instruction completion */
+ DPRINTF(MinorMem, "Fault in memory completeAcc: %s\n",
+ fault->name());
+ fault->invoke(thread, inst->staticInst);
+ } else {
+ /* Stores need to be pushed into the store buffer to finish
+ * them off */
+ if (response->needsToBeSentToStoreBuffer())
+ lsq.sendStoreToStoreBuffer(response);
+ }
+ } else {
+ fatal("There should only ever be reads, "
+ "writes or faults at this point\n");
+ }
+
+ lsq.popResponse(response);
+
+ if (inst->traceData) {
+ inst->traceData->setPredicate((use_context_predicate ?
+ context.readPredicate() : false));
+ }
+
+ doInstCommitAccounting(inst);
+
+ /* Generate output to account for branches */
+ tryToBranch(inst, fault, branch);
+}
+
+bool
+Execute::isInterrupted(ThreadID thread_id) const
+{
+ return cpu.checkInterrupts(cpu.getContext(thread_id));
+}
+
+bool
+Execute::takeInterrupt(ThreadID thread_id, BranchData &branch)
+{
+ DPRINTF(MinorInterrupt, "Considering interrupt status from PC: %s\n",
+ cpu.getContext(thread_id)->pcState());
+
+ Fault interrupt = cpu.getInterruptController()->getInterrupt
+ (cpu.getContext(thread_id));
+
+ if (interrupt != NoFault) {
+ /* The interrupt *must* set pcState */
+ cpu.getInterruptController()->updateIntrInfo
+ (cpu.getContext(thread_id));
+ interrupt->invoke(cpu.getContext(thread_id));
+
+ assert(!lsq.accessesInFlight());
+
+ DPRINTF(MinorInterrupt, "Invoking interrupt: %s to PC: %s\n",
+ interrupt->name(), cpu.getContext(thread_id)->pcState());
+
+ /* Assume that an interrupt *must* cause a branch. Assert this? */
+
+ updateBranchData(BranchData::Interrupt, MinorDynInst::bubble(),
+ cpu.getContext(thread_id)->pcState(), branch);
+ }
+
+ return interrupt != NoFault;
+}
+
+bool
+Execute::executeMemRefInst(MinorDynInstPtr inst, BranchData &branch,
+ bool &passed_predicate, Fault &fault)
+{
+ bool issued = false;
+
+ /* Set to true if the mem op. is issued and sent to the mem system */
+ passed_predicate = false;
+
+ if (!lsq.canRequest()) {
+ /* Not acting on instruction yet as the memory
+ * queues are full */
+ issued = false;
+ } else {
+ ThreadContext *thread = cpu.getContext(inst->id.threadId);
+ TheISA::PCState old_pc = thread->pcState();
+
+ ExecContext context(cpu, *cpu.threads[inst->id.threadId],
+ *this, inst);
+
+ DPRINTF(MinorExecute, "Initiating memRef inst: %s\n", *inst);
+
+ Fault init_fault = inst->staticInst->initiateAcc(&context,
+ inst->traceData);
+
+ if (init_fault != NoFault) {
+ DPRINTF(MinorExecute, "Fault on memory inst: %s"
+ " initiateAcc: %s\n", *inst, init_fault->name());
+ fault = init_fault;
+ } else {
+ /* Only set this if the instruction passed its
+ * predicate */
+ passed_predicate = context.readPredicate();
+
+ /* Set predicate in tracing */
+ if (inst->traceData)
+ inst->traceData->setPredicate(passed_predicate);
+
+ /* If the instruction didn't pass its predicate (and so will not
+ * progress from here) Try to branch to correct and branch
+ * mis-prediction. */
+ if (!passed_predicate) {
+ /* Leave it up to commit to handle the fault */
+ lsq.pushFailedRequest(inst);
+ }
+ }
+
+ /* Restore thread PC */
+ thread->pcState(old_pc);
+ issued = true;
+ }
+
+ return issued;
+}
+
+/** Increment a cyclic buffer index for indices [0, cycle_size-1] */
+inline unsigned int
+cyclicIndexInc(unsigned int index, unsigned int cycle_size)
+{
+ unsigned int ret = index + 1;
+
+ if (ret == cycle_size)
+ ret = 0;
+
+ return ret;
+}
+
+/** Decrement a cyclic buffer index for indices [0, cycle_size-1] */
+inline unsigned int
+cyclicIndexDec(unsigned int index, unsigned int cycle_size)
+{
+ int ret = index - 1;
+
+ if (ret < 0)
+ ret = cycle_size - 1;
+
+ return ret;
+}
+
+unsigned int
+Execute::issue(bool only_issue_microops)
+{
+ const ForwardInstData *insts_in = getInput();
+
+ /* Early termination if we have no instructions */
+ if (!insts_in)
+ return 0;
+
+ /* Start from the first FU */
+ unsigned int fu_index = 0;
+
+ /* Remains true while instructions are still being issued. If any
+ * instruction fails to issue, this is set to false and we exit issue.
+ * This strictly enforces in-order issue. For other issue behaviours,
+ * a more complicated test in the outer while loop below is needed. */
+ bool issued = true;
+
+ /* Number of insts issues this cycle to check for issueLimit */
+ unsigned num_insts_issued = 0;
+
+ /* Number of memory ops issues this cycle to check for memoryIssueLimit */
+ unsigned num_mem_insts_issued = 0;
+
+ /* Number of instructions discarded this cycle in order to enforce a
+ * discardLimit. @todo, add that parameter? */
+ unsigned num_insts_discarded = 0;
+
+ do {
+ MinorDynInstPtr inst = insts_in->insts[inputIndex];
+ ThreadID thread_id = inst->id.threadId;
+ Fault fault = inst->fault;
+ bool discarded = false;
+ bool issued_mem_ref = false;
+
+ if (inst->isBubble()) {
+ /* Skip */
+ issued = true;
+ } else if (cpu.getContext(thread_id)->status() ==
+ ThreadContext::Suspended)
+ {
+ DPRINTF(MinorExecute, "Not issuing inst: %s from suspended"
+ " thread\n", *inst);
+
+ issued = false;
+ } else if (inst->id.streamSeqNum != streamSeqNum) {
+ DPRINTF(MinorExecute, "Discarding inst: %s as its stream"
+ " state was unexpected, expected: %d\n",
+ *inst, streamSeqNum);
+ issued = true;
+ discarded = true;
+ } else if (fault == NoFault && only_issue_microops &&
+ /* Is this anything other than a non-first microop */
+ (!inst->staticInst->isMicroop() ||
+ !inst->staticInst->isFirstMicroop()))
+ {
+ DPRINTF(MinorExecute, "Not issuing new non-microop inst: %s\n",
+ *inst);
+
+ issued = false;
+ } else {
+ /* Try and issue an instruction into an FU, assume we didn't and
+ * fix that in the loop */
+ issued = false;
+
+ /* Try FU from 0 each instruction */
+ fu_index = 0;
+
+ /* Try and issue a single instruction stepping through the
+ * available FUs */
+ do {
+ FUPipeline *fu = funcUnits[fu_index];
+
+ DPRINTF(MinorExecute, "Trying to issue inst: %s to FU: %d\n",
+ *inst, fu_index);
+
+ /* Does the examined fu have the OpClass-related capability
+ * needed to execute this instruction? Faults can always
+ * issue to any FU but probably should just 'live' in the
+ * inFlightInsts queue rather than having an FU. */
+ bool fu_is_capable = (!inst->isFault() ?
+ fu->provides(inst->staticInst->opClass()) : true);
+
+ if (inst->isNoCostInst()) {
+ /* Issue free insts. to a fake numbered FU */
+ fu_index = noCostFUIndex;
+
+ /* And start the countdown on activity to allow
+ * this instruction to get to the end of its FU */
+ cpu.activityRecorder->activity();
+
+ /* Mark the destinations for this instruction as
+ * busy */
+ scoreboard.markupInstDests(inst, cpu.curCycle() +
+ Cycles(0), cpu.getContext(thread_id), false);
+
+ inst->fuIndex = noCostFUIndex;
+ inst->extraCommitDelay = Cycles(0);
+ inst->extraCommitDelayExpr = NULL;
+
+ /* Push the instruction onto the inFlight queue so
+ * it can be committed in order */
+ QueuedInst fu_inst(inst);
+ inFlightInsts->push(fu_inst);
+
+ issued = true;
+
+ } else if (!fu_is_capable || fu->alreadyPushed()) {
+ /* Skip */
+ if (!fu_is_capable) {
+ DPRINTF(MinorExecute, "Can't issue as FU: %d isn't"
+ " capable\n", fu_index);
+ } else {
+ DPRINTF(MinorExecute, "Can't issue as FU: %d is"
+ " already busy\n", fu_index);
+ }
+ } else if (fu->stalled) {
+ DPRINTF(MinorExecute, "Can't issue inst: %s into FU: %d,"
+ " it's stalled\n",
+ *inst, fu_index);
+ } else if (!fu->canInsert()) {
+ DPRINTF(MinorExecute, "Can't issue inst: %s to busy FU"
+ " for another: %d cycles\n",
+ *inst, fu->cyclesBeforeInsert());
+ } else {
+ MinorFUTiming *timing = (!inst->isFault() ?
+ fu->findTiming(inst->staticInst) : NULL);
+
+ const std::vector<Cycles> *src_latencies =
+ (timing ? &(timing->srcRegsRelativeLats)
+ : NULL);
+
+ const std::vector<bool> *cant_forward_from_fu_indices =
+ &(fu->cantForwardFromFUIndices);
+
+ if (timing && timing->suppress) {
+ DPRINTF(MinorExecute, "Can't issue inst: %s as extra"
+ " decoding is suppressing it\n",
+ *inst);
+ } else if (!scoreboard.canInstIssue(inst, src_latencies,
+ cant_forward_from_fu_indices,
+ cpu.curCycle(), cpu.getContext(thread_id)))
+ {
+ DPRINTF(MinorExecute, "Can't issue inst: %s yet\n",
+ *inst);
+ } else {
+ /* Can insert the instruction into this FU */
+ DPRINTF(MinorExecute, "Issuing inst: %s"
+ " into FU %d\n", *inst,
+ fu_index);
+
+ Cycles extra_dest_retire_lat = Cycles(0);
+ TimingExpr *extra_dest_retire_lat_expr = NULL;
+ Cycles extra_assumed_lat = Cycles(0);
+
+ /* Add the extraCommitDelay and extraAssumeLat to
+ * the FU pipeline timings */
+ if (timing) {
+ extra_dest_retire_lat =
+ timing->extraCommitLat;
+ extra_dest_retire_lat_expr =
+ timing->extraCommitLatExpr;
+ extra_assumed_lat =
+ timing->extraAssumedLat;
+ }
+
+ bool issued_mem_ref = inst->isMemRef();
+
+ QueuedInst fu_inst(inst);
+
+ /* Decorate the inst with FU details */
+ inst->fuIndex = fu_index;
+ inst->extraCommitDelay = extra_dest_retire_lat;
+ inst->extraCommitDelayExpr =
+ extra_dest_retire_lat_expr;
+
+ if (issued_mem_ref) {
+ /* Remember which instruction this memory op
+ * depends on so that initiateAcc can be called
+ * early */
+ if (allowEarlyMemIssue) {
+ inst->instToWaitFor =
+ scoreboard.execSeqNumToWaitFor(inst,
+ cpu.getContext(thread_id));
+
+ if (lsq.getLastMemBarrier() >
+ inst->instToWaitFor)
+ {
+ DPRINTF(MinorExecute, "A barrier will"
+ " cause a delay in mem ref issue of"
+ " inst: %s until after inst"
+ " %d(exec)\n", *inst,
+ lsq.getLastMemBarrier());
+
+ inst->instToWaitFor =
+ lsq.getLastMemBarrier();
+ } else {
+ DPRINTF(MinorExecute, "Memory ref inst:"
+ " %s must wait for inst %d(exec)"
+ " before issuing\n",
+ *inst, inst->instToWaitFor);
+ }
+
+ inst->canEarlyIssue = true;
+ }
+ /* Also queue this instruction in the memory ref
+ * queue to ensure in-order issue to the LSQ */
+ DPRINTF(MinorExecute, "Pushing mem inst: %s\n",
+ *inst);
+ inFUMemInsts->push(fu_inst);
+ }
+
+ /* Issue to FU */
+ fu->push(fu_inst);
+ /* And start the countdown on activity to allow
+ * this instruction to get to the end of its FU */
+ cpu.activityRecorder->activity();
+
+ /* Mark the destinations for this instruction as
+ * busy */
+ scoreboard.markupInstDests(inst, cpu.curCycle() +
+ fu->description.opLat +
+ extra_dest_retire_lat +
+ extra_assumed_lat,
+ cpu.getContext(thread_id),
+ issued_mem_ref && extra_assumed_lat == Cycles(0));
+
+ /* Push the instruction onto the inFlight queue so
+ * it can be committed in order */
+ inFlightInsts->push(fu_inst);
+
+ issued = true;
+ }
+ }
+
+ fu_index++;
+ } while (fu_index != numFuncUnits && !issued);
+
+ if (!issued)
+ DPRINTF(MinorExecute, "Didn't issue inst: %s\n", *inst);
+ }
+
+ if (issued) {
+ /* Generate MinorTrace's MinorInst lines. Do this at commit
+ * to allow better instruction annotation? */
+ if (DTRACE(MinorTrace) && !inst->isBubble())
+ inst->minorTraceInst(*this);
+
+ /* Mark up barriers in the LSQ */
+ if (!discarded && inst->isInst() &&
+ inst->staticInst->isMemBarrier())
+ {
+ DPRINTF(MinorMem, "Issuing memory barrier inst: %s\n", *inst);
+ lsq.issuedMemBarrierInst(inst);
+ }
+
+ if (inst->traceData && setTraceTimeOnIssue) {
+ inst->traceData->setWhen(curTick());
+ }
+
+ if (issued_mem_ref)
+ num_mem_insts_issued++;
+
+ if (discarded) {
+ num_insts_discarded++;
+ } else {
+ num_insts_issued++;
+
+ if (num_insts_issued == issueLimit)
+ DPRINTF(MinorExecute, "Reached inst issue limit\n");
+ }
+
+ inputIndex++;
+ DPRINTF(MinorExecute, "Stepping to next inst inputIndex: %d\n",
+ inputIndex);
+ }
+
+ /* Got to the end of a line */
+ if (inputIndex == insts_in->width()) {
+ popInput();
+ /* Set insts_in to null to force us to leave the surrounding
+ * loop */
+ insts_in = NULL;
+
+ if (processMoreThanOneInput) {
+ DPRINTF(MinorExecute, "Wrapping\n");
+ insts_in = getInput();
+ }
+ }
+ } while (insts_in && inputIndex < insts_in->width() &&
+ /* We still have instructions */
+ fu_index != numFuncUnits && /* Not visited all FUs */
+ issued && /* We've not yet failed to issue an instruction */
+ num_insts_issued != issueLimit && /* Still allowed to issue */
+ num_mem_insts_issued != memoryIssueLimit);
+
+ return num_insts_issued;
+}
+
+bool
+Execute::tryPCEvents()
+{
+ ThreadContext *thread = cpu.getContext(0);
+ unsigned int num_pc_event_checks = 0;
+
+ /* Handle PC events on instructions */
+ Addr oldPC;
+ do {
+ oldPC = thread->instAddr();
+ cpu.system->pcEventQueue.service(thread);
+ num_pc_event_checks++;
+ } while (oldPC != thread->instAddr());
+
+ if (num_pc_event_checks > 1) {
+ DPRINTF(PCEvent, "Acting on PC Event to PC: %s\n",
+ thread->pcState());
+ }
+
+ return num_pc_event_checks > 1;
+}
+
+void
+Execute::doInstCommitAccounting(MinorDynInstPtr inst)
+{
+ assert(!inst->isFault());
+
+ MinorThread *thread = cpu.threads[inst->id.threadId];
+
+ /* Increment the many and various inst and op counts in the
+ * thread and system */
+ if (!inst->staticInst->isMicroop() || inst->staticInst->isLastMicroop())
+ {
+ thread->numInst++;
+ thread->numInsts++;
+ cpu.stats.numInsts++;
+ }
+ thread->numOp++;
+ thread->numOps++;
+ cpu.stats.numOps++;
+ cpu.system->totalNumInsts++;
+
+ /* Act on events related to instruction counts */
+ cpu.comInstEventQueue[inst->id.threadId]->serviceEvents(thread->numInst);
+ cpu.system->instEventQueue.serviceEvents(cpu.system->totalNumInsts);
+
+ /* Set the CP SeqNum to the numOps commit number */
+ if (inst->traceData)
+ inst->traceData->setCPSeq(thread->numOp);
+}
+
+bool
+Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue,
+ BranchData &branch, Fault &fault, bool &committed,
+ bool &completed_mem_issue)
+{
+ ThreadID thread_id = inst->id.threadId;
+ ThreadContext *thread = cpu.getContext(thread_id);
+
+ bool completed_inst = true;
+ fault = NoFault;
+
+ /* Is the thread for this instruction suspended? In that case, just
+ * stall as long as there are no pending interrupts */
+ if (thread->status() == ThreadContext::Suspended &&
+ !isInterrupted(thread_id))
+ {
+ DPRINTF(MinorExecute, "Not committing inst from suspended thread"
+ " inst: %s\n", *inst);
+ completed_inst = false;
+ } else if (inst->isFault()) {
+ ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
+
+ DPRINTF(MinorExecute, "Fault inst reached Execute: %s\n",
+ inst->fault->name());
+
+ fault = inst->fault;
+ inst->fault->invoke(thread, NULL);
+
+ tryToBranch(inst, fault, branch);
+ } else if (inst->staticInst->isMemRef()) {
+ /* Memory accesses are executed in two parts:
+ * executeMemRefInst -- calculates the EA and issues the access
+ * to memory. This is done here.
+ * handleMemResponse -- handles the response packet, done by
+ * Execute::commit
+ *
+ * While the memory access is in its FU, the EA is being
+ * calculated. At the end of the FU, when it is ready to
+ * 'commit' (in this function), the access is presented to the
+ * memory queues. When a response comes back from memory,
+ * Execute::commit will commit it.
+ */
+ bool predicate_passed = false;
+ bool completed_mem_inst = executeMemRefInst(inst, branch,
+ predicate_passed, fault);
+
+ if (completed_mem_inst && fault != NoFault) {
+ if (early_memory_issue) {
+ DPRINTF(MinorExecute, "Fault in early executing inst: %s\n",
+ fault->name());
+ /* Don't execute the fault, just stall the instruction
+ * until it gets to the head of inFlightInsts */
+ inst->canEarlyIssue = false;
+ /* Not completed as we'll come here again to pick up
+ * the fault when we get to the end of the FU */
+ completed_inst = false;
+ } else {
+ DPRINTF(MinorExecute, "Fault in execute: %s\n",
+ fault->name());
+ fault->invoke(thread, NULL);
+
+ tryToBranch(inst, fault, branch);
+ completed_inst = true;
+ }
+ } else {
+ completed_inst = completed_mem_inst;
+ }
+ completed_mem_issue = completed_inst;
+ } else if (inst->isInst() && inst->staticInst->isMemBarrier() &&
+ !lsq.canPushIntoStoreBuffer())
+ {
+ DPRINTF(MinorExecute, "Can't commit data barrier inst: %s yet as"
+ " there isn't space in the store buffer\n", *inst);
+
+ completed_inst = false;
+ } else {
+ ExecContext context(cpu, *cpu.threads[thread_id], *this, inst);
+
+ DPRINTF(MinorExecute, "Committing inst: %s\n", *inst);
+
+ fault = inst->staticInst->execute(&context,
+ inst->traceData);
+
+ /* Set the predicate for tracing and dump */
+ if (inst->traceData)
+ inst->traceData->setPredicate(context.readPredicate());
+
+ committed = true;
+
+ if (fault != NoFault) {
+ DPRINTF(MinorExecute, "Fault in execute of inst: %s fault: %s\n",
+ *inst, fault->name());
+ fault->invoke(thread, inst->staticInst);
+ }
+
+ doInstCommitAccounting(inst);
+ tryToBranch(inst, fault, branch);
+ }
+
+ if (completed_inst) {
+ /* Keep a copy of this instruction's predictionSeqNum just in case
+ * we need to issue a branch without an instruction (such as an
+ * interrupt) */
+ lastPredictionSeqNum = inst->id.predictionSeqNum;
+
+ /* Check to see if this instruction suspended the current thread. */
+ if (!inst->isFault() &&
+ thread->status() == ThreadContext::Suspended &&
+ branch.isBubble() && /* It didn't branch too */
+ !isInterrupted(thread_id)) /* Don't suspend if we have
+ interrupts */
+ {
+ TheISA::PCState resume_pc = cpu.getContext(0)->pcState();
+
+ assert(resume_pc.microPC() == 0);
+
+ DPRINTF(MinorInterrupt, "Suspending thread: %d from Execute"
+ " inst: %s\n", inst->id.threadId, *inst);
+
+ cpu.stats.numFetchSuspends++;
+
+ updateBranchData(BranchData::SuspendThread, inst, resume_pc,
+ branch);
+ }
+ }
+
+ return completed_inst;
+}
+
+void
+Execute::commit(bool only_commit_microops, bool discard, BranchData &branch)
+{
+ Fault fault = NoFault;
+ Cycles now = cpu.curCycle();
+
+ /**
+ * Try and execute as many instructions from the end of FU pipelines as
+ * possible. This *doesn't* include actually advancing the pipelines.
+ *
+ * We do this by looping on the front of the inFlightInsts queue for as
+ * long as we can find the desired instruction at the end of the
+ * functional unit it was issued to without seeing a branch or a fault.
+ * In this function, these terms are used:
+ * complete -- The instruction has finished its passage through
+ * its functional unit and its fate has been decided
+ * (committed, discarded, issued to the memory system)
+ * commit -- The instruction is complete(d), not discarded and has
+ * its effects applied to the CPU state
+ * discard(ed) -- The instruction is complete but not committed
+ * as its streamSeqNum disagrees with the current
+ * Execute::streamSeqNum
+ *
+ * Commits are also possible from two other places:
+ *
+ * 1) Responses returning from the LSQ
+ * 2) Mem ops issued to the LSQ ('committed' from the FUs) earlier
+ * than their position in the inFlightInsts queue, but after all
+ * their dependencies are resolved.
+ */
+
+ /* Has an instruction been completed? Once this becomes false, we stop
+ * trying to complete instructions. */
+ bool completed_inst = true;
+
+ /* Number of insts committed this cycle to check against commitLimit */
+ unsigned int num_insts_committed = 0;
+
+ /* Number of memory access instructions committed to check against
+ * memCommitLimit */
+ unsigned int num_mem_refs_committed = 0;
+
+ if (only_commit_microops && !inFlightInsts->empty()) {
+ DPRINTF(MinorInterrupt, "Only commit microops %s %d\n",
+ *(inFlightInsts->front().inst),
+ lastCommitWasEndOfMacroop);
+ }
+
+ while (!inFlightInsts->empty() && /* Some more instructions to process */
+ !branch.isStreamChange() && /* No real branch */
+ fault == NoFault && /* No faults */
+ completed_inst && /* Still finding instructions to execute */
+ num_insts_committed != commitLimit /* Not reached commit limit */
+ )
+ {
+ if (only_commit_microops) {
+ DPRINTF(MinorInterrupt, "Committing tail of insts before"
+ " interrupt: %s\n",
+ *(inFlightInsts->front().inst));
+ }
+
+ QueuedInst *head_inflight_inst = &(inFlightInsts->front());
+
+ InstSeqNum head_exec_seq_num =
+ head_inflight_inst->inst->id.execSeqNum;
+
+ /* The instruction we actually process if completed_inst
+ * remains true to the end of the loop body.
+ * Start by considering the the head of the in flight insts queue */
+ MinorDynInstPtr inst = head_inflight_inst->inst;
+
+ bool committed_inst = false;
+ bool discard_inst = false;
+ bool completed_mem_ref = false;
+ bool issued_mem_ref = false;
+ bool early_memory_issue = false;
+
+ /* Must set this again to go around the loop */
+ completed_inst = false;
+
+ /* If we're just completing a macroop before an interrupt or drain,
+ * can we stil commit another microop (rather than a memory response)
+ * without crosing into the next full instruction? */
+ bool can_commit_insts = !inFlightInsts->empty() &&
+ !(only_commit_microops && lastCommitWasEndOfMacroop);
+
+ /* Can we find a mem response for this inst */
+ LSQ::LSQRequestPtr mem_response =
+ (inst->inLSQ ? lsq.findResponse(inst) : NULL);
+
+ DPRINTF(MinorExecute, "Trying to commit canCommitInsts: %d\n",
+ can_commit_insts);
+
+ /* Test for PC events after every instruction */
+ if (isInbetweenInsts() && tryPCEvents()) {
+ ThreadContext *thread = cpu.getContext(0);
+
+ /* Branch as there was a change in PC */
+ updateBranchData(BranchData::UnpredictedBranch,
+ MinorDynInst::bubble(), thread->pcState(), branch);
+ } else if (mem_response &&
+ num_mem_refs_committed < memoryCommitLimit)
+ {
+ /* Try to commit from the memory responses next */
+ discard_inst = inst->id.streamSeqNum != streamSeqNum ||
+ discard;
+
+ DPRINTF(MinorExecute, "Trying to commit mem response: %s\n",
+ *inst);
+
+ /* Complete or discard the response */
+ if (discard_inst) {
+ DPRINTF(MinorExecute, "Discarding mem inst: %s as its"
+ " stream state was unexpected, expected: %d\n",
+ *inst, streamSeqNum);
+
+ lsq.popResponse(mem_response);
+ } else {
+ handleMemResponse(inst, mem_response, branch, fault);
+ committed_inst = true;
+ }
+
+ completed_mem_ref = true;
+ completed_inst = true;
+ } else if (can_commit_insts) {
+ /* If true, this instruction will, subject to timing tweaks,
+ * be considered for completion. try_to_commit flattens
+ * the `if' tree a bit and allows other tests for inst
+ * commit to be inserted here. */
+ bool try_to_commit = false;
+
+ /* Try and issue memory ops early if they:
+ * - Can push a request into the LSQ
+ * - Have reached the end of their FUs
+ * - Have had all their dependencies satisfied
+ * - Are from the right stream
+ *
+ * For any other case, leave it to the normal instruction
+ * issue below to handle them.
+ */
+ if (!inFUMemInsts->empty() && lsq.canRequest()) {
+ DPRINTF(MinorExecute, "Trying to commit from mem FUs\n");
+
+ const MinorDynInstPtr head_mem_ref_inst =
+ inFUMemInsts->front().inst;
+ FUPipeline *fu = funcUnits[head_mem_ref_inst->fuIndex];
+ const MinorDynInstPtr &fu_inst = fu->front().inst;
+
+ /* Use this, possibly out of order, inst as the one
+ * to 'commit'/send to the LSQ */
+ if (!fu_inst->isBubble() &&
+ !fu_inst->inLSQ &&
+ fu_inst->canEarlyIssue &&
+ streamSeqNum == fu_inst->id.streamSeqNum &&
+ head_exec_seq_num > fu_inst->instToWaitFor)
+ {
+ DPRINTF(MinorExecute, "Issuing mem ref early"
+ " inst: %s instToWaitFor: %d\n",
+ *(fu_inst), fu_inst->instToWaitFor);
+
+ inst = fu_inst;
+ try_to_commit = true;
+ early_memory_issue = true;
+ completed_inst = true;
+ }
+ }
+
+ /* Try and commit FU-less insts */
+ if (!completed_inst && inst->isNoCostInst()) {
+ DPRINTF(MinorExecute, "Committing no cost inst: %s", *inst);
+
+ try_to_commit = true;
+ completed_inst = true;
+ }
+
+ /* Try to issue from the ends of FUs and the inFlightInsts
+ * queue */
+ if (!completed_inst && !inst->inLSQ) {
+ DPRINTF(MinorExecute, "Trying to commit from FUs\n");
+
+ /* Try to commit from a functional unit */
+ /* Is the head inst of the expected inst's FU actually the
+ * expected inst? */
+ QueuedInst &fu_inst =
+ funcUnits[inst->fuIndex]->front();
+ InstSeqNum fu_inst_seq_num = fu_inst.inst->id.execSeqNum;
+
+ if (fu_inst.inst->isBubble()) {
+ /* No instruction ready */
+ completed_inst = false;
+ } else if (fu_inst_seq_num != head_exec_seq_num) {
+ /* Past instruction: we must have already executed it
+ * in the same cycle and so the head inst isn't
+ * actually at the end of its pipeline
+ * Future instruction: handled above and only for
+ * mem refs on their way to the LSQ */
+ } else /* if (fu_inst_seq_num == head_exec_seq_num) */ {
+ /* All instructions can be committed if they have the
+ * right execSeqNum and there are no in-flight
+ * mem insts before us */
+ try_to_commit = true;
+ completed_inst = true;
+ }
+ }
+
+ if (try_to_commit) {
+ discard_inst = inst->id.streamSeqNum != streamSeqNum ||
+ discard;
+
+ /* Is this instruction discardable as its streamSeqNum
+ * doesn't match? */
+ if (!discard_inst) {
+ /* Try to commit or discard a non-memory instruction.
+ * Memory ops are actually 'committed' from this FUs
+ * and 'issued' into the memory system so we need to
+ * account for them later (commit_was_mem_issue gets
+ * set) */
+ if (inst->extraCommitDelayExpr) {
+ DPRINTF(MinorExecute, "Evaluating expression for"
+ " extra commit delay inst: %s\n", *inst);
+
+ ThreadContext *thread =
+ cpu.getContext(inst->id.threadId);
+
+ TimingExprEvalContext context(inst->staticInst,
+ thread, NULL);
+
+ uint64_t extra_delay = inst->extraCommitDelayExpr->
+ eval(context);
+
+ DPRINTF(MinorExecute, "Extra commit delay expr"
+ " result: %d\n", extra_delay);
+
+ if (extra_delay < 128) {
+ inst->extraCommitDelay += Cycles(extra_delay);
+ } else {
+ DPRINTF(MinorExecute, "Extra commit delay was"
+ " very long: %d\n", extra_delay);
+ }
+ inst->extraCommitDelayExpr = NULL;
+ }
+
+ /* Move the extraCommitDelay from the instruction
+ * into the minimumCommitCycle */
+ if (inst->extraCommitDelay != Cycles(0)) {
+ inst->minimumCommitCycle = cpu.curCycle() +
+ inst->extraCommitDelay;
+ inst->extraCommitDelay = Cycles(0);
+ }
+
+ /* @todo Think about making lastMemBarrier be
+ * MAX_UINT_64 to avoid using 0 as a marker value */
+ if (!inst->isFault() && inst->isMemRef() &&
+ lsq.getLastMemBarrier() <
+ inst->id.execSeqNum &&
+ lsq.getLastMemBarrier() != 0)
+ {
+ DPRINTF(MinorExecute, "Not committing inst: %s yet"
+ " as there are incomplete barriers in flight\n",
+ *inst);
+ completed_inst = false;
+ } else if (inst->minimumCommitCycle > now) {
+ DPRINTF(MinorExecute, "Not committing inst: %s yet"
+ " as it wants to be stalled for %d more cycles\n",
+ *inst, inst->minimumCommitCycle - now);
+ completed_inst = false;
+ } else {
+ completed_inst = commitInst(inst,
+ early_memory_issue, branch, fault,
+ committed_inst, issued_mem_ref);
+ }
+ } else {
+ /* Discard instruction */
+ completed_inst = true;
+ }
+
+ if (completed_inst) {
+ /* Allow the pipeline to advance. If the FU head
+ * instruction wasn't the inFlightInsts head
+ * but had already been committed, it would have
+ * unstalled the pipeline before here */
+ if (inst->fuIndex != noCostFUIndex)
+ funcUnits[inst->fuIndex]->stalled = false;
+ }
+ }
+ } else {
+ DPRINTF(MinorExecute, "No instructions to commit\n");
+ completed_inst = false;
+ }
+
+ /* All discardable instructions must also be 'completed' by now */
+ assert(!(discard_inst && !completed_inst));
+
+ /* Instruction committed but was discarded due to streamSeqNum
+ * mismatch */
+ if (discard_inst) {
+ DPRINTF(MinorExecute, "Discarding inst: %s as its stream"
+ " state was unexpected, expected: %d\n",
+ *inst, streamSeqNum);
+
+ if (fault == NoFault)
+ cpu.stats.numDiscardedOps++;
+ }
+
+ /* Mark the mem inst as being in the LSQ */
+ if (issued_mem_ref) {
+ inst->fuIndex = 0;
+ inst->inLSQ = true;
+ }
+
+ /* Pop issued (to LSQ) and discarded mem refs from the inFUMemInsts
+ * as they've *definitely* exited the FUs */
+ if (completed_inst && inst->isMemRef()) {
+ /* The MemRef could have been discarded from the FU or the memory
+ * queue, so just check an FU instruction */
+ if (!inFUMemInsts->empty() &&
+ inFUMemInsts->front().inst == inst)
+ {
+ inFUMemInsts->pop();
+ }
+ }
+
+ if (completed_inst && !(issued_mem_ref && fault == NoFault)) {
+ /* Note that this includes discarded insts */
+ DPRINTF(MinorExecute, "Completed inst: %s\n", *inst);
+
+ /* Got to the end of a full instruction? */
+ lastCommitWasEndOfMacroop = inst->isFault() ||
+ inst->isLastOpInInst();
+
+ /* lastPredictionSeqNum is kept as a convenience to prevent its
+ * value from changing too much on the minorview display */
+ lastPredictionSeqNum = inst->id.predictionSeqNum;
+
+ /* Finished with the inst, remove it from the inst queue and
+ * clear its dependencies */
+ inFlightInsts->pop();
+
+ /* Complete barriers in the LSQ/move to store buffer */
+ if (inst->isInst() && inst->staticInst->isMemBarrier()) {
+ DPRINTF(MinorMem, "Completing memory barrier"
+ " inst: %s committed: %d\n", *inst, committed_inst);
+ lsq.completeMemBarrierInst(inst, committed_inst);
+ }
+
+ scoreboard.clearInstDests(inst, inst->isMemRef());
+ }
+
+ /* Handle per-cycle instruction counting */
+ if (committed_inst) {
+ bool is_no_cost_inst = inst->isNoCostInst();
+
+ /* Don't show no cost instructions as having taken a commit
+ * slot */
+ if (DTRACE(MinorTrace) && !is_no_cost_inst)
+ instsBeingCommitted.insts[num_insts_committed] = inst;
+
+ if (!is_no_cost_inst)
+ num_insts_committed++;
+
+ if (num_insts_committed == commitLimit)
+ DPRINTF(MinorExecute, "Reached inst commit limit\n");
+
+ /* Re-set the time of the instruction if that's required for
+ * tracing */
+ if (inst->traceData) {
+ if (setTraceTimeOnCommit)
+ inst->traceData->setWhen(curTick());
+ inst->traceData->dump();
+ }
+
+ if (completed_mem_ref)
+ num_mem_refs_committed++;
+
+ if (num_mem_refs_committed == memoryCommitLimit)
+ DPRINTF(MinorExecute, "Reached mem ref commit limit\n");
+ }
+ }
+}
+
+bool
+Execute::isInbetweenInsts() const
+{
+ return lastCommitWasEndOfMacroop &&
+ !lsq.accessesInFlight();
+}
+
+void
+Execute::evaluate()
+{
+ inputBuffer.setTail(*inp.outputWire);
+ BranchData &branch = *out.inputWire;
+
+ const ForwardInstData *insts_in = getInput();
+
+ /* Do all the cycle-wise activities for dcachePort here to potentially
+ * free up input spaces in the LSQ's requests queue */
+ lsq.step();
+
+ /* Has an interrupt been signalled? This may not be acted on
+ * straighaway so this is different from took_interrupt below */
+ bool interrupted = false;
+ /* If there was an interrupt signalled, was it acted on now? */
+ bool took_interrupt = false;
+
+ if (cpu.getInterruptController()) {
+ /* This is here because it seems that after drainResume the
+ * interrupt controller isn't always set */
+ interrupted = drainState == NotDraining && isInterrupted(0);
+ } else {
+ DPRINTF(MinorInterrupt, "No interrupt controller\n");
+ }
+
+ unsigned int num_issued = 0;
+
+ if (DTRACE(MinorTrace)) {
+ /* Empty the instsBeingCommitted for MinorTrace */
+ instsBeingCommitted.bubbleFill();
+ }
+
+ /* THREAD threadId on isInterrupted */
+ /* Act on interrupts */
+ if (interrupted && isInbetweenInsts()) {
+ took_interrupt = takeInterrupt(0, branch);
+ /* Clear interrupted if no interrupt was actually waiting */
+ interrupted = took_interrupt;
+ }
+
+ if (took_interrupt) {
+ /* Do no commit/issue this cycle */
+ } else if (!branch.isBubble()) {
+ /* It's important that this is here to carry Fetch1 wakeups to Fetch1
+ * without overwriting them */
+ DPRINTF(MinorInterrupt, "Execute skipping a cycle to allow old"
+ " branch to complete\n");
+ } else {
+ if (interrupted) {
+ if (inFlightInsts->empty()) {
+ DPRINTF(MinorInterrupt, "Waiting but no insts\n");
+ } else {
+ DPRINTF(MinorInterrupt, "Waiting for end of inst before"
+ " signalling interrupt\n");
+ }
+ }
+
+ /* commit can set stalled flags observable to issue and so *must* be
+ * called first */
+ if (drainState != NotDraining) {
+ if (drainState == DrainCurrentInst) {
+ /* Commit only micro-ops, don't kill anything else */
+ commit(true, false, branch);
+
+ if (isInbetweenInsts())
+ setDrainState(DrainHaltFetch);
+
+ /* Discard any generated branch */
+ branch = BranchData::bubble();
+ } else if (drainState == DrainAllInsts) {
+ /* Kill all instructions */
+ while (getInput())
+ popInput();
+ commit(false, true, branch);
+ }
+ } else {
+ /* Commit micro-ops only if interrupted. Otherwise, commit
+ * anything you like */
+ commit(interrupted, false, branch);
+ }
+
+ /* This will issue merrily even when interrupted in the sure and
+ * certain knowledge that the interrupt with change the stream */
+ if (insts_in)
+ num_issued = issue(false);
+ }
+
+ /* Halt fetch, but don't do it until we have the current instruction in
+ * the bag */
+ if (drainState == DrainHaltFetch) {
+ updateBranchData(BranchData::HaltFetch, MinorDynInst::bubble(),
+ TheISA::PCState(0), branch);
+
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+ setDrainState(DrainAllInsts);
+ }
+
+ MinorDynInstPtr next_issuable_inst = NULL;
+ bool can_issue_next = false;
+
+ /* Find the next issuable instruction and see if it can be issued */
+ if (getInput()) {
+ MinorDynInstPtr inst = getInput()->insts[inputIndex];
+
+ if (inst->isFault()) {
+ can_issue_next = true;
+ } else if (!inst->isBubble()) {
+ if (cpu.getContext(inst->id.threadId)->status() !=
+ ThreadContext::Suspended)
+ {
+ next_issuable_inst = inst;
+ }
+ }
+ }
+
+ bool becoming_stalled = true;
+
+ /* Advance the pipelines and note whether they still need to be
+ * advanced */
+ for (unsigned int i = 0; i < numFuncUnits; i++) {
+ FUPipeline *fu = funcUnits[i];
+
+ fu->advance();
+
+ /* If we need to go again, the pipeline will have been left or set
+ * to be unstalled */
+ if (fu->occupancy != 0 && !fu->stalled)
+ becoming_stalled = false;
+
+ /* Could we possibly issue the next instruction? This is quite
+ * an expensive test */
+ if (next_issuable_inst && !fu->stalled &&
+ scoreboard.canInstIssue(next_issuable_inst,
+ NULL, NULL, cpu.curCycle() + Cycles(1),
+ cpu.getContext(next_issuable_inst->id.threadId)) &&
+ fu->provides(next_issuable_inst->staticInst->opClass()))
+ {
+ can_issue_next = true;
+ }
+ }
+
+ bool head_inst_might_commit = false;
+
+ /* Could the head in flight insts be committed */
+ if (!inFlightInsts->empty()) {
+ const QueuedInst &head_inst = inFlightInsts->front();
+
+ if (head_inst.inst->isNoCostInst()) {
+ head_inst_might_commit = true;
+ } else {
+ FUPipeline *fu = funcUnits[head_inst.inst->fuIndex];
+
+ /* Head inst is commitable */
+ if ((fu->stalled &&
+ fu->front().inst->id == head_inst.inst->id) ||
+ lsq.findResponse(head_inst.inst))
+ {
+ head_inst_might_commit = true;
+ }
+ }
+ }
+
+ DPRINTF(Activity, "Need to tick num issued insts: %s%s%s%s%s%s\n",
+ (num_issued != 0 ? " (issued some insts)" : ""),
+ (becoming_stalled ? " (becoming stalled)" : "(not becoming stalled)"),
+ (can_issue_next ? " (can issued next inst)" : ""),
+ (head_inst_might_commit ? "(head inst might commit)" : ""),
+ (lsq.needsToTick() ? " (LSQ needs to tick)" : ""),
+ (interrupted ? " (interrupted)" : ""));
+
+ bool need_to_tick =
+ num_issued != 0 || /* Issued some insts this cycle */
+ !becoming_stalled || /* Some FU pipelines can still move */
+ can_issue_next || /* Can still issue a new inst */
+ head_inst_might_commit || /* Could possible commit the next inst */
+ lsq.needsToTick() || /* Must step the dcache port */
+ interrupted; /* There are pending interrupts */
+
+ if (!need_to_tick) {
+ DPRINTF(Activity, "The next cycle might be skippable as there are no"
+ " advanceable FUs\n");
+ }
+
+ /* Wake up if we need to tick again */
+ if (need_to_tick)
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+
+ /* Note activity of following buffer */
+ if (!branch.isBubble())
+ cpu.activityRecorder->activity();
+
+ /* Make sure the input (if any left) is pushed */
+ inputBuffer.pushTail();
+}
+
+void
+Execute::wakeupFetch(BranchData::Reason reason)
+{
+ BranchData branch;
+ assert(branch.isBubble());
+
+ /* THREAD thread id */
+ ThreadContext *thread = cpu.getContext(0);
+
+ /* Force a branch to the current PC (which should be the next inst.) to
+ * wake up Fetch1 */
+ if (!branch.isStreamChange() /* No real branch already happened */) {
+ DPRINTF(MinorInterrupt, "Waking up Fetch (via Execute) by issuing"
+ " a branch: %s\n", thread->pcState());
+
+ assert(thread->pcState().microPC() == 0);
+
+ updateBranchData(reason,
+ MinorDynInst::bubble(), thread->pcState(), branch);
+ } else {
+ DPRINTF(MinorInterrupt, "Already branching, no need for wakeup\n");
+ }
+
+ *out.inputWire = branch;
+
+ /* Make sure we get ticked */
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+}
+
+void
+Execute::minorTrace() const
+{
+ std::ostringstream insts;
+ std::ostringstream stalled;
+
+ instsBeingCommitted.reportData(insts);
+ lsq.minorTrace();
+ inputBuffer.minorTrace();
+ scoreboard.minorTrace();
+
+ /* Report functional unit stalling in one string */
+ unsigned int i = 0;
+ while (i < numFuncUnits)
+ {
+ stalled << (funcUnits[i]->stalled ? '1' : 'E');
+ i++;
+ if (i != numFuncUnits)
+ stalled << ',';
+ }
+
+ MINORTRACE("insts=%s inputIndex=%d streamSeqNum=%d"
+ " stalled=%s drainState=%d isInbetweenInsts=%d\n",
+ insts.str(), inputIndex, streamSeqNum, stalled.str(), drainState,
+ isInbetweenInsts());
+
+ std::for_each(funcUnits.begin(), funcUnits.end(),
+ std::mem_fun(&FUPipeline::minorTrace));
+
+ inFlightInsts->minorTrace();
+ inFUMemInsts->minorTrace();
+}
+
+void
+Execute::drainResume()
+{
+ DPRINTF(Drain, "MinorExecute drainResume\n");
+
+ setDrainState(NotDraining);
+
+ /* Wakeup fetch and keep the pipeline running until that branch takes
+ * effect */
+ wakeupFetch(BranchData::WakeupFetch);
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+}
+
+std::ostream &operator <<(std::ostream &os, Execute::DrainState state)
+{
+ switch (state)
+ {
+ case Execute::NotDraining:
+ os << "NotDraining";
+ break;
+ case Execute::DrainCurrentInst:
+ os << "DrainCurrentInst";
+ break;
+ case Execute::DrainHaltFetch:
+ os << "DrainHaltFetch";
+ break;
+ case Execute::DrainAllInsts:
+ os << "DrainAllInsts";
+ break;
+ default:
+ os << "Drain-" << static_cast<int>(state);
+ break;
+ }
+
+ return os;
+}
+
+void
+Execute::setDrainState(DrainState state)
+{
+ DPRINTF(Drain, "setDrainState: %s\n", state);
+ drainState = state;
+}
+
+unsigned int
+Execute::drain()
+{
+ DPRINTF(Drain, "MinorExecute drain\n");
+
+ if (drainState == NotDraining) {
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+
+ /* Go to DrainCurrentInst if we're not between operations
+ * this should probably test the LSQ as well. Or maybe
+ * just always go to DrainCurrentInst anyway */
+ if (lastCommitWasEndOfMacroop)
+ setDrainState(DrainHaltFetch);
+ else
+ setDrainState(DrainCurrentInst);
+ }
+
+ return (isDrained() ? 0 : 1);
+}
+
+bool
+Execute::isDrained()
+{
+ return drainState == DrainAllInsts &&
+ inputBuffer.empty() &&
+ inFlightInsts->empty() &&
+ lsq.isDrained();
+}
+
+Execute::~Execute()
+{
+ for (unsigned int i = 0; i < numFuncUnits; i++)
+ delete funcUnits[i];
+
+ delete inFlightInsts;
+}
+
+bool
+Execute::instIsRightStream(MinorDynInstPtr inst)
+{
+ return inst->id.streamSeqNum == streamSeqNum;
+}
+
+bool
+Execute::instIsHeadInst(MinorDynInstPtr inst)
+{
+ bool ret = false;
+
+ if (!inFlightInsts->empty())
+ ret = inFlightInsts->front().inst->id == inst->id;
+
+ return ret;
+}
+
+MinorCPU::MinorCPUPort &
+Execute::getDcachePort()
+{
+ return lsq.getDcachePort();
+}
+
+}
diff --git a/src/cpu/minor/execute.hh b/src/cpu/minor/execute.hh
new file mode 100644
index 000000000..8cd026534
--- /dev/null
+++ b/src/cpu/minor/execute.hh
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * All the fun of executing instructions from Decode and sending branch/new
+ * instruction stream info. to Fetch1.
+ */
+
+#ifndef __CPU_MINOR_EXECUTE_HH__
+#define __CPU_MINOR_EXECUTE_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/func_unit.hh"
+#include "cpu/minor/lsq.hh"
+#include "cpu/minor/pipe_data.hh"
+#include "cpu/minor/scoreboard.hh"
+
+namespace Minor
+{
+
+/** Execute stage. Everything apart from fetching and decoding instructions.
+ * The LSQ lives here too. */
+class Execute : public Named
+{
+ protected:
+ /** Input port carrying instructions from Decode */
+ Latch<ForwardInstData>::Output inp;
+
+ /** Input port carrying stream changes to Fetch1 */
+ Latch<BranchData>::Input out;
+
+ /** Pointer back to the containing CPU */
+ MinorCPU &cpu;
+
+ /** Number of instructions that can be issued per cycle */
+ unsigned int issueLimit;
+
+ /** Number of memory ops that can be issued per cycle */
+ unsigned int memoryIssueLimit;
+
+ /** Number of instructions that can be committed per cycle */
+ unsigned int commitLimit;
+
+ /** Number of memory instructions that can be committed per cycle */
+ unsigned int memoryCommitLimit;
+
+ /** If true, more than one input line can be processed each cycle if
+ * there is room to execute more instructions than taken from the first
+ * line */
+ bool processMoreThanOneInput;
+
+ /** Descriptions of the functional units we want to generate */
+ MinorFUPool &fuDescriptions;
+
+ /** Number of functional units to produce */
+ unsigned int numFuncUnits;
+
+ /** Longest latency of any FU, useful for setting up the activity
+ * recoder */
+ Cycles longestFuLatency;
+
+ /** Modify instruction trace times on commit */
+ bool setTraceTimeOnCommit;
+
+ /** Modify instruction trace times on issue */
+ bool setTraceTimeOnIssue;
+
+ /** Allow mem refs to leave their FUs before reaching the head
+ * of the in flight insts queue if their dependencies are met */
+ bool allowEarlyMemIssue;
+
+ /** The FU index of the non-existent costless FU for instructions
+ * which pass the MinorDynInst::isNoCostInst test */
+ unsigned int noCostFUIndex;
+
+ /** Dcache port to pass on to the CPU. Execute owns this */
+ LSQ lsq;
+
+ /** Scoreboard of instruction dependencies */
+ Scoreboard scoreboard;
+
+ /** The execution functional units */
+ std::vector<FUPipeline *> funcUnits;
+
+ public: /* Public for Pipeline to be able to pass it to Decode */
+ InputBuffer<ForwardInstData> inputBuffer;
+
+ protected:
+ /** Stage cycle-by-cycle state */
+
+ /** State that drain passes through (in order). On a drain request,
+ * Execute transitions into either DrainCurrentInst (if between
+ * microops) or DrainHaltFetch.
+ *
+ * Note that Execute doesn't actually have * a 'Drained' state, only
+ * an indication that it's currently draining and isDrained that can't
+ * tell if there are insts still in the pipeline leading up to
+ * Execute */
+ enum DrainState
+ {
+ NotDraining, /* Not draining, possibly running */
+ DrainCurrentInst, /* Draining to end of inst/macroop */
+ DrainHaltFetch, /* Halting Fetch after completing current inst */
+ DrainAllInsts /* Discarding all remaining insts */
+ };
+
+ /** In-order instructions either in FUs or the LSQ */
+ Queue<QueuedInst, ReportTraitsAdaptor<QueuedInst> > *inFlightInsts;
+
+ /** Memory ref instructions still in the FUs */
+ Queue<QueuedInst, ReportTraitsAdaptor<QueuedInst> > *inFUMemInsts;
+
+ /** Index that we've completed upto in getInput data. We can say we're
+ * popInput when this equals getInput()->width() */
+ unsigned int inputIndex;
+
+ /** The last commit was the end of a full instruction so an interrupt
+ * can safely happen */
+ bool lastCommitWasEndOfMacroop;
+
+ /** Structure for reporting insts currently being processed/retired
+ * for MinorTrace */
+ ForwardInstData instsBeingCommitted;
+
+ /** Source of sequence number for instuction streams. Increment this and
+ * pass to fetch whenever an instruction stream needs to be changed.
+ * For any more complicated behaviour (e.g. speculation) there'll need
+ * to be another plan. THREAD, need one for each thread */
+ InstSeqNum streamSeqNum;
+
+ /** A prediction number for use where one isn't available from an
+ * instruction. This is harvested from committed instructions.
+ * This isn't really needed as the streamSeqNum will change on
+ * a branch, but it minimises disruption in stream identification */
+ InstSeqNum lastPredictionSeqNum;
+
+ /** State progression for draining NotDraining -> ... -> DrainAllInsts */
+ DrainState drainState;
+
+ protected:
+ friend std::ostream &operator <<(std::ostream &os, DrainState state);
+
+ /** Get a piece of data to work on from the inputBuffer, or 0 if there
+ * is no data. */
+ const ForwardInstData *getInput();
+
+ /** Pop an element off the input buffer, if there are any */
+ void popInput();
+
+ /** Generate Branch data based (into branch) on an observed (or not)
+ * change in PC while executing an instruction.
+ * Also handles branch prediction information within the inst. */
+ void tryToBranch(MinorDynInstPtr inst, Fault fault, BranchData &branch);
+
+ /** Actually create a branch to communicate to Fetch1/Fetch2 and,
+ * if that is a stream-changing branch update the streamSeqNum */
+ void updateBranchData(BranchData::Reason reason,
+ MinorDynInstPtr inst, const TheISA::PCState &target,
+ BranchData &branch);
+
+ /** Handle extracting mem ref responses from the memory queues and
+ * completing the associated instructions.
+ * Fault is an output and will contain any fault caused (and already
+ * invoked by the function)
+ * Sets branch to any branch generated by the instruction. */
+ void handleMemResponse(MinorDynInstPtr inst,
+ LSQ::LSQRequestPtr response, BranchData &branch,
+ Fault &fault);
+
+ /** Execute a memory reference instruction. This calls initiateAcc on
+ * the instruction which will then call writeMem or readMem to issue a
+ * memory access to the LSQ.
+ * Returns true if the instruction was executed rather than stalled
+ * because of a lack of LSQ resources and false otherwise.
+ * branch is set to any branch raised by the instruction.
+ * failed_predicate is set to false if the instruction passed its
+ * predicate and so will access memory or true if the instruction
+ * *failed* its predicate and is now complete.
+ * fault is set if any non-NoFault fault is raised.
+ * Any faults raised are actually invoke-d by this function. */
+ bool executeMemRefInst(MinorDynInstPtr inst, BranchData &branch,
+ bool &failed_predicate, Fault &fault);
+
+ /** Has an interrupt been raised */
+ bool isInterrupted(ThreadID thread_id) const;
+
+ /** Are we between instructions? Can we be interrupted? */
+ bool isInbetweenInsts() const;
+
+ /** Act on an interrupt. Returns true if an interrupt was actually
+ * signalled and invoked */
+ bool takeInterrupt(ThreadID thread_id, BranchData &branch);
+
+ /** Try and issue instructions from the inputBuffer */
+ unsigned int issue(bool only_issue_microops);
+
+ /** Try to act on PC-related events. Returns true if any were
+ * executed */
+ bool tryPCEvents();
+
+ /** Do the stats handling and instruction count and PC event events
+ * related to the new instruction/op counts */
+ void doInstCommitAccounting(MinorDynInstPtr inst);
+
+ /** Commit a single instruction. Returns true if the instruction being
+ * examined was completed (fully executed, discarded, or initiated a
+ * memory access), false if there is still some processing to do.
+ * fu_index is the index of the functional unit this instruction is
+ * being executed in into for funcUnits
+ * If early_memory_issue is true then this is an early execution
+ * of a mem ref and so faults will not be processed.
+ * If the return value is true:
+ * fault is set if a fault happened,
+ * branch is set to indicate any branch that occurs
+ * committed is set to true if this instruction is committed
+ * (and so needs to be traced and accounted for)
+ * completed_mem_issue is set if the instruction was a
+ * memory access that was issued */
+ bool commitInst(MinorDynInstPtr inst, bool early_memory_issue,
+ BranchData &branch, Fault &fault, bool &committed,
+ bool &completed_mem_issue);
+
+ /** Try and commit instructions from the ends of the functional unit
+ * pipelines.
+ * If only_commit_microops is true then only commit upto the
+ * end of the currect full instruction.
+ * If discard is true then discard all instructions rather than
+ * committing.
+ * branch is set to any branch raised during commit. */
+ void commit(bool only_commit_microops, bool discard, BranchData &branch);
+
+ /** Set the drain state (with useful debugging messages) */
+ void setDrainState(DrainState state);
+
+ public:
+ Execute(const std::string &name_,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardInstData>::Output inp_,
+ Latch<BranchData>::Input out_);
+
+ ~Execute();
+
+ public:
+
+ /** Cause Execute to issue an UnpredictedBranch (or WakeupFetch if
+ * that was passed as the reason) to Fetch1 to wake the
+ * system up (using the PC from the thread context). */
+ void wakeupFetch(BranchData::Reason reason =
+ BranchData::UnpredictedBranch);
+
+ /** Returns the DcachePort owned by this Execute to pass upwards */
+ MinorCPU::MinorCPUPort &getDcachePort();
+
+ /** To allow ExecContext to find the LSQ */
+ LSQ &getLSQ() { return lsq; }
+
+ /** Does the given instruction have the right stream sequence number
+ * to be committed? */
+ bool instIsRightStream(MinorDynInstPtr inst);
+
+ /** Returns true if the given instruction is at the head of the
+ * inFlightInsts instruction queue */
+ bool instIsHeadInst(MinorDynInstPtr inst);
+
+ /** Pass on input/buffer data to the output if you can */
+ void evaluate();
+
+ void minorTrace() const;
+
+ /** After thread suspension, has Execute been drained of in-flight
+ * instructions and memory accesses. */
+ bool isDrained();
+
+ /** Like the drain interface on SimObject */
+ unsigned int drain();
+ void drainResume();
+};
+
+}
+
+#endif /* __CPU_MINOR_EXECUTE_HH__ */
diff --git a/src/cpu/minor/fetch1.cc b/src/cpu/minor/fetch1.cc
new file mode 100644
index 000000000..45dc5eddc
--- /dev/null
+++ b/src/cpu/minor/fetch1.cc
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <cstring>
+#include <iomanip>
+#include <sstream>
+
+#include "base/cast.hh"
+#include "cpu/minor/fetch1.hh"
+#include "cpu/minor/pipeline.hh"
+#include "debug/Drain.hh"
+#include "debug/Fetch.hh"
+#include "debug/MinorTrace.hh"
+
+namespace Minor
+{
+
+Fetch1::Fetch1(const std::string &name_,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<BranchData>::Output inp_,
+ Latch<ForwardLineData>::Input out_,
+ Latch<BranchData>::Output prediction_,
+ Reservable &next_stage_input_buffer) :
+ Named(name_),
+ cpu(cpu_),
+ inp(inp_),
+ out(out_),
+ prediction(prediction_),
+ nextStageReserve(next_stage_input_buffer),
+ icachePort(name_ + ".icache_port", *this, cpu_),
+ lineSnap(params.fetch1LineSnapWidth),
+ maxLineWidth(params.fetch1LineWidth),
+ fetchLimit(params.fetch1FetchLimit),
+ state(FetchWaitingForPC),
+ pc(0),
+ streamSeqNum(InstId::firstStreamSeqNum),
+ predictionSeqNum(InstId::firstPredictionSeqNum),
+ blocked(false),
+ requests(name_ + ".requests", "lines", params.fetch1FetchLimit),
+ transfers(name_ + ".transfers", "lines", params.fetch1FetchLimit),
+ icacheState(IcacheRunning),
+ lineSeqNum(InstId::firstLineSeqNum),
+ numFetchesInMemorySystem(0),
+ numFetchesInITLB(0)
+{
+ if (lineSnap == 0) {
+ lineSnap = cpu.cacheLineSize();
+ DPRINTF(Fetch, "lineSnap set to cache line size of: %d\n",
+ lineSnap);
+ }
+
+ if (maxLineWidth == 0) {
+ maxLineWidth = cpu.cacheLineSize();
+ DPRINTF(Fetch, "maxLineWidth set to cache line size of: %d\n",
+ maxLineWidth);
+ }
+
+ /* These assertions should be copied to the Python config. as well */
+ if ((lineSnap % sizeof(TheISA::MachInst)) != 0) {
+ fatal("%s: fetch1LineSnapWidth must be a multiple "
+ "of sizeof(TheISA::MachInst) (%d)\n", name_,
+ sizeof(TheISA::MachInst));
+ }
+
+ if (!(maxLineWidth >= lineSnap &&
+ (maxLineWidth % sizeof(TheISA::MachInst)) == 0))
+ {
+ fatal("%s: fetch1LineWidth must be a multiple of"
+ " sizeof(TheISA::MachInst)"
+ " (%d), and >= fetch1LineSnapWidth (%d)\n",
+ name_, sizeof(TheISA::MachInst), lineSnap);
+ }
+
+ if (fetchLimit < 1) {
+ fatal("%s: fetch1FetchLimit must be >= 1 (%d)\n", name_,
+ fetchLimit);
+ }
+}
+
+void
+Fetch1::fetchLine()
+{
+ /* If line_offset != 0, a request is pushed for the remainder of the
+ * line. */
+ /* Use a lower, sizeof(MachInst) aligned address for the fetch */
+ Addr aligned_pc = pc.instAddr() & ~((Addr) lineSnap - 1);
+ unsigned int line_offset = aligned_pc % lineSnap;
+ unsigned int request_size = maxLineWidth - line_offset;
+
+ /* Fill in the line's id */
+ InstId request_id(0 /* thread */,
+ streamSeqNum, predictionSeqNum,
+ lineSeqNum);
+
+ FetchRequestPtr request = new FetchRequest(*this, request_id, pc);
+
+ DPRINTF(Fetch, "Inserting fetch into the fetch queue "
+ "%s addr: 0x%x pc: %s line_offset: %d request_size: %d\n",
+ request_id, aligned_pc, pc, line_offset, request_size);
+
+ request->request.setThreadContext(cpu.cpuId(), /* thread id */ 0);
+ request->request.setVirt(0 /* asid */,
+ aligned_pc, request_size, Request::INST_FETCH, cpu.instMasterId(),
+ /* I've no idea why we need the PC, but give it */
+ pc.instAddr());
+
+ DPRINTF(Fetch, "Submitting ITLB request\n");
+ numFetchesInITLB++;
+
+ request->state = FetchRequest::InTranslation;
+
+ /* Reserve space in the queues upstream of requests for results */
+ transfers.reserve();
+ requests.push(request);
+
+ /* Submit the translation request. The response will come
+ * through finish/markDelayed on this request as it bears
+ * the Translation interface */
+ cpu.threads[request->id.threadId]->itb->translateTiming(
+ &request->request,
+ cpu.getContext(request->id.threadId),
+ request, BaseTLB::Execute);
+
+ lineSeqNum++;
+
+ /* Step the PC for the next line onto the line aligned next address.
+ * Note that as instructions can span lines, this PC is only a
+ * reliable 'new' PC if the next line has a new stream sequence number. */
+#if THE_ISA == ALPHA_ISA
+ /* Restore the low bits of the PC used as address space flags */
+ Addr pc_low_bits = pc.instAddr() &
+ ((Addr) (1 << sizeof(TheISA::MachInst)) - 1);
+
+ pc.set(aligned_pc + request_size + pc_low_bits);
+#else
+ pc.set(aligned_pc + request_size);
+#endif
+}
+
+std::ostream &
+operator <<(std::ostream &os, Fetch1::IcacheState state)
+{
+ switch (state) {
+ case Fetch1::IcacheRunning:
+ os << "IcacheRunning";
+ break;
+ case Fetch1::IcacheNeedsRetry:
+ os << "IcacheNeedsRetry";
+ break;
+ default:
+ os << "IcacheState-" << static_cast<int>(state);
+ break;
+ }
+ return os;
+}
+
+void
+Fetch1::FetchRequest::makePacket()
+{
+ /* Make the necessary packet for a memory transaction */
+ packet = new Packet(&request, MemCmd::ReadReq);
+ packet->allocate();
+
+ /* This FetchRequest becomes SenderState to allow the response to be
+ * identified */
+ packet->pushSenderState(this);
+}
+
+void
+Fetch1::FetchRequest::finish(
+ Fault fault_, RequestPtr request_, ThreadContext *tc, BaseTLB::Mode mode)
+{
+ fault = fault_;
+
+ state = Translated;
+ fetch.handleTLBResponse(this);
+
+ /* Let's try and wake up the processor for the next cycle */
+ fetch.cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
+}
+
+void
+Fetch1::handleTLBResponse(FetchRequestPtr response)
+{
+ numFetchesInITLB--;
+
+ if (response->fault != NoFault) {
+ DPRINTF(Fetch, "Fault in address ITLB translation: %s, "
+ "paddr: 0x%x, vaddr: 0x%x\n",
+ response->fault->name(),
+ (response->request.hasPaddr() ? response->request.getPaddr() : 0),
+ response->request.getVaddr());
+
+ if (DTRACE(MinorTrace))
+ minorTraceResponseLine(name(), response);
+ } else {
+ DPRINTF(Fetch, "Got ITLB response\n");
+ }
+
+ response->state = FetchRequest::Translated;
+
+ tryToSendToTransfers(response);
+}
+
+Fetch1::FetchRequest::~FetchRequest()
+{
+ if (packet)
+ delete packet;
+}
+
+void
+Fetch1::tryToSendToTransfers(FetchRequestPtr request)
+{
+ if (!requests.empty() && requests.front() != request) {
+ DPRINTF(Fetch, "Fetch not at front of requests queue, can't"
+ " issue to memory\n");
+ return;
+ }
+
+ if (request->state == FetchRequest::InTranslation) {
+ DPRINTF(Fetch, "Fetch still in translation, not issuing to"
+ " memory\n");
+ return;
+ }
+
+ if (request->isDiscardable() || request->fault != NoFault) {
+ /* Discarded and faulting requests carry on through transfers
+ * as Complete/packet == NULL */
+
+ request->state = FetchRequest::Complete;
+ moveFromRequestsToTransfers(request);
+
+ /* Wake up the pipeline next cycle as there will be no event
+ * for this queue->queue transfer */
+ cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
+ } else if (request->state == FetchRequest::Translated) {
+ if (!request->packet)
+ request->makePacket();
+
+ /* Ensure that the packet won't delete the request */
+ assert(request->packet->needsResponse());
+
+ if (tryToSend(request))
+ moveFromRequestsToTransfers(request);
+ } else {
+ DPRINTF(Fetch, "Not advancing line fetch\n");
+ }
+}
+
+void
+Fetch1::moveFromRequestsToTransfers(FetchRequestPtr request)
+{
+ assert(!requests.empty() && requests.front() == request);
+
+ requests.pop();
+ transfers.push(request);
+}
+
+bool
+Fetch1::tryToSend(FetchRequestPtr request)
+{
+ bool ret = false;
+
+ if (icachePort.sendTimingReq(request->packet)) {
+ /* Invalidate the fetch_requests packet so we don't
+ * accidentally fail to deallocate it (or use it!)
+ * later by overwriting it */
+ request->packet = NULL;
+ request->state = FetchRequest::RequestIssuing;
+ numFetchesInMemorySystem++;
+
+ ret = true;
+
+ DPRINTF(Fetch, "Issued fetch request to memory: %s\n",
+ request->id);
+ } else {
+ /* Needs to be resent, wait for that */
+ icacheState = IcacheNeedsRetry;
+
+ DPRINTF(Fetch, "Line fetch needs to retry: %s\n",
+ request->id);
+ }
+
+ return ret;
+}
+
+void
+Fetch1::stepQueues()
+{
+ IcacheState old_icache_state = icacheState;
+
+ switch (icacheState) {
+ case IcacheRunning:
+ /* Move ITLB results on to the memory system */
+ if (!requests.empty()) {
+ tryToSendToTransfers(requests.front());
+ }
+ break;
+ case IcacheNeedsRetry:
+ break;
+ }
+
+ if (icacheState != old_icache_state) {
+ DPRINTF(Fetch, "Step in state %s moving to state %s\n",
+ old_icache_state, icacheState);
+ }
+}
+
+void
+Fetch1::popAndDiscard(FetchQueue &queue)
+{
+ if (!queue.empty()) {
+ delete queue.front();
+ queue.pop();
+ }
+}
+
+unsigned int
+Fetch1::numInFlightFetches()
+{
+ return requests.occupiedSpace() +
+ transfers.occupiedSpace();
+}
+
+/** Print the appropriate MinorLine line for a fetch response */
+void
+Fetch1::minorTraceResponseLine(const std::string &name,
+ Fetch1::FetchRequestPtr response) const
+{
+ Request &request M5_VAR_USED = response->request;
+
+ if (response->packet && response->packet->isError()) {
+ MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"error packet\"\n",
+ response->id, request.getVaddr());
+ } else if (response->fault != NoFault) {
+ MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"%s\"\n",
+ response->id, request.getVaddr(), response->fault->name());
+ } else {
+ MINORLINE(this, "id=%s size=%d vaddr=0x%x paddr=0x%x\n",
+ response->id, request.getSize(),
+ request.getVaddr(), request.getPaddr());
+ }
+}
+
+bool
+Fetch1::recvTimingResp(PacketPtr response)
+{
+ DPRINTF(Fetch, "recvTimingResp %d\n", numFetchesInMemorySystem);
+
+ /* Only push the response if we didn't change stream? No, all responses
+ * should hit the responses queue. It's the job of 'step' to throw them
+ * away. */
+ FetchRequestPtr fetch_request = safe_cast<FetchRequestPtr>
+ (response->popSenderState());
+
+ /* Fixup packet in fetch_request as this may have changed */
+ assert(!fetch_request->packet);
+ fetch_request->packet = response;
+
+ numFetchesInMemorySystem--;
+ fetch_request->state = FetchRequest::Complete;
+
+ if (DTRACE(MinorTrace))
+ minorTraceResponseLine(name(), fetch_request);
+
+ if (response->isError()) {
+ DPRINTF(Fetch, "Received error response packet: %s\n",
+ fetch_request->id);
+ }
+
+ /* We go to idle even if there are more things to do on the queues as
+ * it's the job of step to actually step us on to the next transaction */
+
+ /* Let's try and wake up the processor for the next cycle to move on
+ * queues */
+ cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
+
+ /* Never busy */
+ return true;
+}
+
+void
+Fetch1::recvRetry()
+{
+ DPRINTF(Fetch, "recvRetry\n");
+ assert(icacheState == IcacheNeedsRetry);
+ assert(!requests.empty());
+
+ FetchRequestPtr retryRequest = requests.front();
+
+ icacheState = IcacheRunning;
+
+ if (tryToSend(retryRequest))
+ moveFromRequestsToTransfers(retryRequest);
+}
+
+std::ostream &
+operator <<(std::ostream &os, Fetch1::FetchState state)
+{
+ switch (state) {
+ case Fetch1::FetchHalted:
+ os << "FetchHalted";
+ break;
+ case Fetch1::FetchWaitingForPC:
+ os << "FetchWaitingForPC";
+ break;
+ case Fetch1::FetchRunning:
+ os << "FetchRunning";
+ break;
+ default:
+ os << "FetchState-" << static_cast<int>(state);
+ break;
+ }
+ return os;
+}
+
+void
+Fetch1::changeStream(const BranchData &branch)
+{
+ updateExpectedSeqNums(branch);
+
+ /* Start fetching again if we were stopped */
+ switch (branch.reason) {
+ case BranchData::SuspendThread:
+ DPRINTF(Fetch, "Suspending fetch: %s\n", branch);
+ state = FetchWaitingForPC;
+ break;
+ case BranchData::HaltFetch:
+ DPRINTF(Fetch, "Halting fetch\n");
+ state = FetchHalted;
+ break;
+ default:
+ DPRINTF(Fetch, "Changing stream on branch: %s\n", branch);
+ state = FetchRunning;
+ break;
+ }
+ pc = branch.target;
+}
+
+void
+Fetch1::updateExpectedSeqNums(const BranchData &branch)
+{
+ DPRINTF(Fetch, "Updating streamSeqNum from: %d to %d,"
+ " predictionSeqNum from: %d to %d\n",
+ streamSeqNum, branch.newStreamSeqNum,
+ predictionSeqNum, branch.newPredictionSeqNum);
+
+ /* Change the stream */
+ streamSeqNum = branch.newStreamSeqNum;
+ /* Update the prediction. Note that it's possible for this to
+ * actually set the prediction to an *older* value if new
+ * predictions have been discarded by execute */
+ predictionSeqNum = branch.newPredictionSeqNum;
+}
+
+void
+Fetch1::processResponse(Fetch1::FetchRequestPtr response,
+ ForwardLineData &line)
+{
+ PacketPtr packet = response->packet;
+
+ /* Pass the prefetch abort (if any) on to Fetch2 in a ForwardLineData
+ * structure */
+ line.setFault(response->fault);
+ /* Make sequence numbers valid in return */
+ line.id = response->id;
+ /* Set PC to virtual address */
+ line.pc = response->pc;
+ /* Set the lineBase, which is a sizeof(MachInst) aligned address <=
+ * pc.instAddr() */
+ line.lineBaseAddr = response->request.getVaddr();
+
+ if (response->fault != NoFault) {
+ /* Stop fetching if there was a fault */
+ /* Should probably try to flush the queues as well, but we
+ * can't be sure that this fault will actually reach Execute, and we
+ * can't (currently) selectively remove this stream from the queues */
+ DPRINTF(Fetch, "Stopping line fetch because of fault: %s\n",
+ response->fault->name());
+ state = Fetch1::FetchWaitingForPC;
+ } else {
+ line.adoptPacketData(packet);
+ /* Null the response's packet to prevent the response from trying to
+ * deallocate the packet */
+ response->packet = NULL;
+ }
+}
+
+void
+Fetch1::evaluate()
+{
+ const BranchData &execute_branch = *inp.outputWire;
+ const BranchData &fetch2_branch = *prediction.outputWire;
+ ForwardLineData &line_out = *out.inputWire;
+
+ assert(line_out.isBubble());
+
+ blocked = !nextStageReserve.canReserve();
+
+ /* Are we changing stream? Look to the Execute branches first, then
+ * to predicted changes of stream from Fetch2 */
+ /* @todo, find better way to express ignoring branch predictions */
+ if (execute_branch.isStreamChange() &&
+ execute_branch.reason != BranchData::BranchPrediction)
+ {
+ if (state == FetchHalted) {
+ if (execute_branch.reason == BranchData::WakeupFetch) {
+ DPRINTF(Fetch, "Waking up fetch: %s\n", execute_branch);
+ changeStream(execute_branch);
+ } else {
+ DPRINTF(Fetch, "Halted, ignoring branch: %s\n",
+ execute_branch);
+ }
+ } else {
+ changeStream(execute_branch);
+ }
+
+ if (!fetch2_branch.isBubble()) {
+ DPRINTF(Fetch, "Ignoring simultaneous prediction: %s\n",
+ fetch2_branch);
+ }
+
+ /* The streamSeqNum tagging in request/response ->req should handle
+ * discarding those requests when we get to them. */
+ } else if (state != FetchHalted && fetch2_branch.isStreamChange()) {
+ /* Handle branch predictions by changing the instruction source
+ * if we're still processing the same stream (as set by streamSeqNum)
+ * as the one of the prediction.
+ */
+ if (fetch2_branch.newStreamSeqNum != streamSeqNum) {
+ DPRINTF(Fetch, "Not changing stream on prediction: %s,"
+ " streamSeqNum mismatch\n",
+ fetch2_branch);
+ } else {
+ changeStream(fetch2_branch);
+ }
+ }
+
+ /* Can we fetch? */
+ /* The bare minimum requirements for initiating a fetch */
+ /* THREAD need to handle multiple threads */
+ if (state == FetchRunning && /* We are actually fetching */
+ !blocked && /* Space in the Fetch2 inputBuffer */
+ /* The thread we're going to fetch for (thread 0), is active */
+ cpu.getContext(0)->status() == ThreadContext::Active &&
+ numInFlightFetches() < fetchLimit)
+ {
+ fetchLine();
+ /* Take up a slot in the fetch queue */
+ nextStageReserve.reserve();
+ }
+
+ /* Halting shouldn't prevent fetches in flight from being processed */
+ /* Step fetches through the icachePort queues and memory system */
+ stepQueues();
+
+ /* As we've thrown away early lines, if there is a line, it must
+ * be from the right stream */
+ if (!transfers.empty() &&
+ transfers.front()->isComplete())
+ {
+ Fetch1::FetchRequestPtr response = transfers.front();
+
+ if (response->isDiscardable()) {
+ nextStageReserve.freeReservation();
+
+ DPRINTF(Fetch, "Discarding translated fetch at it's for"
+ " an old stream\n");
+
+ /* Wake up next cycle just in case there was some other
+ * action to do */
+ cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
+ } else {
+ DPRINTF(Fetch, "Processing fetched line: %s\n",
+ response->id);
+
+ processResponse(response, line_out);
+ }
+
+ popAndDiscard(transfers);
+ }
+
+ /* If we generated output, and mark the stage as being active
+ * to encourage that output on to the next stage */
+ if (!line_out.isBubble())
+ cpu.activityRecorder->activity();
+
+ /* Fetch1 has no inputBuffer so the only activity we can have is to
+ * generate a line output (tested just above) or to initiate a memory
+ * fetch which will signal activity when it returns/needs stepping
+ * between queues */
+}
+
+bool
+Fetch1::isDrained()
+{
+ DPRINTF(Drain, "isDrained %s %s%s%s\n",
+ state == FetchHalted,
+ (numInFlightFetches() == 0 ? "" : "inFlightFetches "),
+ ((*out.inputWire).isBubble() ? "" : "outputtingLine"));
+
+ return state == FetchHalted &&
+ numInFlightFetches() == 0 &&
+ (*out.inputWire).isBubble();
+}
+
+void
+Fetch1::FetchRequest::reportData(std::ostream &os) const
+{
+ os << id;
+}
+
+bool Fetch1::FetchRequest::isDiscardable() const
+{
+ /* Can't discard lines in TLB/memory */
+ return state != InTranslation && state != RequestIssuing &&
+ (id.streamSeqNum != fetch.streamSeqNum ||
+ id.predictionSeqNum != fetch.predictionSeqNum);
+}
+
+void
+Fetch1::minorTrace() const
+{
+ std::ostringstream data;
+
+ if (blocked)
+ data << 'B';
+ else
+ (*out.inputWire).reportData(data);
+
+ MINORTRACE("state=%s icacheState=%s in_tlb_mem=%s/%s"
+ " streamSeqNum=%d lines=%s\n", state, icacheState,
+ numFetchesInITLB, numFetchesInMemorySystem,
+ streamSeqNum, data.str());
+ requests.minorTrace();
+ transfers.minorTrace();
+}
+
+}
diff --git a/src/cpu/minor/fetch1.hh b/src/cpu/minor/fetch1.hh
new file mode 100644
index 000000000..29a63d1f1
--- /dev/null
+++ b/src/cpu/minor/fetch1.hh
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Fetch1 is responsible for fetching "lines" from memory and passing
+ * them to Fetch2
+ */
+
+#ifndef __CPU_MINOR_FETCH1_HH__
+#define __CPU_MINOR_FETCH1_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/pipe_data.hh"
+#include "cpu/base.hh"
+#include "mem/packet.hh"
+
+namespace Minor
+{
+
+/** A stage responsible for fetching "lines" from memory and passing
+ * them to Fetch2 */
+class Fetch1 : public Named
+{
+ protected:
+ /** Exposable fetch port */
+ class IcachePort : public MinorCPU::MinorCPUPort
+ {
+ protected:
+ /** My owner */
+ Fetch1 &fetch;
+
+ public:
+ IcachePort(std::string name, Fetch1 &fetch_, MinorCPU &cpu) :
+ MinorCPU::MinorCPUPort(name, cpu), fetch(fetch_)
+ { }
+
+ protected:
+ bool recvTimingResp(PacketPtr pkt)
+ { return fetch.recvTimingResp(pkt); }
+
+ void recvRetry() { fetch.recvRetry(); }
+ };
+
+ /** Memory access queuing.
+ *
+ * A request can be submitted by pushing it onto the requests queue after
+ * issuing an ITLB lookup (state becomes InTranslation) with a
+ * FetchSenderState senderState containing the current lineSeqNum and
+ * stream/predictionSeqNum.
+ *
+ * Translated packets (state becomes Translation) are then passed to the
+ * memory system and the transfers queue (state becomes RequestIssuing).
+ * Retries are handled by leaving the packet on the requests queue and
+ * changing the state to IcacheNeedsRetry).
+ *
+ * Responses from the memory system alter the request object (state
+ * become Complete). Responses can be picked up from the head of the
+ * transfers queue to pass on to Fetch2. */
+
+ /** Structure to hold SenderState info through
+ * translation and memory accesses. */
+ class FetchRequest :
+ public BaseTLB::Translation, /* For TLB lookups */
+ public Packet::SenderState /* For packing into a Packet */
+ {
+ protected:
+ /** Owning fetch unit */
+ Fetch1 &fetch;
+
+ public:
+ /** Progress of this request through address translation and
+ * memory */
+ enum FetchRequestState
+ {
+ NotIssued, /* Just been made */
+ InTranslation, /* Issued to ITLB, must wait for reqply */
+ Translated, /* Translation complete */
+ RequestIssuing, /* Issued to memory, must wait for response */
+ Complete /* Complete. Either a fault, or a fetched line */
+ };
+
+ FetchRequestState state;
+
+ /** Identity of the line that this request will generate */
+ InstId id;
+
+ /** FetchRequests carry packets while they're in the requests and
+ * transfers responses queues. When a Packet returns from the memory
+ * system, its request needs to have its packet updated as this may
+ * have changed in flight */
+ PacketPtr packet;
+
+ /** The underlying request that this fetch represents */
+ Request request;
+
+ /** PC to fixup with line address */
+ TheISA::PCState pc;
+
+ /** Fill in a fault if one happens during fetch, check this by
+ * picking apart the response packet */
+ Fault fault;
+
+ /** Make a packet to use with the memory transaction */
+ void makePacket();
+
+ /** Report interface */
+ void reportData(std::ostream &os) const;
+
+ /** Is this line out of date with the current stream/prediction
+ * sequence and can it be discarded without orphaning in flight
+ * TLB lookups/memory accesses? */
+ bool isDiscardable() const;
+
+ /** Is this a complete read line or fault */
+ bool isComplete() const { return state == Complete; }
+
+ protected:
+ /** BaseTLB::Translation interface */
+
+ /** Interface for ITLB responses. We can handle delay, so don't
+ * do anything */
+ void markDelayed() { }
+
+ /** Interface for ITLB responses. Populates self and then passes
+ * the request on to the ports' handleTLBResponse member
+ * function */
+ void finish(Fault fault_, RequestPtr request_, ThreadContext *tc,
+ BaseTLB::Mode mode);
+
+ public:
+ FetchRequest(Fetch1 &fetch_, InstId id_, TheISA::PCState pc_) :
+ SenderState(),
+ fetch(fetch_),
+ state(NotIssued),
+ id(id_),
+ packet(NULL),
+ request(),
+ pc(pc_),
+ fault(NoFault)
+ { }
+
+ ~FetchRequest();
+ };
+
+ typedef FetchRequest *FetchRequestPtr;
+
+ protected:
+ /** Construction-assigned data members */
+
+ /** Pointer back to the containing CPU */
+ MinorCPU &cpu;
+
+ /** Input port carrying branch requests from Execute */
+ Latch<BranchData>::Output inp;
+ /** Output port carrying read lines to Fetch2 */
+ Latch<ForwardLineData>::Input out;
+ /** Input port carrying branch predictions from Fetch2 */
+ Latch<BranchData>::Output prediction;
+
+ /** Interface to reserve space in the next stage */
+ Reservable &nextStageReserve;
+
+ /** IcachePort to pass to the CPU. Fetch1 is the only module that uses
+ * it. */
+ IcachePort icachePort;
+
+ /** Line snap size in bytes. All fetches clip to make their ends not
+ * extend beyond this limit. Setting this to the machine L1 cache line
+ * length will result in fetches never crossing line boundaries. */
+ unsigned int lineSnap;
+
+ /** Maximum fetch width in bytes. Setting this (and lineSnap) to the
+ * machine L1 cache line length will result in fetches of whole cache
+ * lines. Setting this to sizeof(MachInst) will result it fetches of
+ * single instructions (except near the end of lineSnap lines) */
+ unsigned int maxLineWidth;
+
+ /** Maximum number of fetches allowed in flight (in queues or memory) */
+ unsigned int fetchLimit;
+
+ protected:
+ /** Cycle-by-cycle state */
+
+ /** State of memory access for head instruction fetch */
+ enum FetchState
+ {
+ FetchHalted, /* Not fetching, waiting to be woken by transition
+ to FetchWaitingForPC. The PC is not valid in this state */
+ FetchWaitingForPC, /* Not fetching, waiting for stream change.
+ This doesn't stop issued fetches from being returned and
+ processed or for branches to change the state to Running. */
+ FetchRunning /* Try to fetch, when possible */
+ };
+
+ /** Stage cycle-by-cycle state */
+
+ FetchState state;
+
+ /** Fetch PC value. This is updated by branches from Execute, branch
+ * prediction targets from Fetch2 and by incrementing it as we fetch
+ * lines subsequent to those two sources. */
+ TheISA::PCState pc;
+
+ /** Stream sequence number. This changes on request from Execute and is
+ * used to tag instructions by the fetch stream to which they belong.
+ * Execute originates new prediction sequence numbers. */
+ InstSeqNum streamSeqNum;
+
+ /** Prediction sequence number. This changes when requests from Execute
+ * or Fetch2 ask for a change of fetch address and is used to tag lines
+ * by the prediction to which they belong. Fetch2 originates
+ * prediction sequence numbers. */
+ InstSeqNum predictionSeqNum;
+
+ /** The sequence number expected for the next returned cache line. The
+ * responses queue should be ordered and so, if the front of that queue
+ * has a lower lineSeqNum than this, lines need to be discarded. If it
+ * has a higher lineSeqNum, our line hasn't appeared yet */
+ InstSeqNum expectedLineSeqNum;
+
+ /** Blocked indication for report */
+ bool blocked;
+
+ /** State of memory access for head instruction fetch */
+ enum IcacheState
+ {
+ IcacheRunning, /* Default. Step icache queues when possible */
+ IcacheNeedsRetry /* Request rejected, will be asked to retry */
+ };
+
+ typedef Queue<FetchRequestPtr,
+ ReportTraitsPtrAdaptor<FetchRequestPtr>,
+ NoBubbleTraits<FetchRequestPtr> >
+ FetchQueue;
+
+ /** Queue of address translated requests from Fetch1 */
+ FetchQueue requests;
+
+ /** Queue of in-memory system requests and responses */
+ FetchQueue transfers;
+
+ /** Retry state of icache_port */
+ IcacheState icacheState;
+
+ /** Sequence number for line fetch used for ordering lines to flush */
+ InstSeqNum lineSeqNum;
+
+ /** Count of the number fetches which have left the transfers queue
+ * and are in the 'wild' in the memory system. Try not to rely on
+ * this value, it's better to code without knowledge of the number
+ * of outstanding accesses */
+ unsigned int numFetchesInMemorySystem;
+ /** Number of requests inside the ITLB rather than in the queues.
+ * All requests so located *must* have reserved space in the
+ * transfers queue */
+ unsigned int numFetchesInITLB;
+
+ protected:
+ friend std::ostream &operator <<(std::ostream &os,
+ Fetch1::FetchState state);
+
+ /** Start fetching from a new address. */
+ void changeStream(const BranchData &branch);
+
+ /** Update streamSeqNum and predictionSeqNum from the given branch (and
+ * assume these have changed and discard (on delivery) all lines in
+ * flight) */
+ void updateExpectedSeqNums(const BranchData &branch);
+
+ /** Convert a response to a ForwardLineData */
+ void processResponse(FetchRequestPtr response,
+ ForwardLineData &line);
+
+ friend std::ostream &operator <<(std::ostream &os,
+ IcacheState state);
+
+ /** Insert a line fetch into the requests. This can be a partial
+ * line request where the given address has a non-0 offset into a
+ * line. */
+ void fetchLine();
+
+ /** Try and issue a fetch for a translated request at the
+ * head of the requests queue. Also tries to move the request
+ * between queues */
+ void tryToSendToTransfers(FetchRequestPtr request);
+
+ /** Try to send (or resend) a memory request's next/only packet to
+ * the memory system. Returns true if the fetch was successfully
+ * sent to memory */
+ bool tryToSend(FetchRequestPtr request);
+
+ /** Move a request between queues */
+ void moveFromRequestsToTransfers(FetchRequestPtr request);
+
+ /** Step requests along between requests and transfers queues */
+ void stepQueues();
+
+ /** Pop a request from the given queue and correctly deallocate and
+ * discard it. */
+ void popAndDiscard(FetchQueue &queue);
+
+ /** Handle pushing a TLB response onto the right queue */
+ void handleTLBResponse(FetchRequestPtr response);
+
+ /** Returns the total number of queue occupancy, in-ITLB and
+ * in-memory system fetches */
+ unsigned int numInFlightFetches();
+
+ /** Print the appropriate MinorLine line for a fetch response */
+ void minorTraceResponseLine(const std::string &name,
+ FetchRequestPtr response) const;
+
+ /** Memory interface */
+ virtual bool recvTimingResp(PacketPtr pkt);
+ virtual void recvRetry();
+
+ public:
+ Fetch1(const std::string &name_,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<BranchData>::Output inp_,
+ Latch<ForwardLineData>::Input out_,
+ Latch<BranchData>::Output prediction_,
+ Reservable &next_stage_input_buffer);
+
+ public:
+ /** Returns the IcachePort owned by this Fetch1 */
+ MinorCPU::MinorCPUPort &getIcachePort() { return icachePort; }
+
+ /** Pass on input/buffer data to the output if you can */
+ void evaluate();
+
+ void minorTrace() const;
+
+ /** Is this stage drained? For Fetch1, draining is initiated by
+ * Execute signalling a branch with the reason HaltFetch */
+ bool isDrained();
+};
+
+}
+
+#endif /* __CPU_MINOR_FETCH1_HH__ */
diff --git a/src/cpu/minor/fetch2.cc b/src/cpu/minor/fetch2.cc
new file mode 100644
index 000000000..4827b75fc
--- /dev/null
+++ b/src/cpu/minor/fetch2.cc
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <string>
+
+#include "arch/decoder.hh"
+#include "arch/utility.hh"
+#include "cpu/minor/fetch2.hh"
+#include "cpu/minor/pipeline.hh"
+#include "cpu/pred/bpred_unit.hh"
+#include "debug/Branch.hh"
+#include "debug/Fetch.hh"
+#include "debug/MinorTrace.hh"
+
+namespace Minor
+{
+
+Fetch2::Fetch2(const std::string &name,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardLineData>::Output inp_,
+ Latch<BranchData>::Output branchInp_,
+ Latch<BranchData>::Input predictionOut_,
+ Latch<ForwardInstData>::Input out_,
+ Reservable &next_stage_input_buffer) :
+ Named(name),
+ cpu(cpu_),
+ inp(inp_),
+ branchInp(branchInp_),
+ predictionOut(predictionOut_),
+ out(out_),
+ nextStageReserve(next_stage_input_buffer),
+ outputWidth(params.decodeInputWidth),
+ processMoreThanOneInput(params.fetch2CycleInput),
+ branchPredictor(*params.branchPred),
+ inputBuffer(name + ".inputBuffer", "lines", params.fetch2InputBufferSize),
+ inputIndex(0),
+ pc(TheISA::PCState(0)),
+ havePC(false),
+ lastStreamSeqNum(InstId::firstStreamSeqNum),
+ fetchSeqNum(InstId::firstFetchSeqNum),
+ expectedStreamSeqNum(InstId::firstStreamSeqNum),
+ predictionSeqNum(InstId::firstPredictionSeqNum)
+{
+ if (outputWidth < 1)
+ fatal("%s: decodeInputWidth must be >= 1 (%d)\n", name, outputWidth);
+
+ if (params.fetch2InputBufferSize < 1) {
+ fatal("%s: fetch2InputBufferSize must be >= 1 (%d)\n", name,
+ params.fetch2InputBufferSize);
+ }
+}
+
+const ForwardLineData *
+Fetch2::getInput()
+{
+ /* Get a line from the inputBuffer to work with */
+ if (!inputBuffer.empty()) {
+ return &(inputBuffer.front());
+ } else {
+ return NULL;
+ }
+}
+
+void
+Fetch2::popInput()
+{
+ if (!inputBuffer.empty()) {
+ inputBuffer.front().freeLine();
+ inputBuffer.pop();
+ }
+
+ inputIndex = 0;
+}
+
+void
+Fetch2::dumpAllInput()
+{
+ DPRINTF(Fetch, "Dumping whole input buffer\n");
+ while (!inputBuffer.empty())
+ popInput();
+
+ inputIndex = 0;
+}
+
+void
+Fetch2::updateBranchPrediction(const BranchData &branch)
+{
+ MinorDynInstPtr inst = branch.inst;
+
+ /* Don't even consider instructions we didn't try to predict or faults */
+ if (inst->isFault() || !inst->triedToPredict)
+ return;
+
+ switch (branch.reason) {
+ case BranchData::NoBranch:
+ /* No data to update */
+ break;
+ case BranchData::Interrupt:
+ /* Never try to predict interrupts */
+ break;
+ case BranchData::SuspendThread:
+ /* Don't need to act on suspends */
+ break;
+ case BranchData::WakeupFetch:
+ /* Don't need to act on wakeups, no instruction tied to action. */
+ break;
+ case BranchData::HaltFetch:
+ /* Don't need to act on fetch wakeup */
+ break;
+ case BranchData::BranchPrediction:
+ /* Shouldn't happen. Fetch2 is the only source of
+ * BranchPredictions */
+ break;
+ case BranchData::UnpredictedBranch:
+ /* Unpredicted branch or barrier */
+ DPRINTF(Branch, "Unpredicted branch seen inst: %s\n", *inst);
+ branchPredictor.squash(inst->id.fetchSeqNum,
+ branch.target, true, inst->id.threadId);
+ break;
+ case BranchData::CorrectlyPredictedBranch:
+ /* Predicted taken, was taken */
+ DPRINTF(Branch, "Branch predicted correctly inst: %s\n", *inst);
+ branchPredictor.update(inst->id.fetchSeqNum,
+ inst->id.threadId);
+ break;
+ case BranchData::BadlyPredictedBranch:
+ /* Predicted taken, not taken */
+ DPRINTF(Branch, "Branch mis-predicted inst: %s\n", *inst);
+ branchPredictor.squash(inst->id.fetchSeqNum,
+ branch.target /* Not used */, false, inst->id.threadId);
+ break;
+ case BranchData::BadlyPredictedBranchTarget:
+ /* Predicted taken, was taken but to a different target */
+ DPRINTF(Branch, "Branch mis-predicted target inst: %s target: %s\n",
+ *inst, branch.target);
+ branchPredictor.squash(inst->id.fetchSeqNum,
+ branch.target, true, inst->id.threadId);
+ break;
+ }
+}
+
+void
+Fetch2::predictBranch(MinorDynInstPtr inst, BranchData &branch)
+{
+ TheISA::PCState inst_pc = inst->pc;
+
+ assert(!inst->predictedTaken);
+
+ /* Skip non-control/sys call instructions */
+ if (inst->staticInst->isControl() ||
+ inst->staticInst->isSyscall())
+ {
+ /* Tried to predict */
+ inst->triedToPredict = true;
+
+ DPRINTF(Branch, "Trying to predict for inst: %s\n", *inst);
+
+ if (branchPredictor.predict(inst->staticInst,
+ inst->id.fetchSeqNum, inst_pc,
+ inst->id.threadId))
+ {
+ inst->predictedTaken = true;
+ inst->predictedTarget = inst_pc;
+ branch.target = inst_pc;
+ }
+ } else {
+ DPRINTF(Branch, "Not attempting prediction for inst: %s\n", *inst);
+ }
+
+ /* If we predict taken, set branch and update sequence numbers */
+ if (inst->predictedTaken) {
+ /* Update the predictionSeqNum and remember the streamSeqNum that it
+ * was associated with */
+ expectedStreamSeqNum = inst->id.streamSeqNum;
+
+ BranchData new_branch = BranchData(BranchData::BranchPrediction,
+ inst->id.streamSeqNum, predictionSeqNum + 1,
+ inst->predictedTarget, inst);
+
+ /* Mark with a new prediction number by the stream number of the
+ * instruction causing the prediction */
+ predictionSeqNum++;
+ branch = new_branch;
+
+ DPRINTF(Branch, "Branch predicted taken inst: %s target: %s"
+ " new predictionSeqNum: %d\n",
+ *inst, inst->predictedTarget, predictionSeqNum);
+ }
+}
+
+void
+Fetch2::evaluate()
+{
+ inputBuffer.setTail(*inp.outputWire);
+ ForwardInstData &insts_out = *out.inputWire;
+ BranchData prediction;
+ BranchData &branch_inp = *branchInp.outputWire;
+
+ assert(insts_out.isBubble());
+
+ blocked = false;
+
+ /* React to branches from Execute to update local branch prediction
+ * structures */
+ updateBranchPrediction(branch_inp);
+
+ /* If a branch arrives, don't try and do anything about it. Only
+ * react to your own predictions */
+ if (branch_inp.isStreamChange()) {
+ DPRINTF(Fetch, "Dumping all input as a stream changing branch"
+ " has arrived\n");
+ dumpAllInput();
+ havePC = false;
+ }
+
+ /* Even when blocked, clear out input lines with the wrong
+ * prediction sequence number */
+ {
+ const ForwardLineData *line_in = getInput();
+
+ while (line_in &&
+ expectedStreamSeqNum == line_in->id.streamSeqNum &&
+ predictionSeqNum != line_in->id.predictionSeqNum)
+ {
+ DPRINTF(Fetch, "Discarding line %s"
+ " due to predictionSeqNum mismatch (expected: %d)\n",
+ line_in->id, predictionSeqNum);
+
+ popInput();
+ havePC = false;
+
+ if (processMoreThanOneInput) {
+ DPRINTF(Fetch, "Wrapping\n");
+ line_in = getInput();
+ } else {
+ line_in = NULL;
+ }
+ }
+ }
+
+ if (!nextStageReserve.canReserve()) {
+ blocked = true;
+ } else {
+ const ForwardLineData *line_in = getInput();
+
+ unsigned int output_index = 0;
+
+ /* Pack instructions into the output while we can. This may involve
+ * using more than one input line. Note that lineWidth will be 0
+ * for faulting lines */
+ while (line_in &&
+ (line_in->isFault() ||
+ inputIndex < line_in->lineWidth) && /* More input */
+ output_index < outputWidth && /* More output to fill */
+ prediction.isBubble() /* No predicted branch */)
+ {
+ ThreadContext *thread = cpu.getContext(line_in->id.threadId);
+ TheISA::Decoder *decoder = thread->getDecoderPtr();
+
+ /* Discard line due to prediction sequence number being wrong but
+ * without the streamSeqNum number having changed */
+ bool discard_line =
+ expectedStreamSeqNum == line_in->id.streamSeqNum &&
+ predictionSeqNum != line_in->id.predictionSeqNum;
+
+ /* Set the PC if the stream changes. Setting havePC to false in
+ * a previous cycle handles all other change of flow of control
+ * issues */
+ bool set_pc = lastStreamSeqNum != line_in->id.streamSeqNum;
+
+ if (!discard_line && (!havePC || set_pc)) {
+ /* Set the inputIndex to be the MachInst-aligned offset
+ * from lineBaseAddr of the new PC value */
+ inputIndex =
+ (line_in->pc.instAddr() & BaseCPU::PCMask) -
+ line_in->lineBaseAddr;
+ DPRINTF(Fetch, "Setting new PC value: %s inputIndex: 0x%x"
+ " lineBaseAddr: 0x%x lineWidth: 0x%x\n",
+ line_in->pc, inputIndex, line_in->lineBaseAddr,
+ line_in->lineWidth);
+ pc = line_in->pc;
+ havePC = true;
+ decoder->reset();
+ }
+
+ /* The generated instruction. Leave as NULL if no instruction
+ * is to be packed into the output */
+ MinorDynInstPtr dyn_inst = NULL;
+
+ if (discard_line) {
+ /* Rest of line was from an older prediction in the same
+ * stream */
+ DPRINTF(Fetch, "Discarding line %s (from inputIndex: %d)"
+ " due to predictionSeqNum mismatch (expected: %d)\n",
+ line_in->id, inputIndex, predictionSeqNum);
+ } else if (line_in->isFault()) {
+ /* Pack a fault as a MinorDynInst with ->fault set */
+
+ /* Make a new instruction and pick up the line, stream,
+ * prediction, thread ids from the incoming line */
+ dyn_inst = new MinorDynInst(line_in->id);
+
+ /* Fetch and prediction sequence numbers originate here */
+ dyn_inst->id.fetchSeqNum = fetchSeqNum;
+ dyn_inst->id.predictionSeqNum = predictionSeqNum;
+ /* To complete the set, test that exec sequence number has
+ * not been set */
+ assert(dyn_inst->id.execSeqNum == 0);
+
+ dyn_inst->pc = pc;
+
+ /* Pack a faulting instruction but allow other
+ * instructions to be generated. (Fetch2 makes no
+ * immediate judgement about streamSeqNum) */
+ dyn_inst->fault = line_in->fault;
+ DPRINTF(Fetch, "Fault being passed output_index: "
+ "%d: %s\n", output_index, dyn_inst->fault->name());
+ } else {
+ uint8_t *line = line_in->line;
+
+ TheISA::MachInst inst_word;
+ /* The instruction is wholly in the line, can just
+ * assign */
+ inst_word = TheISA::gtoh(
+ *(reinterpret_cast<TheISA::MachInst *>
+ (line + inputIndex)));
+
+ if (!decoder->instReady()) {
+ decoder->moreBytes(pc,
+ line_in->lineBaseAddr + inputIndex, inst_word);
+ DPRINTF(Fetch, "Offering MachInst to decoder"
+ " addr: 0x%x\n", line_in->lineBaseAddr + inputIndex);
+ }
+
+ /* Maybe make the above a loop to accomodate ISAs with
+ * instructions longer than sizeof(MachInst) */
+
+ if (decoder->instReady()) {
+ /* Make a new instruction and pick up the line, stream,
+ * prediction, thread ids from the incoming line */
+ dyn_inst = new MinorDynInst(line_in->id);
+
+ /* Fetch and prediction sequence numbers originate here */
+ dyn_inst->id.fetchSeqNum = fetchSeqNum;
+ dyn_inst->id.predictionSeqNum = predictionSeqNum;
+ /* To complete the set, test that exec sequence number
+ * has not been set */
+ assert(dyn_inst->id.execSeqNum == 0);
+
+ /* Note that the decoder can update the given PC.
+ * Remember not to assign it until *after* calling
+ * decode */
+ StaticInstPtr decoded_inst = decoder->decode(pc);
+ dyn_inst->staticInst = decoded_inst;
+
+ dyn_inst->pc = pc;
+
+ DPRINTF(Fetch, "Instruction extracted from line %s"
+ " lineWidth: %d output_index: %d inputIndex: %d"
+ " pc: %s inst: %s\n",
+ line_in->id,
+ line_in->lineWidth, output_index, inputIndex,
+ pc, *dyn_inst);
+
+#if THE_ISA == X86_ISA || THE_ISA == ARM_ISA
+ /* In SE mode, it's possible to branch to a microop when
+ * replaying faults such as page faults (or simply
+ * intra-microcode branches in X86). Unfortunately,
+ * as Minor has micro-op decomposition in a separate
+ * pipeline stage from instruction decomposition, the
+ * following advancePC (which may follow a branch with
+ * microPC() != 0) *must* see a fresh macroop. This
+ * kludge should be improved with an addition to PCState
+ * but I offer it in this form for the moment
+ *
+ * X86 can branch within microops so we need to deal with
+ * the case that, after a branch, the first un-advanced PC
+ * may be pointing to a microop other than 0. Once
+ * advanced, however, the microop number *must* be 0 */
+ pc.upc(0);
+ pc.nupc(1);
+#endif
+
+ /* Advance PC for the next instruction */
+ TheISA::advancePC(pc, decoded_inst);
+
+ /* Predict any branches and issue a branch if
+ * necessary */
+ predictBranch(dyn_inst, prediction);
+ } else {
+ DPRINTF(Fetch, "Inst not ready yet\n");
+ }
+
+ /* Step on the pointer into the line if there's no
+ * complete instruction waiting */
+ if (decoder->needMoreBytes()) {
+ inputIndex += sizeof(TheISA::MachInst);
+
+ DPRINTF(Fetch, "Updated inputIndex value PC: %s"
+ " inputIndex: 0x%x lineBaseAddr: 0x%x lineWidth: 0x%x\n",
+ line_in->pc, inputIndex, line_in->lineBaseAddr,
+ line_in->lineWidth);
+ }
+ }
+
+ if (dyn_inst) {
+ /* Step to next sequence number */
+ fetchSeqNum++;
+
+ /* Correctly size the output before writing */
+ if (output_index == 0)
+ insts_out.resize(outputWidth);
+ /* Pack the generated dynamic instruction into the output */
+ insts_out.insts[output_index] = dyn_inst;
+ output_index++;
+
+ /* Output MinorTrace instruction info for
+ * pre-microop decomposition macroops */
+ if (DTRACE(MinorTrace) && !dyn_inst->isFault() &&
+ dyn_inst->staticInst->isMacroop())
+ {
+ dyn_inst->minorTraceInst(*this);
+ }
+ }
+
+ /* Remember the streamSeqNum of this line so we can tell when
+ * we change stream */
+ lastStreamSeqNum = line_in->id.streamSeqNum;
+
+ /* Asked to discard line or there was a branch or fault */
+ if (!prediction.isBubble() || /* The remains of a
+ line with a prediction in it */
+ line_in->isFault() /* A line which is just a fault */)
+ {
+ DPRINTF(Fetch, "Discarding all input on branch/fault\n");
+ dumpAllInput();
+ havePC = false;
+ line_in = NULL;
+ } else if (discard_line) {
+ /* Just discard one line, one's behind it may have new
+ * stream sequence numbers. There's a DPRINTF above
+ * for this event */
+ popInput();
+ havePC = false;
+ line_in = NULL;
+ } else if (inputIndex == line_in->lineWidth) {
+ /* Got to end of a line, pop the line but keep PC
+ * in case this is a line-wrapping inst. */
+ popInput();
+ line_in = NULL;
+ }
+
+ if (!line_in && processMoreThanOneInput) {
+ DPRINTF(Fetch, "Wrapping\n");
+ line_in = getInput();
+ }
+ }
+
+ /* The rest of the output (if any) should already have been packed
+ * with bubble instructions by insts_out's initialisation */
+ }
+
+ /** Reserve a slot in the next stage and output data */
+ *predictionOut.inputWire = prediction;
+
+ /* If we generated output, reserve space for the result in the next stage
+ * and mark the stage as being active this cycle */
+ if (!insts_out.isBubble()) {
+ /* Note activity of following buffer */
+ cpu.activityRecorder->activity();
+ nextStageReserve.reserve();
+ }
+
+ /* If we still have input to process and somewhere to put it,
+ * mark stage as active */
+ if (getInput() && nextStageReserve.canReserve())
+ cpu.activityRecorder->activateStage(Pipeline::Fetch2StageId);
+
+ /* Make sure the input (if any left) is pushed */
+ inputBuffer.pushTail();
+}
+
+bool
+Fetch2::isDrained()
+{
+ return inputBuffer.empty() &&
+ (*inp.outputWire).isBubble() &&
+ (*predictionOut.inputWire).isBubble();
+}
+
+void
+Fetch2::minorTrace() const
+{
+ std::ostringstream data;
+
+ if (blocked)
+ data << 'B';
+ else
+ (*out.inputWire).reportData(data);
+
+ MINORTRACE("inputIndex=%d havePC=%d predictionSeqNum=%d insts=%s\n",
+ inputIndex, havePC, predictionSeqNum, data.str());
+ inputBuffer.minorTrace();
+}
+
+}
diff --git a/src/cpu/minor/fetch2.hh b/src/cpu/minor/fetch2.hh
new file mode 100644
index 000000000..2fc38b377
--- /dev/null
+++ b/src/cpu/minor/fetch2.hh
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Fetch2 receives lines of data from Fetch1, separates them into
+ * instructions and passes them to Decode
+ */
+
+#ifndef __CPU_MINOR_FETCH2_HH__
+#define __CPU_MINOR_FETCH2_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/pipe_data.hh"
+#include "cpu/pred/bpred_unit.hh"
+#include "params/MinorCPU.hh"
+
+namespace Minor
+{
+
+/** This stage receives lines of data from Fetch1, separates them into
+ * instructions and passes them to Decode */
+class Fetch2 : public Named
+{
+ protected:
+ /** Pointer back to the containing CPU */
+ MinorCPU &cpu;
+
+ /** Input port carrying lines from Fetch1 */
+ Latch<ForwardLineData>::Output inp;
+
+ /** Input port carrying branches from Execute. This is a snoop of the
+ * data provided to F1. */
+ Latch<BranchData>::Output branchInp;
+
+ /** Output port carrying predictions back to Fetch1 */
+ Latch<BranchData>::Input predictionOut;
+
+ /** Output port carrying instructions into Decode */
+ Latch<ForwardInstData>::Input out;
+
+ /** Interface to reserve space in the next stage */
+ Reservable &nextStageReserve;
+
+ /** Width of output of this stage/input of next in instructions */
+ unsigned int outputWidth;
+
+ /** If true, more than one input word can be processed each cycle if
+ * there is room in the output to contain its processed data */
+ bool processMoreThanOneInput;
+
+ /** Branch predictor passed from Python configuration */
+ BPredUnit &branchPredictor;
+
+ public:
+ /* Public so that Pipeline can pass it to Fetch1 */
+ InputBuffer<ForwardLineData> inputBuffer;
+
+ protected:
+ /** Data members after this line are cycle-to-cycle state */
+
+ /** Index into an incompletely processed input line that instructions
+ * are to be extracted from */
+ unsigned int inputIndex;
+
+ /** Remembered program counter value. Between contiguous lines, this
+ * is just updated with advancePC. For lines following changes of
+ * stream, a new PC must be loaded and havePC be set.
+ * havePC is needed to accomodate instructions which span across
+ * lines meaning that Fetch2 and the decoder need to remember a PC
+ * value and a partially-offered instruction from the previous line */
+ TheISA::PCState pc;
+
+ /** PC is currently valid. Initially false, gets set to true when a
+ * change-of-stream line is received and false again when lines are
+ * discarded for any reason */
+ bool havePC;
+
+ /** Stream sequence number of the last seen line used to identify changes
+ * of instruction stream */
+ InstSeqNum lastStreamSeqNum;
+
+ /** Fetch2 is the source of fetch sequence numbers. These represent the
+ * sequence that instructions were extracted from fetched lines. */
+ InstSeqNum fetchSeqNum;
+
+ /** Stream sequence number remembered from last time the predictionSeqNum
+ * changed. Lines should only be discarded when their predictionSeqNums
+ * disagree with Fetch2::predictionSeqNum *and* they are from the same
+ * stream that bore that prediction number */
+ InstSeqNum expectedStreamSeqNum;
+
+ /** Fetch2 is the source of prediction sequence numbers. These represent
+ * predicted changes of control flow sources from branch prediction in
+ * Fetch2. */
+ InstSeqNum predictionSeqNum;
+
+ /** Blocked indication for report */
+ bool blocked;
+
+ protected:
+ /** Get a piece of data to work on from the inputBuffer, or 0 if there
+ * is no data. */
+ const ForwardLineData *getInput();
+
+ /** Pop an element off the input buffer, if there are any */
+ void popInput();
+
+ /** Dump the whole contents of the input buffer. Useful after a
+ * prediction changes control flow */
+ void dumpAllInput();
+
+ /** Update local branch prediction structures from feedback from
+ * Execute. */
+ void updateBranchPrediction(const BranchData &branch);
+
+ /** Predicts branches for the given instruction. Updates the
+ * instruction's predicted... fields and also the branch which
+ * carries the prediction to Fetch1 */
+ void predictBranch(MinorDynInstPtr inst, BranchData &branch);
+
+ public:
+ Fetch2(const std::string &name,
+ MinorCPU &cpu_,
+ MinorCPUParams &params,
+ Latch<ForwardLineData>::Output inp_,
+ Latch<BranchData>::Output branchInp_,
+ Latch<BranchData>::Input predictionOut_,
+ Latch<ForwardInstData>::Input out_,
+ Reservable &next_stage_input_buffer);
+
+ public:
+ /** Pass on input/buffer data to the output if you can */
+ void evaluate();
+
+ void minorTrace() const;
+
+ /** Is this stage drained? For Fetch2, draining is initiated by
+ * Execute halting Fetch1 causing Fetch2 to naturally drain.
+ * Branch predictions are ignored by Fetch1 during halt */
+ bool isDrained();
+};
+
+}
+
+#endif /* __CPU_MINOR_FETCH2_HH__ */
diff --git a/src/cpu/minor/func_unit.cc b/src/cpu/minor/func_unit.cc
new file mode 100644
index 000000000..1a75c4aa8
--- /dev/null
+++ b/src/cpu/minor/func_unit.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <iomanip>
+#include <sstream>
+#include <typeinfo>
+
+#include "cpu/minor/func_unit.hh"
+#include "debug/MinorTiming.hh"
+#include "enums/OpClass.hh"
+
+MinorOpClass *
+MinorOpClassParams::create()
+{
+ return new MinorOpClass(this);
+}
+
+MinorOpClassSet *
+MinorOpClassSetParams::create()
+{
+ return new MinorOpClassSet(this);
+}
+
+MinorFUTiming *
+MinorFUTimingParams::create()
+{
+ return new MinorFUTiming(this);
+}
+
+MinorFU *
+MinorFUParams::create()
+{
+ return new MinorFU(this);
+}
+
+MinorFUPool *
+MinorFUPoolParams::create()
+{
+ return new MinorFUPool(this);
+}
+
+MinorOpClassSet::MinorOpClassSet(const MinorOpClassSetParams *params) :
+ SimObject(params),
+ opClasses(params->opClasses),
+ /* Initialise to true for an empty list so that 'fully capable' is
+ * the default */
+ capabilityList(Num_OpClasses, (opClasses.empty() ? true : false))
+{
+ for (unsigned int i = 0; i < opClasses.size(); i++)
+ capabilityList[opClasses[i]->opClass] = true;
+}
+
+MinorFUTiming::MinorFUTiming(
+ const MinorFUTimingParams *params) :
+ SimObject(params),
+ mask(params->mask),
+ match(params->match),
+ description(params->description),
+ suppress(params->suppress),
+ extraCommitLat(params->extraCommitLat),
+ extraCommitLatExpr(params->extraCommitLatExpr),
+ extraAssumedLat(params->extraAssumedLat),
+ srcRegsRelativeLats(params->srcRegsRelativeLats),
+ opClasses(params->opClasses)
+{ }
+
+namespace Minor
+{
+
+void
+QueuedInst::reportData(std::ostream &os) const
+{
+ inst->reportData(os);
+}
+
+FUPipeline::FUPipeline(const std::string &name, const MinorFU &description_,
+ ClockedObject &timeSource_) :
+ FUPipelineBase(name, "insts", description_.opLat),
+ description(description_),
+ timeSource(timeSource_),
+ nextInsertCycle(Cycles(0))
+{
+ /* Issue latencies are set to 1 in calls to addCapability here.
+ * Issue latencies are associated with the pipeline as a whole,
+ * rather than instruction classes in Minor */
+
+ /* All pipelines should be able to execute No_OpClass instructions */
+ addCapability(No_OpClass, description.opLat, 1);
+
+ /* Add the capabilities listed in the MinorFU for this functional unit */
+ for (unsigned int i = 0; i < description.opClasses->opClasses.size();
+ i++)
+ {
+ addCapability(description.opClasses->opClasses[i]->opClass,
+ description.opLat, 1);
+ }
+
+ for (unsigned int i = 0; i < description.timings.size(); i++) {
+ MinorFUTiming &timing = *(description.timings[i]);
+
+ if (DTRACE(MinorTiming)) {
+ std::ostringstream lats;
+
+ unsigned int num_lats = timing.srcRegsRelativeLats.size();
+ unsigned int j = 0;
+ while (j < num_lats) {
+ lats << timing.srcRegsRelativeLats[j];
+
+ j++;
+ if (j != num_lats)
+ lats << ',';
+ }
+
+ DPRINTFS(MinorTiming, static_cast<Named *>(this),
+ "Adding extra timing decode pattern %d to FU"
+ " mask: %016x match: %016x srcRegLatencies: %s\n",
+ i, timing.mask, timing.match, lats.str());
+ }
+ }
+
+ const std::vector<unsigned> &cant_forward =
+ description.cantForwardFromFUIndices;
+
+ /* Setup the bit vector cantForward... with the set indices
+ * specified in the parameters */
+ for (auto i = cant_forward.begin(); i != cant_forward.end(); ++i) {
+ cantForwardFromFUIndices.resize((*i) + 1, false);
+ cantForwardFromFUIndices[*i] = true;
+ }
+}
+
+Cycles
+FUPipeline::cyclesBeforeInsert()
+{
+ if (nextInsertCycle == 0 || timeSource.curCycle() > nextInsertCycle)
+ return Cycles(0);
+ else
+ return nextInsertCycle - timeSource.curCycle();
+}
+
+bool
+FUPipeline::canInsert() const
+{
+ return nextInsertCycle == 0 || timeSource.curCycle() >= nextInsertCycle;
+}
+
+void
+FUPipeline::advance()
+{
+ bool was_stalled = stalled;
+
+ /* If an instruction was pushed into the pipeline, set the delay before
+ * the next instruction can follow */
+ if (alreadyPushed()) {
+ if (nextInsertCycle <= timeSource.curCycle()) {
+ nextInsertCycle = timeSource.curCycle() + description.issueLat;
+ }
+ } else if (was_stalled && nextInsertCycle != 0) {
+ /* Don't count stalled cycles as part of the issue latency */
+ ++nextInsertCycle;
+ }
+ FUPipelineBase::advance();
+}
+
+MinorFUTiming *
+FUPipeline::findTiming(StaticInstPtr inst)
+{
+#if THE_ISA == ARM_ISA
+ /* This should work for any ISA with a POD mach_inst */
+ TheISA::ExtMachInst mach_inst = inst->machInst;
+#else
+ /* Just allow extra decode based on op classes */
+ uint64_t mach_inst = 0;
+#endif
+
+ const std::vector<MinorFUTiming *> &timings =
+ description.timings;
+ unsigned int num_timings = timings.size();
+
+ for (unsigned int i = 0; i < num_timings; i++) {
+ MinorFUTiming &timing = *timings[i];
+
+ if (timing.provides(inst->opClass()) &&
+ (mach_inst & timing.mask) == timing.match)
+ {
+ DPRINTFS(MinorTiming, static_cast<Named *>(this),
+ "Found extra timing match (pattern %d '%s')"
+ " %s %16x (type %s)\n",
+ i, timing.description, inst->disassemble(0), mach_inst,
+ typeid(*inst).name());
+
+ return &timing;
+ }
+ }
+
+ if (num_timings != 0) {
+ DPRINTFS(MinorTiming, static_cast<Named *>(this),
+ "No extra timing info. found for inst: %s"
+ " mach_inst: %16x\n",
+ inst->disassemble(0), mach_inst);
+ }
+
+ return NULL;
+}
+
+}
diff --git a/src/cpu/minor/func_unit.hh b/src/cpu/minor/func_unit.hh
new file mode 100644
index 000000000..34da579b6
--- /dev/null
+++ b/src/cpu/minor/func_unit.hh
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Execute function unit descriptions and pipeline implementations.
+ */
+
+#ifndef __CPU_MINOR_FUNC_UNIT_HH__
+#define __CPU_MINOR_FUNC_UNIT_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/func_unit.hh"
+#include "cpu/timing_expr.hh"
+#include "params/MinorFU.hh"
+#include "params/MinorFUPool.hh"
+#include "params/MinorOpClass.hh"
+#include "params/MinorOpClassSet.hh"
+#include "sim/clocked_object.hh"
+
+/** Boxing for MinorOpClass to get around a build problem with C++11 but
+ * also allow for future additions to op class checking */
+class MinorOpClass : public SimObject
+{
+ public:
+ OpClass opClass;
+
+ public:
+ MinorOpClass(const MinorOpClassParams *params) :
+ SimObject(params),
+ opClass(params->opClass)
+ { }
+};
+
+/** Wrapper for a matchable set of op classes */
+class MinorOpClassSet : public SimObject
+{
+ public:
+ std::vector<MinorOpClass *> opClasses;
+
+ /** Convenience packing of opClasses into a bit vector for easier
+ * testing */
+ std::vector<bool> capabilityList;
+
+ public:
+ MinorOpClassSet(const MinorOpClassSetParams *params);
+
+ public:
+ /** Does this set support the given op class */
+ bool provides(OpClass op_class) { return capabilityList[op_class]; }
+};
+
+/** Extra timing capability to allow individual ops to have their source
+ * register dependency latencies tweaked based on the ExtMachInst of the
+ * source instruction.
+ */
+class MinorFUTiming: public SimObject
+{
+ public:
+ /** Mask off the ExtMachInst of an instruction before comparing with
+ * match */
+ uint64_t mask;
+ uint64_t match;
+
+ /** Textual description of the decode's purpose */
+ std::string description;
+
+ /** If true, instructions matching this mask/match should *not* be
+ * issued in this FU */
+ bool suppress;
+
+ /** Extra latency that the instruction should spend at the end of
+ * the pipeline */
+ Cycles extraCommitLat;
+ TimingExpr *extraCommitLatExpr;
+
+ /** Extra delay that results should show in the scoreboard after
+ * leaving the pipeline. If set to Cycles(0) for memory references,
+ * an 'unpredictable' return time will be set in the scoreboard
+ * blocking following dependent instructions from issuing */
+ Cycles extraAssumedLat;
+
+ /** Cycle offsets from the scoreboard delivery times of register values
+ * for each of this instruction's source registers (in srcRegs order).
+ * The offsets are subtracted from the scoreboard returnCycle times.
+ * For example, for an instruction type with 3 source registers,
+ * [2, 1, 2] will allow the instruction to issue upto 2 cycles early
+ * for dependencies on the 1st and 3rd register and upto 1 cycle early
+ * on the 2nd. */
+ std::vector<Cycles> srcRegsRelativeLats;
+
+ /** Extra opClasses check (after the FU one) */
+ MinorOpClassSet *opClasses;
+
+ public:
+ MinorFUTiming(const MinorFUTimingParams *params);
+
+ public:
+ /** Does the extra decode in this object support the given op class */
+ bool provides(OpClass op_class) { return opClasses->provides(op_class); }
+};
+
+/** A functional unit that can execute any of opClasses operations with a
+ * single op(eration)Lat(ency) and issueLat(ency) associated with the unit
+ * rather than each operation (as in src/FuncUnit).
+ *
+ * This is very similar to cpu/func_unit but replicated here to allow
+ * the Minor functional units to change without having to disturb the common
+ * definition.
+ */
+class MinorFU : public SimObject
+{
+ public:
+ MinorOpClassSet *opClasses;
+
+ /** Delay from issuing the operation, to it reaching the
+ * end of the associated pipeline */
+ Cycles opLat;
+
+ /** Delay after issuing an operation before the next
+ * operation can be issued */
+ Cycles issueLat;
+
+ /** FUs which this pipeline can't receive a forwarded (i.e. relative
+ * latency != 0) result from */
+ std::vector<unsigned int> cantForwardFromFUIndices;
+
+ /** Extra timing info to give timings to individual ops */
+ std::vector<MinorFUTiming *> timings;
+
+ public:
+ MinorFU(const MinorFUParams *params) :
+ SimObject(params),
+ opClasses(params->opClasses),
+ opLat(params->opLat),
+ issueLat(params->issueLat),
+ cantForwardFromFUIndices(params->cantForwardFromFUIndices),
+ timings(params->timings)
+ { }
+};
+
+/** A collection of MinorFUs */
+class MinorFUPool : public SimObject
+{
+ public:
+ std::vector<MinorFU *> funcUnits;
+
+ public:
+ MinorFUPool(const MinorFUPoolParams *params) :
+ SimObject(params),
+ funcUnits(params->funcUnits)
+ { }
+};
+
+namespace Minor
+{
+
+/** Container class to box instructions in the FUs to make those
+ * queues have correct bubble behaviour when stepped */
+class QueuedInst
+{
+ public:
+ MinorDynInstPtr inst;
+
+ public:
+ QueuedInst(MinorDynInstPtr inst_ = MinorDynInst::bubble()) :
+ inst(inst_)
+ { }
+
+ public:
+ /** Report and bubble interfaces */
+ void reportData(std::ostream &os) const;
+ bool isBubble() const { return inst->isBubble(); }
+
+ static QueuedInst bubble()
+ { return QueuedInst(MinorDynInst::bubble()); }
+};
+
+/** Functional units have pipelines which stall when an inst gets to
+ * their ends allowing Execute::commit to pick up timing-completed insts
+ * when it feels like it */
+typedef SelfStallingPipeline<QueuedInst,
+ ReportTraitsAdaptor<QueuedInst> > FUPipelineBase;
+
+/** A functional unit configured from a MinorFU object */
+class FUPipeline : public FUPipelineBase, public FuncUnit
+{
+ public:
+ /** Functional unit description that this pipeline implements */
+ const MinorFU &description;
+
+ /** An FUPipeline needs access to curCycle, use this timing source */
+ ClockedObject &timeSource;
+
+ /** Set of operation classes supported by this FU */
+ std::bitset<Num_OpClasses> capabilityList;
+
+ /** FUs which this pipeline can't receive a forwarded (i.e. relative
+ * latency != 0) result from */
+ std::vector<bool> cantForwardFromFUIndices;
+
+ public:
+ /** When can a new instruction be inserted into the pipeline? This is
+ * an absolute cycle time unless it is 0 in which case the an
+ * instruction can be pushed straightaway */
+ Cycles nextInsertCycle;
+
+ public:
+ FUPipeline(const std::string &name, const MinorFU &description_,
+ ClockedObject &timeSource_);
+
+ public:
+ /** How many cycles must from curCycle before insertion into the
+ * pipeline is allowed */
+ Cycles cyclesBeforeInsert();
+
+ /** Can an instruction be inserted now? */
+ bool canInsert() const;
+
+ /** Find the extra timing information for this instruction. Returns
+ * NULL if no decode info. is found */
+ MinorFUTiming *findTiming(StaticInstPtr inst);
+
+ /** Step the pipeline. Allow multiple steps? */
+ void advance();
+};
+
+}
+
+#endif /* __CPU_MINOR_FUNC_UNIT_HH__ */
diff --git a/src/cpu/minor/lsq.cc b/src/cpu/minor/lsq.cc
new file mode 100644
index 000000000..c5e38c78d
--- /dev/null
+++ b/src/cpu/minor/lsq.cc
@@ -0,0 +1,1614 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#include "arch/locked_mem.hh"
+#include "arch/mmapped_ipr.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/exec_context.hh"
+#include "cpu/minor/execute.hh"
+#include "cpu/minor/lsq.hh"
+#include "cpu/minor/pipeline.hh"
+#include "debug/Activity.hh"
+#include "debug/MinorMem.hh"
+
+namespace Minor
+{
+
+/** Returns the offset of addr into an aligned a block of size block_size */
+static Addr
+addrBlockOffset(Addr addr, unsigned int block_size)
+{
+ return addr & (block_size - 1);
+}
+
+/** Returns true if the given [addr .. addr+size-1] transfer needs to be
+ * fragmented across a block size of block_size */
+static bool
+transferNeedsBurst(Addr addr, unsigned int size, unsigned int block_size)
+{
+ return (addrBlockOffset(addr, block_size) + size) > block_size;
+}
+
+LSQ::LSQRequest::LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
+ PacketDataPtr data_, uint64_t *res_) :
+ SenderState(),
+ port(port_),
+ inst(inst_),
+ isLoad(isLoad_),
+ data(data_),
+ packet(NULL),
+ request(),
+ fault(NoFault),
+ res(res_),
+ skipped(false),
+ issuedToMemory(false),
+ state(NotIssued)
+{ }
+
+LSQ::AddrRangeCoverage
+LSQ::LSQRequest::containsAddrRangeOf(
+ Addr req1_addr, unsigned int req1_size,
+ Addr req2_addr, unsigned int req2_size)
+{
+ /* 'end' here means the address of the byte just past the request
+ * blocks */
+ Addr req2_end_addr = req2_addr + req2_size;
+ Addr req1_end_addr = req1_addr + req1_size;
+
+ AddrRangeCoverage ret;
+
+ if (req1_addr > req2_end_addr || req1_end_addr < req2_addr)
+ ret = NoAddrRangeCoverage;
+ else if (req1_addr <= req2_addr && req1_end_addr >= req2_end_addr)
+ ret = FullAddrRangeCoverage;
+ else
+ ret = PartialAddrRangeCoverage;
+
+ return ret;
+}
+
+LSQ::AddrRangeCoverage
+LSQ::LSQRequest::containsAddrRangeOf(LSQRequestPtr other_request)
+{
+ return containsAddrRangeOf(request.getPaddr(), request.getSize(),
+ other_request->request.getPaddr(), other_request->request.getSize());
+}
+
+bool
+LSQ::LSQRequest::isBarrier()
+{
+ return inst->isInst() && inst->staticInst->isMemBarrier();
+}
+
+bool
+LSQ::LSQRequest::needsToBeSentToStoreBuffer()
+{
+ return state == StoreToStoreBuffer;
+}
+
+void
+LSQ::LSQRequest::setState(LSQRequestState new_state)
+{
+ DPRINTFS(MinorMem, (&port), "Setting state from %d to %d for request:"
+ " %s\n", state, new_state, *inst);
+ state = new_state;
+}
+
+bool
+LSQ::LSQRequest::isComplete() const
+{
+ /* @todo, There is currently only one 'completed' state. This
+ * may not be a good choice */
+ return state == Complete;
+}
+
+void
+LSQ::LSQRequest::reportData(std::ostream &os) const
+{
+ os << (isLoad ? 'R' : 'W') << ';';
+ inst->reportData(os);
+ os << ';' << state;
+}
+
+std::ostream &
+operator <<(std::ostream &os, LSQ::AddrRangeCoverage coverage)
+{
+ switch (coverage) {
+ case LSQ::PartialAddrRangeCoverage:
+ os << "PartialAddrRangeCoverage";
+ break;
+ case LSQ::FullAddrRangeCoverage:
+ os << "FullAddrRangeCoverage";
+ break;
+ case LSQ::NoAddrRangeCoverage:
+ os << "NoAddrRangeCoverage";
+ break;
+ default:
+ os << "AddrRangeCoverage-" << static_cast<int>(coverage);
+ break;
+ }
+ return os;
+}
+
+std::ostream &
+operator <<(std::ostream &os, LSQ::LSQRequest::LSQRequestState state)
+{
+ switch (state) {
+ case LSQ::LSQRequest::NotIssued:
+ os << "NotIssued";
+ break;
+ case LSQ::LSQRequest::InTranslation:
+ os << "InTranslation";
+ break;
+ case LSQ::LSQRequest::Translated:
+ os << "Translated";
+ break;
+ case LSQ::LSQRequest::Failed:
+ os << "Failed";
+ break;
+ case LSQ::LSQRequest::RequestIssuing:
+ os << "RequestIssuing";
+ break;
+ case LSQ::LSQRequest::StoreToStoreBuffer:
+ os << "StoreToStoreBuffer";
+ break;
+ case LSQ::LSQRequest::StoreInStoreBuffer:
+ os << "StoreInStoreBuffer";
+ break;
+ case LSQ::LSQRequest::StoreBufferIssuing:
+ os << "StoreBufferIssuing";
+ break;
+ case LSQ::LSQRequest::RequestNeedsRetry:
+ os << "RequestNeedsRetry";
+ break;
+ case LSQ::LSQRequest::StoreBufferNeedsRetry:
+ os << "StoreBufferNeedsRetry";
+ break;
+ case LSQ::LSQRequest::Complete:
+ os << "Complete";
+ break;
+ default:
+ os << "LSQRequestState-" << static_cast<int>(state);
+ break;
+ }
+ return os;
+}
+
+void
+LSQ::clearMemBarrier(MinorDynInstPtr inst)
+{
+ bool is_last_barrier = inst->id.execSeqNum >= lastMemBarrier;
+
+ DPRINTF(MinorMem, "Moving %s barrier out of store buffer inst: %s\n",
+ (is_last_barrier ? "last" : "a"), *inst);
+
+ if (is_last_barrier)
+ lastMemBarrier = 0;
+}
+
+void
+LSQ::SingleDataRequest::finish(Fault fault_, RequestPtr request_,
+ ThreadContext *tc, BaseTLB::Mode mode)
+{
+ fault = fault_;
+
+ port.numAccessesInDTLB--;
+
+ DPRINTFS(MinorMem, (&port), "Received translation response for"
+ " request: %s\n", *inst);
+
+ makePacket();
+
+ setState(Translated);
+ port.tryToSendToTransfers(this);
+
+ /* Let's try and wake up the processor for the next cycle */
+ port.cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+}
+
+void
+LSQ::SingleDataRequest::startAddrTranslation()
+{
+ ThreadContext *thread = port.cpu.getContext(
+ inst->id.threadId);
+
+ port.numAccessesInDTLB++;
+
+ setState(LSQ::LSQRequest::InTranslation);
+
+ DPRINTFS(MinorMem, (&port), "Submitting DTLB request\n");
+ /* Submit the translation request. The response will come through
+ * finish/markDelayed on the LSQRequest as it bears the Translation
+ * interface */
+ thread->getDTBPtr()->translateTiming(
+ &request, thread, this, (isLoad ? BaseTLB::Read : BaseTLB::Write));
+}
+
+void
+LSQ::SingleDataRequest::retireResponse(PacketPtr packet_)
+{
+ DPRINTFS(MinorMem, (&port), "Retiring packet\n");
+ packet = packet_;
+ packetInFlight = false;
+ setState(Complete);
+}
+
+void
+LSQ::SplitDataRequest::finish(Fault fault_, RequestPtr request_,
+ ThreadContext *tc, BaseTLB::Mode mode)
+{
+ fault = fault_;
+
+ port.numAccessesInDTLB--;
+
+ unsigned int M5_VAR_USED expected_fragment_index =
+ numTranslatedFragments;
+
+ numInTranslationFragments--;
+ numTranslatedFragments++;
+
+ DPRINTFS(MinorMem, (&port), "Received translation response for fragment"
+ " %d of request: %s\n", expected_fragment_index, *inst);
+
+ assert(request_ == fragmentRequests[expected_fragment_index]);
+
+ /* Wake up next cycle to get things going again in case the
+ * tryToSendToTransfers does take */
+ port.cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+
+ if (fault != NoFault) {
+ /* tryToSendToTransfers will handle the fault */
+
+ DPRINTFS(MinorMem, (&port), "Faulting translation for fragment:"
+ " %d of request: %s\n",
+ expected_fragment_index, *inst);
+
+ setState(Translated);
+ port.tryToSendToTransfers(this);
+ } else if (numTranslatedFragments == numFragments) {
+ makeFragmentPackets();
+
+ setState(Translated);
+ port.tryToSendToTransfers(this);
+ } else {
+ /* Avoid calling translateTiming from within ::finish */
+ assert(!translationEvent.scheduled());
+ port.cpu.schedule(translationEvent, curTick());
+ }
+}
+
+LSQ::SplitDataRequest::SplitDataRequest(LSQ &port_, MinorDynInstPtr inst_,
+ bool isLoad_, PacketDataPtr data_, uint64_t *res_) :
+ LSQRequest(port_, inst_, isLoad_, data_, res_),
+ translationEvent(*this),
+ numFragments(0),
+ numInTranslationFragments(0),
+ numTranslatedFragments(0),
+ numIssuedFragments(0),
+ numRetiredFragments(0),
+ fragmentRequests(),
+ fragmentPackets()
+{
+ /* Don't know how many elements are needed until the request is
+ * populated by the caller. */
+}
+
+LSQ::SplitDataRequest::~SplitDataRequest()
+{
+ for (auto i = fragmentRequests.begin();
+ i != fragmentRequests.end(); i++)
+ {
+ delete *i;
+ }
+
+ for (auto i = fragmentPackets.begin();
+ i != fragmentPackets.end(); i++)
+ {
+ delete *i;
+ }
+}
+
+void
+LSQ::SplitDataRequest::makeFragmentRequests()
+{
+ Addr base_addr = request.getVaddr();
+ unsigned int whole_size = request.getSize();
+ unsigned int line_width = port.lineWidth;
+
+ unsigned int fragment_size;
+ Addr fragment_addr;
+
+ /* Assume that this transfer is across potentially many block snap
+ * boundaries:
+ *
+ * | _|________|________|________|___ |
+ * | |0| 1 | 2 | 3 | 4 | |
+ * | |_|________|________|________|___| |
+ * | | | | | |
+ *
+ * The first transfer (0) can be up to lineWidth in size.
+ * All the middle transfers (1-3) are lineWidth in size
+ * The last transfer (4) can be from zero to lineWidth - 1 in size
+ */
+ unsigned int first_fragment_offset =
+ addrBlockOffset(base_addr, line_width);
+ unsigned int last_fragment_size =
+ addrBlockOffset(base_addr + whole_size, line_width);
+ unsigned int first_fragment_size =
+ line_width - first_fragment_offset;
+
+ unsigned int middle_fragments_total_size =
+ whole_size - (first_fragment_size + last_fragment_size);
+
+ assert(addrBlockOffset(middle_fragments_total_size, line_width) == 0);
+
+ unsigned int middle_fragment_count =
+ middle_fragments_total_size / line_width;
+
+ numFragments = 1 /* first */ + middle_fragment_count +
+ (last_fragment_size == 0 ? 0 : 1);
+
+ DPRINTFS(MinorMem, (&port), "Dividing transfer into %d fragmentRequests."
+ " First fragment size: %d Last fragment size: %d\n",
+ numFragments, first_fragment_size,
+ (last_fragment_size == 0 ? line_width : last_fragment_size));
+
+ assert(((middle_fragment_count * line_width) +
+ first_fragment_size + last_fragment_size) == whole_size);
+
+ fragment_addr = base_addr;
+ fragment_size = first_fragment_size;
+
+ /* Just past the last address in the request */
+ Addr end_addr = base_addr + whole_size;
+
+ for (unsigned int fragment_index = 0; fragment_index < numFragments;
+ fragment_index++)
+ {
+ bool M5_VAR_USED is_last_fragment = false;
+
+ if (fragment_addr == base_addr) {
+ /* First fragment */
+ fragment_size = first_fragment_size;
+ } else {
+ if ((fragment_addr + line_width) > end_addr) {
+ /* Adjust size of last fragment */
+ fragment_size = end_addr - fragment_addr;
+ is_last_fragment = true;
+ } else {
+ /* Middle fragments */
+ fragment_size = line_width;
+ }
+ }
+
+ Request *fragment = new Request();
+
+ fragment->setThreadContext(request.contextId(), /* thread id */ 0);
+ fragment->setVirt(0 /* asid */,
+ fragment_addr, fragment_size, request.getFlags(),
+ request.masterId(),
+ request.getPC());
+
+ DPRINTFS(MinorMem, (&port), "Generating fragment addr: 0x%x size: %d"
+ " (whole request addr: 0x%x size: %d) %s\n",
+ fragment_addr, fragment_size, base_addr, whole_size,
+ (is_last_fragment ? "last fragment" : ""));
+
+ fragment_addr += fragment_size;
+
+ fragmentRequests.push_back(fragment);
+ }
+}
+
+void
+LSQ::SplitDataRequest::makeFragmentPackets()
+{
+ Addr base_addr = request.getVaddr();
+
+ DPRINTFS(MinorMem, (&port), "Making packets for request: %s\n", *inst);
+
+ for (unsigned int fragment_index = 0; fragment_index < numFragments;
+ fragment_index++)
+ {
+ Request *fragment = fragmentRequests[fragment_index];
+
+ DPRINTFS(MinorMem, (&port), "Making packet %d for request: %s"
+ " (%d, 0x%x)\n",
+ fragment_index, *inst,
+ (fragment->hasPaddr() ? "has paddr" : "no paddr"),
+ (fragment->hasPaddr() ? fragment->getPaddr() : 0));
+
+ Addr fragment_addr = fragment->getVaddr();
+ unsigned int fragment_size = fragment->getSize();
+
+ uint8_t *request_data = NULL;
+
+ if (!isLoad) {
+ /* Split data for Packets. Will become the property of the
+ * outgoing Packets */
+ request_data = new uint8_t[fragment_size];
+ std::memcpy(request_data, data + (fragment_addr - base_addr),
+ fragment_size);
+ }
+
+ assert(fragment->hasPaddr());
+
+ PacketPtr fragment_packet =
+ makePacketForRequest(*fragment, isLoad, this, request_data);
+
+ fragmentPackets.push_back(fragment_packet);
+ }
+
+ /* Might as well make the overall/response packet here */
+ /* Get the physical address for the whole request/packet from the first
+ * fragment */
+ request.setPaddr(fragmentRequests[0]->getPaddr());
+ makePacket();
+}
+
+void
+LSQ::SplitDataRequest::startAddrTranslation()
+{
+ setState(LSQ::LSQRequest::InTranslation);
+
+ makeFragmentRequests();
+
+ numInTranslationFragments = 0;
+ numTranslatedFragments = 0;
+
+ /* @todo, just do these in sequence for now with
+ * a loop of:
+ * do {
+ * sendNextFragmentToTranslation ; translateTiming ; finish
+ * } while (numTranslatedFragments != numFragments);
+ */
+
+ /* Do first translation */
+ sendNextFragmentToTranslation();
+}
+
+PacketPtr
+LSQ::SplitDataRequest::getHeadPacket()
+{
+ assert(numIssuedFragments < numFragments);
+
+ return fragmentPackets[numIssuedFragments];
+}
+
+void
+LSQ::SplitDataRequest::stepToNextPacket()
+{
+ assert(numIssuedFragments < numFragments);
+
+ numIssuedFragments++;
+}
+
+void
+LSQ::SplitDataRequest::retireResponse(PacketPtr response)
+{
+ assert(numRetiredFragments < numFragments);
+
+ DPRINTFS(MinorMem, (&port), "Retiring fragment addr: 0x%x size: %d"
+ " offset: 0x%x (retired fragment num: %d) %s\n",
+ response->req->getVaddr(), response->req->getSize(),
+ request.getVaddr() - response->req->getVaddr(),
+ numRetiredFragments,
+ (fault == NoFault ? "" : fault->name()));
+
+ numRetiredFragments++;
+
+ if (skipped) {
+ /* Skip because we already knew the request had faulted or been
+ * skipped */
+ DPRINTFS(MinorMem, (&port), "Skipping this fragment\n");
+ } else if (response->isError()) {
+ /* Mark up the error and leave to execute to handle it */
+ DPRINTFS(MinorMem, (&port), "Fragment has an error, skipping\n");
+ setSkipped();
+ packet->copyError(response);
+ } else {
+ if (isLoad) {
+ if (!data) {
+ /* For a split transfer, a Packet must be constructed
+ * to contain all returning data. This is that packet's
+ * data */
+ data = new uint8_t[request.getSize()];
+ }
+
+ /* Populate the portion of the overall response data represented
+ * by the response fragment */
+ std::memcpy(
+ data + (response->req->getVaddr() - request.getVaddr()),
+ response->getPtr<uint8_t>(),
+ response->req->getSize());
+ }
+ }
+
+ /* Complete early if we're skipping are no more in-flight accesses */
+ if (skipped && !hasPacketsInMemSystem()) {
+ DPRINTFS(MinorMem, (&port), "Completed skipped burst\n");
+ setState(Complete);
+ if (packet->needsResponse())
+ packet->makeResponse();
+ }
+
+ if (numRetiredFragments == numFragments)
+ setState(Complete);
+
+ if (!skipped && isComplete()) {
+ DPRINTFS(MinorMem, (&port), "Completed burst %d\n", packet != NULL);
+
+ DPRINTFS(MinorMem, (&port), "Retired packet isRead: %d isWrite: %d"
+ " needsResponse: %d packetSize: %s requestSize: %s responseSize:"
+ " %s\n", packet->isRead(), packet->isWrite(),
+ packet->needsResponse(), packet->getSize(), request.getSize(),
+ response->getSize());
+
+ /* A request can become complete by several paths, this is a sanity
+ * check to make sure the packet's data is created */
+ if (!data) {
+ data = new uint8_t[request.getSize()];
+ }
+
+ if (isLoad) {
+ DPRINTFS(MinorMem, (&port), "Copying read data\n");
+ std::memcpy(packet->getPtr<uint8_t>(), data, request.getSize());
+ }
+ packet->makeResponse();
+ }
+
+ /* Packets are all deallocated together in ~SplitLSQRequest */
+}
+
+void
+LSQ::SplitDataRequest::sendNextFragmentToTranslation()
+{
+ unsigned int fragment_index = numTranslatedFragments;
+
+ ThreadContext *thread = port.cpu.getContext(
+ inst->id.threadId);
+
+ DPRINTFS(MinorMem, (&port), "Submitting DTLB request for fragment: %d\n",
+ fragment_index);
+
+ port.numAccessesInDTLB++;
+ numInTranslationFragments++;
+
+ thread->getDTBPtr()->translateTiming(
+ fragmentRequests[fragment_index], thread, this, (isLoad ?
+ BaseTLB::Read : BaseTLB::Write));
+}
+
+bool
+LSQ::StoreBuffer::canInsert() const
+{
+ /* @todo, support store amalgamation */
+ return slots.size() < numSlots;
+}
+
+void
+LSQ::StoreBuffer::deleteRequest(LSQRequestPtr request)
+{
+ auto found = std::find(slots.begin(), slots.end(), request);
+
+ if (found != slots.end()) {
+ DPRINTF(MinorMem, "Deleting request: %s %s %s from StoreBuffer\n",
+ request, *found, *(request->inst));
+ slots.erase(found);
+
+ delete request;
+ }
+}
+
+void
+LSQ::StoreBuffer::insert(LSQRequestPtr request)
+{
+ if (!canInsert()) {
+ warn("%s: store buffer insertion without space to insert from"
+ " inst: %s\n", name(), *(request->inst));
+ }
+
+ DPRINTF(MinorMem, "Pushing store: %s into store buffer\n", request);
+
+ numUnissuedAccesses++;
+
+ if (request->state != LSQRequest::Complete)
+ request->setState(LSQRequest::StoreInStoreBuffer);
+
+ slots.push_back(request);
+
+ /* Let's try and wake up the processor for the next cycle to step
+ * the store buffer */
+ lsq.cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+}
+
+LSQ::AddrRangeCoverage
+LSQ::StoreBuffer::canForwardDataToLoad(LSQRequestPtr request,
+ unsigned int &found_slot)
+{
+ unsigned int slot_index = slots.size() - 1;
+ auto i = slots.rbegin();
+ AddrRangeCoverage ret = NoAddrRangeCoverage;
+
+ /* Traverse the store buffer in reverse order (most to least recent)
+ * and try to find a slot whose address range overlaps this request */
+ while (ret == NoAddrRangeCoverage && i != slots.rend()) {
+ LSQRequestPtr slot = *i;
+
+ if (slot->packet) {
+ AddrRangeCoverage coverage = slot->containsAddrRangeOf(request);
+
+ if (coverage != NoAddrRangeCoverage) {
+ DPRINTF(MinorMem, "Forwarding: slot: %d result: %s thisAddr:"
+ " 0x%x thisSize: %d slotAddr: 0x%x slotSize: %d\n",
+ slot_index, coverage,
+ request->request.getPaddr(), request->request.getSize(),
+ slot->request.getPaddr(), slot->request.getSize());
+
+ found_slot = slot_index;
+ ret = coverage;
+ }
+ }
+
+ i++;
+ slot_index--;
+ }
+
+ return ret;
+}
+
+/** Fill the given packet with appropriate date from slot slot_number */
+void
+LSQ::StoreBuffer::forwardStoreData(LSQRequestPtr load,
+ unsigned int slot_number)
+{
+ assert(slot_number < slots.size());
+ assert(load->packet);
+ assert(load->isLoad);
+
+ LSQRequestPtr store = slots[slot_number];
+
+ assert(store->packet);
+ assert(store->containsAddrRangeOf(load) == FullAddrRangeCoverage);
+
+ Addr load_addr = load->request.getPaddr();
+ Addr store_addr = store->request.getPaddr();
+ Addr addr_offset = load_addr - store_addr;
+
+ unsigned int load_size = load->request.getSize();
+
+ DPRINTF(MinorMem, "Forwarding %d bytes for addr: 0x%x from store buffer"
+ " slot: %d addr: 0x%x addressOffset: 0x%x\n",
+ load_size, load_addr, slot_number,
+ store_addr, addr_offset);
+
+ void *load_packet_data = load->packet->getPtr<void>();
+ void *store_packet_data = store->packet->getPtr<uint8_t>() + addr_offset;
+
+ std::memcpy(load_packet_data, store_packet_data, load_size);
+}
+
+void
+LSQ::StoreBuffer::step()
+{
+ DPRINTF(MinorMem, "StoreBuffer step numUnissuedAccesses: %d\n",
+ numUnissuedAccesses);
+
+ if (numUnissuedAccesses != 0 && lsq.state == LSQ::MemoryRunning) {
+ /* Clear all the leading barriers */
+ while (!slots.empty() &&
+ slots.front()->isComplete() && slots.front()->isBarrier())
+ {
+ LSQRequestPtr barrier = slots.front();
+
+ DPRINTF(MinorMem, "Clearing barrier for inst: %s\n",
+ *(barrier->inst));
+
+ numUnissuedAccesses--;
+ lsq.clearMemBarrier(barrier->inst);
+ slots.pop_front();
+
+ delete barrier;
+ }
+
+ auto i = slots.begin();
+ bool issued = true;
+ unsigned int issue_count = 0;
+
+ /* Skip trying if the memory system is busy */
+ if (lsq.state == LSQ::MemoryNeedsRetry)
+ issued = false;
+
+ /* Try to issue all stores in order starting from the head
+ * of the queue. Responses are allowed to be retired
+ * out of order */
+ while (issued &&
+ issue_count < storeLimitPerCycle &&
+ lsq.canSendToMemorySystem() &&
+ i != slots.end())
+ {
+ LSQRequestPtr request = *i;
+
+ DPRINTF(MinorMem, "Considering request: %s, sentAllPackets: %d"
+ " state: %s\n",
+ *(request->inst), request->sentAllPackets(),
+ request->state);
+
+ if (request->isBarrier() && request->isComplete()) {
+ /* Give up at barriers */
+ issued = false;
+ } else if (!(request->state == LSQRequest::StoreBufferIssuing &&
+ request->sentAllPackets()))
+ {
+ DPRINTF(MinorMem, "Trying to send request: %s to memory"
+ " system\n", *(request->inst));
+
+ if (lsq.tryToSend(request)) {
+ /* Barrier are accounted for as they are cleared from
+ * the queue, not after their transfers are complete */
+ if (!request->isBarrier())
+ numUnissuedAccesses--;
+ issue_count++;
+ } else {
+ /* Don't step on to the next store buffer entry if this
+ * one hasn't issued all its packets as the store
+ * buffer must still enforce ordering */
+ issued = false;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+void
+LSQ::completeMemBarrierInst(MinorDynInstPtr inst,
+ bool committed)
+{
+ if (committed) {
+ /* Not already sent to the store buffer as a store request? */
+ if (!inst->inStoreBuffer) {
+ /* Insert an entry into the store buffer to tick off barriers
+ * until there are none in flight */
+ storeBuffer.insert(new BarrierDataRequest(*this, inst));
+ }
+ } else {
+ /* Clear the barrier anyway if it wasn't actually committed */
+ clearMemBarrier(inst);
+ }
+}
+
+void
+LSQ::StoreBuffer::minorTrace() const
+{
+ unsigned int size = slots.size();
+ unsigned int i = 0;
+ std::ostringstream os;
+
+ while (i < size) {
+ LSQRequestPtr request = slots[i];
+
+ request->reportData(os);
+
+ i++;
+ if (i < numSlots)
+ os << ',';
+ }
+
+ while (i < numSlots) {
+ os << '-';
+
+ i++;
+ if (i < numSlots)
+ os << ',';
+ }
+
+ MINORTRACE("addr=%s num_unissued_stores=%d\n", os.str(),
+ numUnissuedAccesses);
+}
+
+void
+LSQ::tryToSendToTransfers(LSQRequestPtr request)
+{
+ if (state == MemoryNeedsRetry) {
+ DPRINTF(MinorMem, "Request needs retry, not issuing to"
+ " memory until retry arrives\n");
+ return;
+ }
+
+ if (request->state == LSQRequest::InTranslation) {
+ DPRINTF(MinorMem, "Request still in translation, not issuing to"
+ " memory\n");
+ return;
+ }
+
+ assert(request->state == LSQRequest::Translated ||
+ request->state == LSQRequest::RequestIssuing ||
+ request->state == LSQRequest::Failed ||
+ request->state == LSQRequest::Complete);
+
+ if (requests.empty() || requests.front() != request) {
+ DPRINTF(MinorMem, "Request not at front of requests queue, can't"
+ " issue to memory\n");
+ return;
+ }
+
+ if (transfers.unreservedRemainingSpace() == 0) {
+ DPRINTF(MinorMem, "No space to insert request into transfers"
+ " queue\n");
+ return;
+ }
+
+ if (request->isComplete() || request->state == LSQRequest::Failed) {
+ DPRINTF(MinorMem, "Passing a %s transfer on to transfers"
+ " queue\n", (request->isComplete() ? "completed" : "failed"));
+ request->setState(LSQRequest::Complete);
+ request->setSkipped();
+ moveFromRequestsToTransfers(request);
+ return;
+ }
+
+ if (!execute.instIsRightStream(request->inst)) {
+ /* Wrong stream, try to abort the transfer but only do so if
+ * there are no packets in flight */
+ if (request->hasPacketsInMemSystem()) {
+ DPRINTF(MinorMem, "Request's inst. is from the wrong stream,"
+ " waiting for responses before aborting request\n");
+ } else {
+ DPRINTF(MinorMem, "Request's inst. is from the wrong stream,"
+ " aborting request\n");
+ request->setState(LSQRequest::Complete);
+ request->setSkipped();
+ moveFromRequestsToTransfers(request);
+ }
+ return;
+ }
+
+ if (request->fault != NoFault) {
+ if (request->inst->staticInst->isPrefetch()) {
+ DPRINTF(MinorMem, "Not signalling fault for faulting prefetch\n");
+ }
+ DPRINTF(MinorMem, "Moving faulting request into the transfers"
+ " queue\n");
+ request->setState(LSQRequest::Complete);
+ request->setSkipped();
+ moveFromRequestsToTransfers(request);
+ return;
+ }
+
+ bool is_load = request->isLoad;
+ bool is_llsc = request->request.isLLSC();
+ bool is_swap = request->request.isSwap();
+ bool bufferable = !(request->request.isUncacheable() ||
+ is_llsc || is_swap);
+
+ if (is_load) {
+ if (numStoresInTransfers != 0) {
+ DPRINTF(MinorMem, "Load request with stores still in transfers"
+ " queue, stalling\n");
+ return;
+ }
+ } else {
+ /* Store. Can it be sent to the store buffer? */
+ if (bufferable && !request->request.isMmappedIpr()) {
+ request->setState(LSQRequest::StoreToStoreBuffer);
+ moveFromRequestsToTransfers(request);
+ DPRINTF(MinorMem, "Moving store into transfers queue\n");
+ return;
+ }
+ }
+
+ /* Check if this is the head instruction (and so must be executable as
+ * its stream sequence number was checked above) for loads which must
+ * not be speculatively issued and stores which must be issued here */
+ if (!bufferable) {
+ if (!execute.instIsHeadInst(request->inst)) {
+ DPRINTF(MinorMem, "Memory access not the head inst., can't be"
+ " sure it can be performed, not issuing\n");
+ return;
+ }
+
+ unsigned int forwarding_slot = 0;
+
+ if (storeBuffer.canForwardDataToLoad(request, forwarding_slot) !=
+ NoAddrRangeCoverage)
+ {
+ DPRINTF(MinorMem, "Memory access can receive forwarded data"
+ " from the store buffer, need to wait for store buffer to"
+ " drain\n");
+ return;
+ }
+ }
+
+ /* True: submit this packet to the transfers queue to be sent to the
+ * memory system.
+ * False: skip the memory and push a packet for this request onto
+ * requests */
+ bool do_access = true;
+
+ if (!is_llsc) {
+ /* Check for match in the store buffer */
+ if (is_load) {
+ unsigned int forwarding_slot = 0;
+ AddrRangeCoverage forwarding_result =
+ storeBuffer.canForwardDataToLoad(request,
+ forwarding_slot);
+
+ switch (forwarding_result) {
+ case FullAddrRangeCoverage:
+ /* Forward data from the store buffer into this request and
+ * repurpose this request's packet into a response packet */
+ storeBuffer.forwardStoreData(request, forwarding_slot);
+ request->packet->makeResponse();
+
+ /* Just move between queues, no access */
+ do_access = false;
+ break;
+ case PartialAddrRangeCoverage:
+ DPRINTF(MinorMem, "Load partly satisfied by store buffer"
+ " data. Must wait for the store to complete\n");
+ return;
+ break;
+ case NoAddrRangeCoverage:
+ DPRINTF(MinorMem, "No forwardable data from store buffer\n");
+ /* Fall through to try access */
+ break;
+ }
+ }
+ } else {
+ if (!canSendToMemorySystem()) {
+ DPRINTF(MinorMem, "Can't send request to memory system yet\n");
+ return;
+ }
+
+ SimpleThread &thread = *cpu.threads[request->inst->id.threadId];
+
+ TheISA::PCState old_pc = thread.pcState();
+ ExecContext context(cpu, thread, execute, request->inst);
+
+ /* Handle LLSC requests and tests */
+ if (is_load) {
+ TheISA::handleLockedRead(&context, &request->request);
+ } else {
+ do_access = TheISA::handleLockedWrite(&context,
+ &request->request, cacheBlockMask);
+
+ if (!do_access) {
+ DPRINTF(MinorMem, "Not perfoming a memory "
+ "access for store conditional\n");
+ }
+ }
+ thread.pcState(old_pc);
+ }
+
+ /* See the do_access comment above */
+ if (do_access) {
+ if (!canSendToMemorySystem()) {
+ DPRINTF(MinorMem, "Can't send request to memory system yet\n");
+ return;
+ }
+
+ /* Remember if this is an access which can't be idly
+ * discarded by an interrupt */
+ if (!bufferable) {
+ numAccessesIssuedToMemory++;
+ request->issuedToMemory = true;
+ }
+
+ if (tryToSend(request))
+ moveFromRequestsToTransfers(request);
+ } else {
+ request->setState(LSQRequest::Complete);
+ moveFromRequestsToTransfers(request);
+ }
+}
+
+bool
+LSQ::tryToSend(LSQRequestPtr request)
+{
+ bool ret = false;
+
+ if (!canSendToMemorySystem()) {
+ DPRINTF(MinorMem, "Can't send request: %s yet, no space in memory\n",
+ *(request->inst));
+ } else {
+ PacketPtr packet = request->getHeadPacket();
+
+ DPRINTF(MinorMem, "Trying to send request: %s addr: 0x%x\n",
+ *(request->inst), packet->req->getVaddr());
+
+ /* The sender state of the packet *must* be an LSQRequest
+ * so the response can be correctly handled */
+ assert(packet->findNextSenderState<LSQRequest>());
+
+ if (request->request.isMmappedIpr()) {
+ ThreadContext *thread =
+ cpu.getContext(request->request.threadId());
+
+ if (request->isLoad) {
+ DPRINTF(MinorMem, "IPR read inst: %s\n", *(request->inst));
+ TheISA::handleIprRead(thread, packet);
+ } else {
+ DPRINTF(MinorMem, "IPR write inst: %s\n", *(request->inst));
+ TheISA::handleIprWrite(thread, packet);
+ }
+
+ request->stepToNextPacket();
+ ret = request->sentAllPackets();
+
+ if (!ret) {
+ DPRINTF(MinorMem, "IPR access has another packet: %s\n",
+ *(request->inst));
+ }
+
+ if (ret)
+ request->setState(LSQRequest::Complete);
+ else
+ request->setState(LSQRequest::RequestIssuing);
+ } else if (dcachePort.sendTimingReq(packet)) {
+ DPRINTF(MinorMem, "Sent data memory request\n");
+
+ numAccessesInMemorySystem++;
+
+ request->stepToNextPacket();
+
+ ret = request->sentAllPackets();
+
+ switch (request->state) {
+ case LSQRequest::Translated:
+ case LSQRequest::RequestIssuing:
+ /* Fully or partially issued a request in the transfers
+ * queue */
+ request->setState(LSQRequest::RequestIssuing);
+ break;
+ case LSQRequest::StoreInStoreBuffer:
+ case LSQRequest::StoreBufferIssuing:
+ /* Fully or partially issued a request in the store
+ * buffer */
+ request->setState(LSQRequest::StoreBufferIssuing);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ state = MemoryRunning;
+ } else {
+ DPRINTF(MinorMem,
+ "Sending data memory request - needs retry\n");
+
+ /* Needs to be resent, wait for that */
+ state = MemoryNeedsRetry;
+ retryRequest = request;
+
+ switch (request->state) {
+ case LSQRequest::Translated:
+ case LSQRequest::RequestIssuing:
+ request->setState(LSQRequest::RequestNeedsRetry);
+ break;
+ case LSQRequest::StoreInStoreBuffer:
+ case LSQRequest::StoreBufferIssuing:
+ request->setState(LSQRequest::StoreBufferNeedsRetry);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void
+LSQ::moveFromRequestsToTransfers(LSQRequestPtr request)
+{
+ assert(!requests.empty() && requests.front() == request);
+ assert(transfers.unreservedRemainingSpace() != 0);
+
+ /* Need to count the number of stores in the transfers
+ * queue so that loads know when their store buffer forwarding
+ * results will be correct (only when all those stores
+ * have reached the store buffer) */
+ if (!request->isLoad)
+ numStoresInTransfers++;
+
+ requests.pop();
+ transfers.push(request);
+}
+
+bool
+LSQ::canSendToMemorySystem()
+{
+ return state == MemoryRunning &&
+ numAccessesInMemorySystem < inMemorySystemLimit;
+}
+
+bool
+LSQ::recvTimingResp(PacketPtr response)
+{
+ LSQRequestPtr request =
+ safe_cast<LSQRequestPtr>(response->popSenderState());
+
+ DPRINTF(MinorMem, "Received response packet inst: %s"
+ " addr: 0x%x cmd: %s\n",
+ *(request->inst), response->getAddr(),
+ response->cmd.toString());
+
+ numAccessesInMemorySystem--;
+
+ if (response->isError()) {
+ DPRINTF(MinorMem, "Received error response packet: %s\n",
+ *request->inst);
+ }
+
+ switch (request->state) {
+ case LSQRequest::RequestIssuing:
+ case LSQRequest::RequestNeedsRetry:
+ /* Response to a request from the transfers queue */
+ request->retireResponse(response);
+
+ DPRINTF(MinorMem, "Has outstanding packets?: %d %d\n",
+ request->hasPacketsInMemSystem(), request->isComplete());
+
+ break;
+ case LSQRequest::StoreBufferIssuing:
+ case LSQRequest::StoreBufferNeedsRetry:
+ /* Response to a request from the store buffer */
+ request->retireResponse(response);
+
+ /* Remove completed requests unless they are barrier (which will
+ * need to be removed in order */
+ if (request->isComplete()) {
+ if (!request->isBarrier()) {
+ storeBuffer.deleteRequest(request);
+ } else {
+ DPRINTF(MinorMem, "Completed transfer for barrier: %s"
+ " leaving the request as it is also a barrier\n",
+ *(request->inst));
+ }
+ }
+ break;
+ default:
+ /* Shouldn't be allowed to receive a response from another
+ * state */
+ assert(false);
+ break;
+ }
+
+ /* We go to idle even if there are more things in the requests queue
+ * as it's the job of step to actually step us on to the next
+ * transaction */
+
+ /* Let's try and wake up the processor for the next cycle */
+ cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
+
+ /* Never busy */
+ return true;
+}
+
+void
+LSQ::recvRetry()
+{
+ DPRINTF(MinorMem, "Received retry request\n");
+
+ assert(state == MemoryNeedsRetry);
+
+ switch (retryRequest->state) {
+ case LSQRequest::RequestNeedsRetry:
+ /* Retry in the requests queue */
+ retryRequest->setState(LSQRequest::Translated);
+ break;
+ case LSQRequest::StoreBufferNeedsRetry:
+ /* Retry in the store buffer */
+ retryRequest->setState(LSQRequest::StoreInStoreBuffer);
+ break;
+ default:
+ assert(false);
+ }
+
+ /* Set state back to MemoryRunning so that the following
+ * tryToSend can actually send. Note that this won't
+ * allow another transfer in as tryToSend should
+ * issue a memory request and either succeed for this
+ * request or return the LSQ back to MemoryNeedsRetry */
+ state = MemoryRunning;
+
+ /* Try to resend the request */
+ if (tryToSend(retryRequest)) {
+ /* Successfully sent, need to move the request */
+ switch (retryRequest->state) {
+ case LSQRequest::RequestIssuing:
+ /* In the requests queue */
+ moveFromRequestsToTransfers(retryRequest);
+ break;
+ case LSQRequest::StoreBufferIssuing:
+ /* In the store buffer */
+ storeBuffer.numUnissuedAccesses--;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ retryRequest = NULL;
+}
+
+LSQ::LSQ(std::string name_, std::string dcache_port_name_,
+ MinorCPU &cpu_, Execute &execute_,
+ unsigned int in_memory_system_limit, unsigned int line_width,
+ unsigned int requests_queue_size, unsigned int transfers_queue_size,
+ unsigned int store_buffer_size,
+ unsigned int store_buffer_cycle_store_limit) :
+ Named(name_),
+ cpu(cpu_),
+ execute(execute_),
+ dcachePort(dcache_port_name_, *this, cpu_),
+ lastMemBarrier(0),
+ state(MemoryRunning),
+ inMemorySystemLimit(in_memory_system_limit),
+ lineWidth((line_width == 0 ? cpu.cacheLineSize() : line_width)),
+ requests(name_ + ".requests", "addr", requests_queue_size),
+ transfers(name_ + ".transfers", "addr", transfers_queue_size),
+ storeBuffer(name_ + ".storeBuffer",
+ *this, store_buffer_size, store_buffer_cycle_store_limit),
+ numAccessesInMemorySystem(0),
+ numAccessesInDTLB(0),
+ numStoresInTransfers(0),
+ numAccessesIssuedToMemory(0),
+ retryRequest(NULL),
+ cacheBlockMask(~(cpu_.cacheLineSize() - 1))
+{
+ if (in_memory_system_limit < 1) {
+ fatal("%s: executeMaxAccessesInMemory must be >= 1 (%d)\n", name_,
+ in_memory_system_limit);
+ }
+
+ if (store_buffer_cycle_store_limit < 1) {
+ fatal("%s: executeLSQMaxStoreBufferStoresPerCycle must be"
+ " >= 1 (%d)\n", name_, store_buffer_cycle_store_limit);
+ }
+
+ if (requests_queue_size < 1) {
+ fatal("%s: executeLSQRequestsQueueSize must be"
+ " >= 1 (%d)\n", name_, requests_queue_size);
+ }
+
+ if (transfers_queue_size < 1) {
+ fatal("%s: executeLSQTransfersQueueSize must be"
+ " >= 1 (%d)\n", name_, transfers_queue_size);
+ }
+
+ if (store_buffer_size < 1) {
+ fatal("%s: executeLSQStoreBufferSize must be"
+ " >= 1 (%d)\n", name_, store_buffer_size);
+ }
+
+ if ((lineWidth & (lineWidth - 1)) != 0) {
+ fatal("%s: lineWidth: %d must be a power of 2\n", name(), lineWidth);
+ }
+}
+
+LSQ::~LSQ()
+{ }
+
+LSQ::LSQRequest::~LSQRequest()
+{
+ if (packet)
+ delete packet;
+ if (data)
+ delete [] data;
+}
+
+/**
+ * Step the memory access mechanism on to its next state. In reality, most
+ * of the stepping is done by the callbacks on the LSQ but this
+ * function is responsible for issuing memory requests lodged in the
+ * requests queue.
+ */
+void
+LSQ::step()
+{
+ /* Try to move address-translated requests between queues and issue
+ * them */
+ if (!requests.empty())
+ tryToSendToTransfers(requests.front());
+
+ storeBuffer.step();
+}
+
+LSQ::LSQRequestPtr
+LSQ::findResponse(MinorDynInstPtr inst)
+{
+ LSQ::LSQRequestPtr ret = NULL;
+
+ if (!transfers.empty()) {
+ LSQRequestPtr request = transfers.front();
+
+ /* Same instruction and complete access or a store that's
+ * capable of being moved to the store buffer */
+ if (request->inst->id == inst->id) {
+ if (request->isComplete() ||
+ (request->state == LSQRequest::StoreToStoreBuffer &&
+ storeBuffer.canInsert()))
+ {
+ ret = request;
+ }
+ }
+ }
+
+ if (ret) {
+ DPRINTF(MinorMem, "Found matching memory response for inst: %s\n",
+ *inst);
+ } else {
+ DPRINTF(MinorMem, "No matching memory response for inst: %s\n",
+ *inst);
+ }
+
+ return ret;
+}
+
+void
+LSQ::popResponse(LSQ::LSQRequestPtr response)
+{
+ assert(!transfers.empty() && transfers.front() == response);
+
+ transfers.pop();
+
+ if (!response->isLoad)
+ numStoresInTransfers--;
+
+ if (response->issuedToMemory)
+ numAccessesIssuedToMemory--;
+
+ if (response->state != LSQRequest::StoreInStoreBuffer) {
+ DPRINTF(MinorMem, "Deleting %s request: %s\n",
+ (response->isLoad ? "load" : "store"),
+ *(response->inst));
+
+ delete response;
+ }
+}
+
+void
+LSQ::sendStoreToStoreBuffer(LSQRequestPtr request)
+{
+ assert(request->state == LSQRequest::StoreToStoreBuffer);
+
+ DPRINTF(MinorMem, "Sending store: %s to store buffer\n",
+ *(request->inst));
+
+ request->inst->inStoreBuffer = true;
+
+ storeBuffer.insert(request);
+}
+
+bool
+LSQ::isDrained()
+{
+ return requests.empty() && transfers.empty() &&
+ storeBuffer.isDrained();
+}
+
+bool
+LSQ::needsToTick()
+{
+ bool ret = false;
+
+ if (canSendToMemorySystem()) {
+ bool have_translated_requests = !requests.empty() &&
+ requests.front()->state != LSQRequest::InTranslation &&
+ transfers.unreservedRemainingSpace() != 0;
+
+ ret = have_translated_requests ||
+ storeBuffer.numUnissuedStores() != 0;
+ }
+
+ if (ret)
+ DPRINTF(Activity, "Need to tick\n");
+
+ return ret;
+}
+
+void
+LSQ::pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
+ unsigned int size, Addr addr, unsigned int flags, uint64_t *res)
+{
+ bool needs_burst = transferNeedsBurst(addr, size, lineWidth);
+ LSQRequestPtr request;
+
+ /* Copy given data into the request. The request will pass this to the
+ * packet and then it will own the data */
+ uint8_t *request_data = NULL;
+
+ DPRINTF(MinorMem, "Pushing request (%s) addr: 0x%x size: %d flags:"
+ " 0x%x%s lineWidth : 0x%x\n",
+ (isLoad ? "load" : "store"), addr, size, flags,
+ (needs_burst ? " (needs burst)" : ""), lineWidth);
+
+ if (!isLoad) {
+ /* request_data becomes the property of a ...DataRequest (see below)
+ * and destroyed by its destructor */
+ request_data = new uint8_t[size];
+ if (flags & Request::CACHE_BLOCK_ZERO) {
+ /* For cache zeroing, just use zeroed data */
+ std::memset(request_data, 0, size);
+ } else {
+ std::memcpy(request_data, data, size);
+ }
+ }
+
+ if (needs_burst) {
+ request = new SplitDataRequest(
+ *this, inst, isLoad, request_data, res);
+ } else {
+ request = new SingleDataRequest(
+ *this, inst, isLoad, request_data, res);
+ }
+
+ if (inst->traceData)
+ inst->traceData->setAddr(addr);
+
+ request->request.setThreadContext(cpu.cpuId(), /* thread id */ 0);
+ request->request.setVirt(0 /* asid */,
+ addr, size, flags, cpu.instMasterId(),
+ /* I've no idea why we need the PC, but give it */
+ inst->pc.instAddr());
+
+ requests.push(request);
+ request->startAddrTranslation();
+}
+
+void
+LSQ::pushFailedRequest(MinorDynInstPtr inst)
+{
+ LSQRequestPtr request = new FailedDataRequest(*this, inst);
+ requests.push(request);
+}
+
+void
+LSQ::minorTrace() const
+{
+ MINORTRACE("state=%s in_tlb_mem=%d/%d stores_in_transfers=%d"
+ " lastMemBarrier=%d\n",
+ state, numAccessesInDTLB, numAccessesInMemorySystem,
+ numStoresInTransfers, lastMemBarrier);
+ requests.minorTrace();
+ transfers.minorTrace();
+ storeBuffer.minorTrace();
+}
+
+LSQ::StoreBuffer::StoreBuffer(std::string name_, LSQ &lsq_,
+ unsigned int store_buffer_size,
+ unsigned int store_limit_per_cycle) :
+ Named(name_), lsq(lsq_),
+ numSlots(store_buffer_size),
+ storeLimitPerCycle(store_limit_per_cycle),
+ slots(),
+ numUnissuedAccesses(0)
+{
+}
+
+PacketPtr
+makePacketForRequest(Request &request, bool isLoad,
+ Packet::SenderState *sender_state, PacketDataPtr data)
+{
+ MemCmd command;
+
+ /* Make a ret with the right command type to match the request */
+ if (request.isLLSC()) {
+ command = (isLoad ? MemCmd::LoadLockedReq : MemCmd::StoreCondReq);
+ } else if (request.isSwap()) {
+ command = MemCmd::SwapReq;
+ } else {
+ command = (isLoad ? MemCmd::ReadReq : MemCmd::WriteReq);
+ }
+
+ PacketPtr ret = new Packet(&request, command);
+
+ if (sender_state)
+ ret->pushSenderState(sender_state);
+
+ if (isLoad)
+ ret->allocate();
+ else
+ ret->dataDynamicArray(data);
+
+ return ret;
+}
+
+void
+LSQ::issuedMemBarrierInst(MinorDynInstPtr inst)
+{
+ assert(inst->isInst() && inst->staticInst->isMemBarrier());
+ assert(inst->id.execSeqNum > lastMemBarrier);
+
+ /* Remember the barrier. We only have a notion of one
+ * barrier so this may result in some mem refs being
+ * delayed if they are between barriers */
+ lastMemBarrier = inst->id.execSeqNum;
+}
+
+void
+LSQ::LSQRequest::makePacket()
+{
+ /* Make the function idempotent */
+ if (packet)
+ return;
+
+ packet = makePacketForRequest(request, isLoad, this, data);
+ /* Null the ret data so we know not to deallocate it when the
+ * ret is destroyed. The data now belongs to the ret and
+ * the ret is responsible for its destruction */
+ data = NULL;
+}
+
+std::ostream &
+operator <<(std::ostream &os, LSQ::MemoryState state)
+{
+ switch (state) {
+ case LSQ::MemoryRunning:
+ os << "MemoryRunning";
+ break;
+ case LSQ::MemoryNeedsRetry:
+ os << "MemoryNeedsRetry";
+ break;
+ default:
+ os << "MemoryState-" << static_cast<int>(state);
+ break;
+ }
+ return os;
+}
+
+void
+LSQ::recvTimingSnoopReq(PacketPtr pkt)
+{
+ /* LLSC operations in Minor can't be speculative and are executed from
+ * the head of the requests queue. We shouldn't need to do more than
+ * this action on snoops. */
+
+ /* THREAD */
+ TheISA::handleLockedSnoop(cpu.getContext(0), pkt, cacheBlockMask);
+}
+
+}
diff --git a/src/cpu/minor/lsq.hh b/src/cpu/minor/lsq.hh
new file mode 100644
index 000000000..0998395e0
--- /dev/null
+++ b/src/cpu/minor/lsq.hh
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * A load/store queue that allows outstanding reads and writes.
+ *
+ */
+
+#ifndef __CPU_MINOR_NEW_LSQ_HH__
+#define __CPU_MINOR_NEW_LSQ_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/pipe_data.hh"
+#include "cpu/minor/trace.hh"
+
+namespace Minor
+{
+
+/* Forward declaration */
+class Execute;
+
+class LSQ : public Named
+{
+ protected:
+ /** My owner(s) */
+ MinorCPU &cpu;
+ Execute &execute;
+
+ protected:
+ /** State of memory access for head access. */
+ enum MemoryState
+ {
+ MemoryRunning, /* Default. Step dcache queues when possible. */
+ MemoryNeedsRetry /* Request rejected, will be asked to retry */
+ };
+
+ /** Print MemoryState values as shown in the enum definition */
+ friend std::ostream &operator <<(std::ostream &os,
+ MemoryState state);
+
+ /** Coverage of one address range with another */
+ enum AddrRangeCoverage
+ {
+ PartialAddrRangeCoverage, /* Two ranges partly overlap */
+ FullAddrRangeCoverage, /* One range fully covers another */
+ NoAddrRangeCoverage /* Two ranges are disjoint */
+ };
+
+ /** Exposable data port */
+ class DcachePort : public MinorCPU::MinorCPUPort
+ {
+ protected:
+ /** My owner */
+ LSQ &lsq;
+
+ public:
+ DcachePort(std::string name, LSQ &lsq_, MinorCPU &cpu) :
+ MinorCPU::MinorCPUPort(name, cpu), lsq(lsq_)
+ { }
+
+ protected:
+ bool recvTimingResp(PacketPtr pkt)
+ { return lsq.recvTimingResp(pkt); }
+
+ void recvRetry() { lsq.recvRetry(); }
+
+ void recvTimingSnoopReq(PacketPtr pkt)
+ { return lsq.recvTimingSnoopReq(pkt); }
+ };
+
+ DcachePort dcachePort;
+
+ public:
+ /** Derived SenderState to carry data access info. through address
+ * translation, the queues in this port and back from the memory
+ * system. */
+ class LSQRequest :
+ public BaseTLB::Translation, /* For TLB lookups */
+ public Packet::SenderState /* For packing into a Packet */
+ {
+ public:
+ /** Owning port */
+ LSQ &port;
+
+ /** Instruction which made this request */
+ MinorDynInstPtr inst;
+
+ /** Load/store indication used for building packet. This isn't
+ * carried by Request so we need to keep it here */
+ bool isLoad;
+
+ /** Dynamically allocated and populated data carried for
+ * building write packets */
+ PacketDataPtr data;
+
+ /* Requests carry packets on their way to the memory system.
+ * When a Packet returns from the memory system, its
+ * request needs to have its packet updated as this
+ * may have changed in flight */
+ PacketPtr packet;
+
+ /** The underlying request of this LSQRequest */
+ Request request;
+
+ /** Fault generated performing this request */
+ Fault fault;
+
+ /** Res from pushRequest */
+ uint64_t *res;
+
+ /** Was skipped. Set to indicate any reason (faulted, bad
+ * stream sequence number, in a fault shadow) that this
+ * request did not perform a memory transfer */
+ bool skipped;
+
+ /** This in an access other than a normal cacheable load
+ * that's visited the memory system */
+ bool issuedToMemory;
+
+ enum LSQRequestState
+ {
+ NotIssued, /* Newly created */
+ InTranslation, /* TLB accessed, no reply yet */
+ Translated, /* Finished address translation */
+ Failed, /* The starting start of FailedDataRequests */
+ RequestIssuing, /* Load/store issued to memory in the requests
+ queue */
+ StoreToStoreBuffer, /* Store in transfers on its way to the
+ store buffer */
+ RequestNeedsRetry, /* Retry needed for load */
+ StoreInStoreBuffer, /* Store in the store buffer, before issuing
+ a memory transfer */
+ StoreBufferIssuing, /* Store in store buffer and has been
+ issued */
+ StoreBufferNeedsRetry, /* Retry needed for store */
+ /* All completed states. Includes
+ completed loads, TLB faults and skipped requests whose
+ seqNum's no longer match */
+ Complete
+ };
+
+ LSQRequestState state;
+
+ protected:
+ /** BaseTLB::Translation interface */
+ void markDelayed() { }
+
+ public:
+ LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
+ PacketDataPtr data_ = NULL, uint64_t *res_ = NULL);
+
+ virtual ~LSQRequest();
+
+ public:
+ /** Make a packet to use with the memory transaction */
+ void makePacket();
+
+ /** Was no memory access attempted for this request? */
+ bool skippedMemAccess() { return skipped; }
+
+ /** Set this request as having been skipped before a memory
+ * transfer was attempt */
+ void setSkipped() { skipped = true; }
+
+ /** Does address range req1 (req1_addr to req1_addr + req1_size - 1)
+ * fully cover, partially cover or not cover at all the range req2 */
+ static AddrRangeCoverage containsAddrRangeOf(
+ Addr req1_addr, unsigned int req1_size,
+ Addr req2_addr, unsigned int req2_size);
+
+ /** Does this request's address range fully cover the range
+ * of other_request? */
+ AddrRangeCoverage containsAddrRangeOf(LSQRequest *other_request);
+
+ /** Start the address translation process for this request. This
+ * will issue a translation request to the TLB. */
+ virtual void startAddrTranslation() = 0;
+
+ /** Get the next packet to issue for this request. For split
+ * transfers, it will be necessary to step through the available
+ * packets by calling do { getHeadPacket ; stepToNextPacket } while
+ * (!sentAllPackets) and by retiring response using retireResponse */
+ virtual PacketPtr getHeadPacket() = 0;
+
+ /** Step to the next packet for the next call to getHeadPacket */
+ virtual void stepToNextPacket() = 0;
+
+ /** Have all packets been sent? */
+ virtual bool sentAllPackets() = 0;
+
+ /** True if this request has any issued packets in the memory
+ * system and so can't be interrupted until it gets responses */
+ virtual bool hasPacketsInMemSystem() = 0;
+
+ /** Retire a response packet into the LSQRequest packet possibly
+ * completing this transfer */
+ virtual void retireResponse(PacketPtr packet_) = 0;
+
+ /** Is this a request a barrier? */
+ virtual bool isBarrier();
+
+ /** This request, once processed by the requests/transfers
+ * queues, will need to go to the store buffer */
+ bool needsToBeSentToStoreBuffer();
+
+ /** Set state and output trace output */
+ void setState(LSQRequestState new_state);
+
+ /** Has this request been completed. This includes *all* reasons
+ * for completion: successful transfers, faults, skipped because
+ * of preceding faults */
+ bool isComplete() const;
+
+ /** MinorTrace report interface */
+ void reportData(std::ostream &os) const;
+ };
+
+ typedef LSQRequest *LSQRequestPtr;
+
+ friend std::ostream & operator <<(std::ostream &os,
+ AddrRangeCoverage state);
+
+ friend std::ostream & operator <<(std::ostream &os,
+ LSQRequest::LSQRequestState state);
+
+ protected:
+ /** Special request types that don't actually issue memory requests */
+ class SpecialDataRequest : public LSQRequest
+ {
+ protected:
+ /** TLB interace */
+ void finish(Fault fault_, RequestPtr request_, ThreadContext *tc,
+ BaseTLB::Mode mode)
+ { }
+
+ public:
+ /** Send single translation request */
+ void startAddrTranslation() { }
+
+ /** Get the head packet as counted by numIssuedFragments */
+ PacketPtr getHeadPacket()
+ { fatal("No packets in a SpecialDataRequest"); }
+
+ /** Step on numIssuedFragments */
+ void stepToNextPacket() { }
+
+ /** Has no packets to send */
+ bool sentAllPackets() { return true; }
+
+ /** Never sends any requests */
+ bool hasPacketsInMemSystem() { return false; }
+
+ /** Keep the given packet as the response packet
+ * LSQRequest::packet */
+ void retireResponse(PacketPtr packet_) { }
+
+ public:
+ SpecialDataRequest(LSQ &port_, MinorDynInstPtr inst_) :
+ /* Say this is a load, not actually relevant */
+ LSQRequest(port_, inst_, true, NULL, 0)
+ { }
+ };
+
+ /** FailedDataRequest represents requests from instructions that
+ * failed their predicates but need to ride the requests/transfers
+ * queues to maintain trace ordering */
+ class FailedDataRequest : public SpecialDataRequest
+ {
+ public:
+ FailedDataRequest(LSQ &port_, MinorDynInstPtr inst_) :
+ SpecialDataRequest(port_, inst_)
+ { state = Failed; }
+ };
+
+ /** Request for doing barrier accounting in the store buffer. Not
+ * for use outside that unit */
+ class BarrierDataRequest : public SpecialDataRequest
+ {
+ public:
+ bool isBarrier() { return true; }
+
+ public:
+ BarrierDataRequest(LSQ &port_, MinorDynInstPtr inst_) :
+ SpecialDataRequest(port_, inst_)
+ { state = Complete; }
+ };
+
+ /** SingleDataRequest is used for requests that don't fragment */
+ class SingleDataRequest : public LSQRequest
+ {
+ protected:
+ /** TLB interace */
+ void finish(Fault fault_, RequestPtr request_, ThreadContext *tc,
+ BaseTLB::Mode mode);
+
+ /** Has my only packet been sent to the memory system but has not
+ * yet been responded to */
+ bool packetInFlight;
+
+ /** Has the packet been at least sent to the memory system? */
+ bool packetSent;
+
+ public:
+ /** Send single translation request */
+ void startAddrTranslation();
+
+ /** Get the head packet as counted by numIssuedFragments */
+ PacketPtr getHeadPacket() { return packet; }
+
+ /** Remember that the packet has been sent */
+ void stepToNextPacket() { packetInFlight = true; packetSent = true; }
+
+ /** Has packet been sent */
+ bool hasPacketsInMemSystem() { return packetInFlight; }
+
+ /** packetInFlight can become false again, so need to check
+ * packetSent */
+ bool sentAllPackets() { return packetSent; }
+
+ /** Keep the given packet as the response packet
+ * LSQRequest::packet */
+ void retireResponse(PacketPtr packet_);
+
+ public:
+ SingleDataRequest(LSQ &port_, MinorDynInstPtr inst_,
+ bool isLoad_, PacketDataPtr data_ = NULL, uint64_t *res_ = NULL) :
+ LSQRequest(port_, inst_, isLoad_, data_, res_),
+ packetInFlight(false),
+ packetSent(false)
+ { }
+ };
+
+ class SplitDataRequest : public LSQRequest
+ {
+ protected:
+ /** Event to step between translations */
+ class TranslationEvent : public Event
+ {
+ protected:
+ SplitDataRequest &owner;
+
+ public:
+ TranslationEvent(SplitDataRequest &owner_)
+ : owner(owner_) { }
+
+ void process()
+ { owner.sendNextFragmentToTranslation(); }
+ };
+
+ TranslationEvent translationEvent;
+ protected:
+ /** Number of fragments this request is split into */
+ unsigned int numFragments;
+
+ /** Number of fragments in the address translation mechanism */
+ unsigned int numInTranslationFragments;
+
+ /** Number of fragments that have completed address translation,
+ * (numTranslatedFragments + numInTranslationFragments) <=
+ * numFragments. When numTranslatedFramgents == numFragments,
+ * translation is complete */
+ unsigned int numTranslatedFragments;
+
+ /** Number of fragments already issued (<= numFragments) */
+ unsigned int numIssuedFragments;
+
+ /** Number of fragments retired back to this request */
+ unsigned int numRetiredFragments;
+
+ /** Fragment Requests corresponding to the address ranges of
+ * each fragment */
+ std::vector<Request *> fragmentRequests;
+
+ /** Packets matching fragmentRequests to issue fragments to memory */
+ std::vector<Packet *> fragmentPackets;
+
+ protected:
+ /** TLB response interface */
+ void finish(Fault fault_, RequestPtr request_, ThreadContext *tc,
+ BaseTLB::Mode mode);
+
+ public:
+ SplitDataRequest(LSQ &port_, MinorDynInstPtr inst_,
+ bool isLoad_, PacketDataPtr data_ = NULL,
+ uint64_t *res_ = NULL);
+
+ ~SplitDataRequest();
+
+ public:
+ /** Make all the Requests for this transfer's fragments so that those
+ * requests can be sent for address translation */
+ void makeFragmentRequests();
+
+ /** Make the packets to go with the requests so they can be sent to
+ * the memory system */
+ void makeFragmentPackets();
+
+ /** Start a loop of do { sendNextFragmentToTranslation ;
+ * translateTiming ; finish } while (numTranslatedFragments !=
+ * numFragments) to complete all this requests' fragments' address
+ * translations */
+ void startAddrTranslation();
+
+ /** Get the head packet as counted by numIssuedFragments */
+ PacketPtr getHeadPacket();
+
+ /** Step on numIssuedFragments */
+ void stepToNextPacket();
+
+ bool hasPacketsInMemSystem()
+ { return numIssuedFragments != numRetiredFragments; }
+
+ /** Have we stepped past the end of fragmentPackets? */
+ bool sentAllPackets() { return numIssuedFragments == numFragments; }
+
+ /** For loads, paste the response data into the main
+ * response packet */
+ void retireResponse(PacketPtr packet_);
+
+ /** Part of the address translation loop, see startAddTranslation */
+ void sendNextFragmentToTranslation();
+ };
+
+ /** Store buffer. This contains stores which have been committed
+ * but whose memory transfers have not yet been issued. Load data
+ * can be forwarded out of the store buffer */
+ class StoreBuffer : public Named
+ {
+ public:
+ /** My owner */
+ LSQ &lsq;
+
+ /** Number of slots, this is a bound on the size of slots */
+ const unsigned int numSlots;
+
+ /** Maximum number of stores that can be issued per cycle */
+ const unsigned int storeLimitPerCycle;
+
+ public:
+ /** Queue of store requests on their way to memory */
+ std::deque<LSQRequestPtr> slots;
+
+ /** Number of occupied slots which have not yet issued a
+ * memory access */
+ unsigned int numUnissuedAccesses;
+
+ public:
+ StoreBuffer(std::string name_, LSQ &lsq_,
+ unsigned int store_buffer_size,
+ unsigned int store_limit_per_cycle);
+
+ public:
+ /** Can a new request be inserted into the queue? */
+ bool canInsert() const;
+
+ /** Delete the given request and free the slot it occupied */
+ void deleteRequest(LSQRequestPtr request);
+
+ /** Insert a request at the back of the queue */
+ void insert(LSQRequestPtr request);
+
+ /** Look for a store which satisfies the given load. Returns an
+ * indication whether the forwarding request can be wholly,
+ * partly or not all all satisfied. If the request can be
+ * wholly satisfied, the store buffer slot number which can be used
+ * is returned in found_slot */
+ AddrRangeCoverage canForwardDataToLoad(LSQRequestPtr request,
+ unsigned int &found_slot);
+
+ /** Fill the given packet with appropriate date from slot
+ * slot_number */
+ void forwardStoreData(LSQRequestPtr load, unsigned int slot_number);
+
+ /** Number of stores in the store buffer which have not been
+ * completely issued to the memory system */
+ unsigned int numUnissuedStores() { return numUnissuedAccesses; }
+
+ /** Drained if there is absolutely nothing left in the buffer */
+ bool isDrained() const { return slots.empty(); }
+
+ /** Try to issue more stores to memory */
+ void step();
+
+ /** Report queue contents for MinorTrace */
+ void minorTrace() const;
+ };
+
+ protected:
+ /** Most recent execSeqNum of a memory barrier instruction or
+ * 0 if there are no in-flight barriers. Useful as a
+ * dependency for early-issued memory operations */
+ InstSeqNum lastMemBarrier;
+
+ public:
+ /** Retry state of last issued memory transfer */
+ MemoryState state;
+
+ /** Maximum number of in-flight accesses issued to the memory system */
+ const unsigned int inMemorySystemLimit;
+
+ /** Memory system access width (and snap) in bytes */
+ const unsigned int lineWidth;
+
+ public:
+ /** The LSQ consists of three queues: requests, transfers and the
+ * store buffer storeBuffer. */
+
+ typedef Queue<LSQRequestPtr,
+ ReportTraitsPtrAdaptor<LSQRequestPtr>,
+ NoBubbleTraits<LSQRequestPtr> >
+ LSQQueue;
+
+ /** requests contains LSQRequests which have been issued to the TLB by
+ * calling ExecContext::readMem/writeMem (which in turn calls
+ * LSQ::pushRequest and LSQRequest::startAddrTranslation). Once they
+ * have a physical address, requests at the head of requests can be
+ * issued to the memory system. At this stage, it cannot be clear that
+ * memory accesses *must* happen (that there are no preceding faults or
+ * changes of flow of control) and so only cacheable reads are issued
+ * to memory.
+ * Cacheable stores are not issued at all (and just pass through
+ * 'transfers' in order) and all other transfers are stalled in requests
+ * until their corresponding instructions are at the head of the
+ * inMemInsts instruction queue and have the right streamSeqNum. */
+ LSQQueue requests;
+
+ /** Once issued to memory (or, for stores, just had their
+ * state changed to StoreToStoreBuffer) LSQRequests pass through
+ * transfers waiting for memory responses. At the head of transfers,
+ * Execute::commitInst can pick up the memory response for a request
+ * using LSQ::findResponse. Responses to be committed can then
+ * have ExecContext::completeAcc on them. Stores can then be pushed
+ * into the store buffer. All other transfers will then be complete. */
+ LSQQueue transfers;
+
+ /* The store buffer contains committed cacheable stores on
+ * their way to memory decoupled from subsequence instruction execution.
+ * Before trying to issue a cacheable read from 'requests' to memory,
+ * the store buffer is checked to see if a previous store contains the
+ * needed data (StoreBuffer::canForwardDataToLoad) which can be
+ * forwarded in lieu of a memory access. If there are outstanding
+ * stores in the transfers queue, they must be promoted to the store
+ * buffer (and so be commited) before they can be correctly checked
+ * for forwarding. */
+ StoreBuffer storeBuffer;
+
+ protected:
+ /** Count of the number of mem. accesses which have left the
+ * requests queue and are in the 'wild' in the memory system. */
+ unsigned int numAccessesInMemorySystem;
+
+ /** Number of requests in the DTLB in the requests queue */
+ unsigned int numAccessesInDTLB;
+
+ /** The number of stores in the transfers queue. Useful when
+ * testing if the store buffer contains all the forwardable stores */
+ unsigned int numStoresInTransfers;
+
+ /** The number of accesses which have been issued to the memory
+ * system but have not been committed/discarded *excluding*
+ * cacheable normal loads which don't need to be tracked */
+ unsigned int numAccessesIssuedToMemory;
+
+ /** The request (from either requests or the store buffer) which is
+ * currently waiting have its memory access retried */
+ LSQRequestPtr retryRequest;
+
+ /** Address Mask for a cache block (e.g. ~(cache_block_size-1)) */
+ Addr cacheBlockMask;
+
+ protected:
+ /** Try and issue a memory access for a translated request at the
+ * head of the requests queue. Also tries to move the request
+ * between queues */
+ void tryToSendToTransfers(LSQRequestPtr request);
+
+ /** Try to send (or resend) a memory request's next/only packet to
+ * the memory system. Returns true if the request was successfully
+ * sent to memory (and was also the last packet in a transfer) */
+ bool tryToSend(LSQRequestPtr request);
+
+ /** Clear a barrier (if it's the last one marked up in lastMemBarrier) */
+ void clearMemBarrier(MinorDynInstPtr inst);
+
+ /** Move a request between queues */
+ void moveFromRequestsToTransfers(LSQRequestPtr request);
+
+ /** Can a request be sent to the memory system */
+ bool canSendToMemorySystem();
+
+ public:
+ LSQ(std::string name_, std::string dcache_port_name_,
+ MinorCPU &cpu_, Execute &execute_,
+ unsigned int max_accesses_in_memory_system, unsigned int line_width,
+ unsigned int requests_queue_size, unsigned int transfers_queue_size,
+ unsigned int store_buffer_size,
+ unsigned int store_buffer_cycle_store_limit);
+
+ virtual ~LSQ();
+
+ public:
+ /** Step checks the queues to see if their are issuable transfers
+ * which were not otherwise picked up by tests at the end of other
+ * events.
+ *
+ * Steppable actions include deferred actions which couldn't be
+ * cascaded on the end of a memory response/TLB response event
+ * because of resource congestion. */
+ void step();
+
+ /** Is their space in the request queue to be able to push a request by
+ * issuing an isMemRef instruction */
+ bool canRequest() { return requests.unreservedRemainingSpace() != 0; }
+
+ /** Returns a response if it's at the head of the transfers queue and
+ * it's either complete or can be sent on to the store buffer. After
+ * calling, the request still remains on the transfer queue until
+ * popResponse is called */
+ LSQRequestPtr findResponse(MinorDynInstPtr inst);
+
+ /** Sanity check and pop the head response */
+ void popResponse(LSQRequestPtr response);
+
+ /** Must check this before trying to insert into the store buffer */
+ bool canPushIntoStoreBuffer() const { return storeBuffer.canInsert(); }
+
+ /** A store has been committed, please move it to the store buffer */
+ void sendStoreToStoreBuffer(LSQRequestPtr request);
+
+ /** Are there any accesses other than normal cached loads in the
+ * memory system or having received responses which need to be
+ * handled for their instruction's to be completed */
+ bool accessesInFlight() const
+ { return numAccessesIssuedToMemory != 0; }
+
+ /** A memory barrier instruction has been issued, remember its
+ * execSeqNum that we can avoid issuing memory ops until it is
+ * committed */
+ void issuedMemBarrierInst(MinorDynInstPtr inst);
+
+ /** Get the execSeqNum of the last issued memory barrier */
+ InstSeqNum getLastMemBarrier() const { return lastMemBarrier; }
+
+ /** Is there nothing left in the LSQ */
+ bool isDrained();
+
+ /** May need to be ticked next cycle as one of the queues contains
+ * an actionable transfers or address translation */
+ bool needsToTick();
+
+ /** Complete a barrier instruction. Where committed, makes a
+ * BarrierDataRequest and pushed it into the store buffer */
+ void completeMemBarrierInst(MinorDynInstPtr inst,
+ bool committed);
+
+ /** Single interface for readMem/writeMem to issue requests into
+ * the LSQ */
+ void pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
+ unsigned int size, Addr addr, unsigned int flags, uint64_t *res);
+
+ /** Push a predicate failed-representing request into the queues just
+ * to maintain commit order */
+ void pushFailedRequest(MinorDynInstPtr inst);
+
+ /** Memory interface */
+ bool recvTimingResp(PacketPtr pkt);
+ void recvRetry();
+ void recvTimingSnoopReq(PacketPtr pkt);
+
+ /** Return the raw-bindable port */
+ MinorCPU::MinorCPUPort &getDcachePort() { return dcachePort; }
+
+ void minorTrace() const;
+};
+
+/** Make a suitable packet for the given request. If the request is a store,
+ * data will be the payload data. If sender_state is NULL, it won't be
+ * pushed into the packet as senderState */
+PacketPtr makePacketForRequest(Request &request, bool isLoad,
+ Packet::SenderState *sender_state = NULL, PacketDataPtr data = NULL);
+}
+
+#endif /* __CPU_MINOR_NEW_LSQ_HH__ */
diff --git a/src/cpu/minor/pipe_data.cc b/src/cpu/minor/pipe_data.cc
new file mode 100644
index 000000000..447f9c0e7
--- /dev/null
+++ b/src/cpu/minor/pipe_data.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "cpu/minor/pipe_data.hh"
+
+namespace Minor
+{
+
+std::ostream &
+operator <<(std::ostream &os, BranchData::Reason reason)
+{
+ switch (reason)
+ {
+ case BranchData::NoBranch:
+ os << "NoBranch";
+ break;
+ case BranchData::UnpredictedBranch:
+ os << "UnpredictedBranch";
+ break;
+ case BranchData::BranchPrediction:
+ os << "BranchPrediction";
+ break;
+ case BranchData::CorrectlyPredictedBranch:
+ os << "CorrectlyPredictedBranch";
+ break;
+ case BranchData::BadlyPredictedBranch:
+ os << "BadlyPredictedBranch";
+ break;
+ case BranchData::BadlyPredictedBranchTarget:
+ os << "BadlyPredictedBranchTarget";
+ break;
+ case BranchData::Interrupt:
+ os << "Interrupt";
+ break;
+ case BranchData::SuspendThread:
+ os << "SuspendThread";
+ break;
+ case BranchData::WakeupFetch:
+ os << "WakeupFetch";
+ break;
+ case BranchData::HaltFetch:
+ os << "HaltFetch";
+ break;
+ }
+
+ return os;
+}
+
+bool
+BranchData::isStreamChange(const BranchData::Reason reason)
+{
+ bool ret = false;
+
+ switch (reason)
+ {
+ /* No change of stream (see the enum comment in pipe_data.hh) */
+ case NoBranch:
+ case CorrectlyPredictedBranch:
+ ret = false;
+ break;
+
+ /* Change of stream (Fetch1 should act on) */
+ case UnpredictedBranch:
+ case BranchPrediction:
+ case BadlyPredictedBranchTarget:
+ case BadlyPredictedBranch:
+ case SuspendThread:
+ case Interrupt:
+ case WakeupFetch:
+ case HaltFetch:
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
+bool
+BranchData::isBranch(const BranchData::Reason reason)
+{
+ bool ret = false;
+
+ switch (reason)
+ {
+ /* No change of stream (see the enum comment in pipe_data.hh) */
+ case NoBranch:
+ case CorrectlyPredictedBranch:
+ case SuspendThread:
+ case Interrupt:
+ case WakeupFetch:
+ case HaltFetch:
+ ret = false;
+ break;
+
+ /* Change of stream (Fetch1 should act on) */
+ case UnpredictedBranch:
+ case BranchPrediction:
+ case BadlyPredictedBranchTarget:
+ case BadlyPredictedBranch:
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
+void
+BranchData::reportData(std::ostream &os) const
+{
+ if (isBubble()) {
+ os << '-';
+ } else {
+ os << reason
+ << ';' << newStreamSeqNum << '.' << newPredictionSeqNum
+ << ";0x" << std::hex << target.instAddr() << std::dec
+ << ';';
+ inst->reportData(os);
+ }
+}
+
+std::ostream &
+operator <<(std::ostream &os, const BranchData &branch)
+{
+ os << branch.reason << " target: 0x"
+ << std::hex << branch.target.instAddr() << std::dec
+ << ' ' << *branch.inst
+ << ' ' << branch.newStreamSeqNum << "(stream)."
+ << branch.newPredictionSeqNum << "(pred)";
+
+ return os;
+}
+
+void
+ForwardLineData::setFault(Fault fault_)
+{
+ fault = fault_;
+ if (isFault())
+ bubbleFlag = false;
+}
+
+void
+ForwardLineData::allocateLine(unsigned int width_)
+{
+ lineWidth = width_;
+ bubbleFlag = false;
+
+ assert(!isFault());
+ assert(!line);
+
+ line = new uint8_t[width_];
+}
+
+void
+ForwardLineData::adoptPacketData(Packet *packet)
+{
+ this->packet = packet;
+ lineWidth = packet->req->getSize();
+ bubbleFlag = false;
+
+ assert(!isFault());
+ assert(!line);
+
+ line = packet->getPtr<uint8_t>();
+}
+
+void
+ForwardLineData::freeLine()
+{
+ /* Only free lines in non-faulting, non-bubble lines */
+ if (!isFault() && !isBubble()) {
+ assert(line);
+ /* If packet is not NULL then the line must belong to the packet so
+ * we don't need to separately deallocate the line */
+ if (packet) {
+ delete packet;
+ } else {
+ delete [] line;
+ }
+ line = NULL;
+ bubbleFlag = true;
+ }
+}
+
+void
+ForwardLineData::reportData(std::ostream &os) const
+{
+ if (isBubble())
+ os << '-';
+ else if (fault != NoFault)
+ os << "F;" << id;
+ else
+ os << id;
+}
+
+ForwardInstData::ForwardInstData(unsigned int width) :
+ numInsts(width)
+{
+ bubbleFill();
+}
+
+ForwardInstData::ForwardInstData(const ForwardInstData &src)
+{
+ *this = src;
+}
+
+ForwardInstData &
+ForwardInstData::operator =(const ForwardInstData &src)
+{
+ numInsts = src.numInsts;
+
+ for (unsigned int i = 0; i < src.numInsts; i++)
+ insts[i] = src.insts[i];
+
+ return *this;
+}
+
+bool
+ForwardInstData::isBubble() const
+{
+ return numInsts == 0 || insts[0]->isBubble();
+}
+
+void
+ForwardInstData::bubbleFill()
+{
+ for (unsigned int i = 0; i < numInsts; i++)
+ insts[i] = MinorDynInst::bubble();
+}
+
+void
+ForwardInstData::resize(unsigned int width)
+{
+ assert(width < MAX_FORWARD_INSTS);
+ numInsts = width;
+
+ bubbleFill();
+}
+
+void
+ForwardInstData::reportData(std::ostream &os) const
+{
+ if (isBubble()) {
+ os << '-';
+ } else {
+ unsigned int i = 0;
+
+ os << '(';
+ while (i != numInsts) {
+ insts[i]->reportData(os);
+ i++;
+ if (i != numInsts)
+ os << ',';
+ }
+ os << ')';
+ }
+}
+
+}
diff --git a/src/cpu/minor/pipe_data.hh b/src/cpu/minor/pipe_data.hh
new file mode 100644
index 000000000..4468cb89e
--- /dev/null
+++ b/src/cpu/minor/pipe_data.hh
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Contains class definitions for data flowing between pipeline stages in
+ * the top-level structure portion of this model. Latch types are also
+ * defined which pair forward/backward flowing data specific to each stage
+ * pair.
+ *
+ * No post-configuration inter-stage communication should *ever* take place
+ * outside these classes (except for reservation!)
+ */
+
+#ifndef __CPU_MINOR_PIPE_DATA_HH__
+#define __CPU_MINOR_PIPE_DATA_HH__
+
+#include "cpu/minor/buffers.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/base.hh"
+
+namespace Minor
+{
+
+/** Forward data betwen Execute and Fetch1 carrying change-of-address/stream
+ * information. */
+class BranchData /* : public ReportIF, public BubbleIF */
+{
+ public:
+ enum Reason
+ {
+ /* *** No change of stream (information to branch prediction) */
+
+ /* Don't branch at all (bubble) */
+ NoBranch,
+ /* Don't branch, but here's the details of a correct prediction
+ * that was executed */
+ CorrectlyPredictedBranch,
+
+ /* *** Change of stream */
+
+ /* Take an unpredicted branch */
+ UnpredictedBranch,
+ /* Take a branch on branch prediction data (from Fetch2) */
+ BranchPrediction,
+ /* Prediction of wrong target PC */
+ BadlyPredictedBranchTarget,
+ /* Bad branch prediction (didn't actually branch). Need to branch
+ * back to correct stream. If the target is wrong, use
+ * BadlyPredictedBranchTarget */
+ BadlyPredictedBranch,
+ /* Suspend fetching for this thread (inst->id.threadId).
+ * This will be woken up by another stream changing branch so
+ * count it as stream changing itself and expect pc to be the PC
+ * of the next instruction */
+ SuspendThread,
+ /* Wakeup fetching from Halted */
+ WakeupFetch,
+ /* Branch from an interrupt (no instruction) */
+ Interrupt,
+ /* Stop fetching in anticipation of of draining */
+ HaltFetch
+ };
+
+ /** Is a request with this reason actually a request to change the
+ * PC rather than a bubble or branch prediction information */
+ static bool isStreamChange(const BranchData::Reason reason);
+
+ /** Is a request with this reason actually a 'real' branch, that is,
+ * a stream change that's not just an instruction to Fetch1 to halt
+ * or wake up */
+ static bool isBranch(const BranchData::Reason reason);
+
+ public:
+ /** Explanation for this branch */
+ Reason reason;
+
+ /** Sequence number of new stream/prediction to be adopted */
+ InstSeqNum newStreamSeqNum;
+ InstSeqNum newPredictionSeqNum;
+
+ /** Starting PC of that stream */
+ TheISA::PCState target;
+
+ /** Instruction which caused this branch */
+ MinorDynInstPtr inst;
+
+ public:
+ BranchData() :
+ reason(NoBranch), newStreamSeqNum(0),
+ newPredictionSeqNum(0), target(TheISA::PCState(0)),
+ inst(MinorDynInst::bubble())
+ { }
+
+ BranchData(
+ Reason reason_,
+ InstSeqNum new_stream_seq_num,
+ InstSeqNum new_prediction_seq_num,
+ TheISA::PCState target,
+ MinorDynInstPtr inst_) :
+ reason(reason_),
+ newStreamSeqNum(new_stream_seq_num),
+ newPredictionSeqNum(new_prediction_seq_num),
+ target(target),
+ inst(inst_)
+ { }
+
+ /** BubbleIF interface */
+ static BranchData bubble() { return BranchData(); }
+ bool isBubble() const { return reason == NoBranch; }
+
+ /** As static isStreamChange but on this branch data */
+ bool isStreamChange() const { return isStreamChange(reason); }
+
+ /** As static isBranch but on this branch data */
+ bool isBranch() const { return isBranch(reason); }
+
+ /** ReportIF interface */
+ void reportData(std::ostream &os) const;
+};
+
+/** Print a branch reason enum */
+std::ostream &operator <<(std::ostream &os, BranchData::Reason reason);
+
+/** Print BranchData contents in a format suitable for DPRINTF comments, not
+ * for MinorTrace */
+std::ostream &operator <<(std::ostream &os, const BranchData &branch);
+
+/** Line fetch data in the forward direction. Contains a single cache line
+ * (or fragment of a line), its address, a sequence number assigned when
+ * that line was fetched and a bubbleFlag that can allow ForwardLineData to
+ * be used to represent the absence of line data in a pipeline. */
+class ForwardLineData /* : public ReportIF, public BubbleIF */
+{
+ private:
+ /** This line is a bubble. No other data member is required to be valid
+ * if this is true */
+ bool bubbleFlag;
+
+ public:
+ /** First byte address in the line. This is allowed to be
+ * <= pc.instAddr() */
+ Addr lineBaseAddr;
+
+ /** PC of the first requested inst within this line */
+ TheISA::PCState pc;
+
+ /** Explicit line width, don't rely on data.size */
+ unsigned int lineWidth;
+
+ public:
+ /** This line has a fault. The bubble flag will be false and seqNums
+ * will be valid but no data will */
+ Fault fault;
+
+ /** Thread, stream, prediction ... id of this line */
+ InstId id;
+
+ /** Line data. line[0] is the byte at address pc.instAddr(). Data is
+ * only valid upto lineWidth - 1. */
+ uint8_t *line;
+
+ /** Packet from which the line is taken */
+ Packet *packet;
+
+ public:
+ ForwardLineData() :
+ bubbleFlag(true),
+ lineBaseAddr(0),
+ lineWidth(0),
+ fault(NoFault),
+ line(NULL),
+ packet(NULL)
+ {
+ /* Make lines bubbles by default */
+ }
+
+ ~ForwardLineData() { line = NULL; }
+
+ public:
+ /** This is a fault, not a line */
+ bool isFault() const { return fault != NoFault; }
+
+ /** Set fault and possible clear the bubble flag */
+ void setFault(Fault fault_);
+
+ /** In-place initialise a ForwardLineData, freeing and overridding the
+ * line */
+ void allocateLine(unsigned int width_);
+
+ /** Use the data from a packet as line instead of allocating new
+ * space. On destruction of this object, the packet will be destroyed */
+ void adoptPacketData(Packet *packet);
+
+ /** Free this ForwardLineData line. Note that these are shared between
+ * line objects and so you must be careful when deallocating them.
+ * Copying of ForwardLineData can, therefore, be done by default copy
+ * constructors/assignment */
+ void freeLine();
+
+ /** BubbleIF interface */
+ static ForwardLineData bubble() { return ForwardLineData(); }
+ bool isBubble() const { return bubbleFlag; }
+
+ /** ReportIF interface */
+ void reportData(std::ostream &os) const;
+};
+
+/** Maximum number of instructions that can be carried by the pipeline. */
+const unsigned int MAX_FORWARD_INSTS = 16;
+
+/** Forward flowing data between Fetch2,Decode,Execute carrying a packet of
+ * instructions of a width appropriate to the configured stage widths.
+ * Also carries exception information where instructions are not valid */
+class ForwardInstData /* : public ReportIF, public BubbleIF */
+{
+ public:
+ /** Array of carried insts, ref counted */
+ MinorDynInstPtr insts[MAX_FORWARD_INSTS];
+
+ /** The number of insts slots that can be expected to be valid insts */
+ unsigned int numInsts;
+
+ public:
+ explicit ForwardInstData(unsigned int width = 0);
+
+ ForwardInstData(const ForwardInstData &src);
+
+ public:
+ /** Number of instructions carried by this object */
+ unsigned int width() const { return numInsts; }
+
+ /** Copy the inst array only as far as numInsts */
+ ForwardInstData &operator =(const ForwardInstData &src);
+
+ /** Resize a bubble/empty ForwardInstData and fill with bubbles */
+ void resize(unsigned int width);
+
+ /** Fill with bubbles from 0 to width() - 1 */
+ void bubbleFill();
+
+ /** BubbleIF interface */
+ bool isBubble() const;
+
+ /** ReportIF interface */
+ void reportData(std::ostream &os) const;
+};
+
+}
+
+#endif /* __CPU_MINOR_PIPE_DATA_HH__ */
diff --git a/src/cpu/minor/pipeline.cc b/src/cpu/minor/pipeline.cc
new file mode 100644
index 000000000..9d802234b
--- /dev/null
+++ b/src/cpu/minor/pipeline.cc
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include <algorithm>
+
+#include "cpu/minor/decode.hh"
+#include "cpu/minor/execute.hh"
+#include "cpu/minor/fetch1.hh"
+#include "cpu/minor/fetch2.hh"
+#include "cpu/minor/pipeline.hh"
+#include "debug/Drain.hh"
+#include "debug/MinorCPU.hh"
+#include "debug/MinorTrace.hh"
+#include "debug/Quiesce.hh"
+
+namespace Minor
+{
+
+Pipeline::Pipeline(MinorCPU &cpu_, MinorCPUParams &params) :
+ Ticked(cpu_, &(cpu_.BaseCPU::numCycles)),
+ cpu(cpu_),
+ allow_idling(params.enableIdling),
+ f1ToF2(cpu.name() + ".f1ToF2", "lines",
+ params.fetch1ToFetch2ForwardDelay),
+ f2ToF1(cpu.name() + ".f2ToF1", "prediction",
+ params.fetch1ToFetch2BackwardDelay, true),
+ f2ToD(cpu.name() + ".f2ToD", "insts",
+ params.fetch2ToDecodeForwardDelay),
+ dToE(cpu.name() + ".dToE", "insts",
+ params.decodeToExecuteForwardDelay),
+ eToF1(cpu.name() + ".eToF1", "branch",
+ params.executeBranchDelay),
+ execute(cpu.name() + ".execute", cpu, params,
+ dToE.output(), eToF1.input()),
+ decode(cpu.name() + ".decode", cpu, params,
+ f2ToD.output(), dToE.input(), execute.inputBuffer),
+ fetch2(cpu.name() + ".fetch2", cpu, params,
+ f1ToF2.output(), eToF1.output(), f2ToF1.input(), f2ToD.input(),
+ decode.inputBuffer),
+ fetch1(cpu.name() + ".fetch1", cpu, params,
+ eToF1.output(), f1ToF2.input(), f2ToF1.output(), fetch2.inputBuffer),
+ activityRecorder(cpu.name() + ".activity", Num_StageId,
+ /* The max depth of inter-stage FIFOs */
+ std::max(params.fetch1ToFetch2ForwardDelay,
+ std::max(params.fetch2ToDecodeForwardDelay,
+ std::max(params.decodeToExecuteForwardDelay,
+ params.executeBranchDelay)))),
+ needToSignalDrained(false)
+{
+ if (params.fetch1ToFetch2ForwardDelay < 1) {
+ fatal("%s: fetch1ToFetch2ForwardDelay must be >= 1 (%d)\n",
+ cpu.name(), params.fetch1ToFetch2ForwardDelay);
+ }
+
+ if (params.fetch2ToDecodeForwardDelay < 1) {
+ fatal("%s: fetch2ToDecodeForwardDelay must be >= 1 (%d)\n",
+ cpu.name(), params.fetch2ToDecodeForwardDelay);
+ }
+
+ if (params.decodeToExecuteForwardDelay < 1) {
+ fatal("%s: decodeToExecuteForwardDelay must be >= 1 (%d)\n",
+ cpu.name(), params.decodeToExecuteForwardDelay);
+ }
+
+ if (params.executeBranchDelay < 1) {
+ fatal("%s: executeBranchDelay must be >= 1\n",
+ cpu.name(), params.executeBranchDelay);
+ }
+}
+
+void
+Pipeline::minorTrace() const
+{
+ fetch1.minorTrace();
+ f1ToF2.minorTrace();
+ f2ToF1.minorTrace();
+ fetch2.minorTrace();
+ f2ToD.minorTrace();
+ decode.minorTrace();
+ dToE.minorTrace();
+ execute.minorTrace();
+ eToF1.minorTrace();
+ activityRecorder.minorTrace();
+}
+
+void
+Pipeline::evaluate()
+{
+ /* Note that it's important to evaluate the stages in order to allow
+ * 'immediate', 0-time-offset TimeBuffer activity to be visible from
+ * later stages to earlier ones in the same cycle */
+ execute.evaluate();
+ decode.evaluate();
+ fetch2.evaluate();
+ fetch1.evaluate();
+
+ if (DTRACE(MinorTrace))
+ minorTrace();
+
+ /* Update the time buffers after the stages */
+ f1ToF2.evaluate();
+ f2ToF1.evaluate();
+ f2ToD.evaluate();
+ dToE.evaluate();
+ eToF1.evaluate();
+
+ /* The activity recorder must be be called after all the stages and
+ * before the idler (which acts on the advice of the activity recorder */
+ activityRecorder.evaluate();
+
+ if (allow_idling) {
+ /* Become idle if we can but are not draining */
+ if (!activityRecorder.active() && !needToSignalDrained) {
+ DPRINTF(Quiesce, "Suspending as the processor is idle\n");
+ stop();
+ }
+
+ /* Deactivate all stages. Note that the stages *could*
+ * activate and deactivate themselves but that's fraught
+ * with additional difficulty.
+ * As organised herre */
+ activityRecorder.deactivateStage(Pipeline::CPUStageId);
+ activityRecorder.deactivateStage(Pipeline::Fetch1StageId);
+ activityRecorder.deactivateStage(Pipeline::Fetch2StageId);
+ activityRecorder.deactivateStage(Pipeline::DecodeStageId);
+ activityRecorder.deactivateStage(Pipeline::ExecuteStageId);
+ }
+
+ if (needToSignalDrained) /* Must be draining */
+ {
+ DPRINTF(Drain, "Still draining\n");
+ if (isDrained()) {
+ DPRINTF(Drain, "Signalling end of draining\n");
+ cpu.signalDrainDone();
+ needToSignalDrained = false;
+ stop();
+ }
+ }
+}
+
+MinorCPU::MinorCPUPort &
+Pipeline::getInstPort()
+{
+ return fetch1.getIcachePort();
+}
+
+MinorCPU::MinorCPUPort &
+Pipeline::getDataPort()
+{
+ return execute.getDcachePort();
+}
+
+void
+Pipeline::wakeupFetch()
+{
+ execute.wakeupFetch();
+}
+
+unsigned int
+Pipeline::drain(DrainManager *manager)
+{
+ DPRINTF(MinorCPU, "Draining pipeline by halting inst fetches. "
+ " Execution should drain naturally\n");
+
+ execute.drain();
+
+ /* Make sure that needToSignalDrained isn't accidentally set if we
+ * are 'pre-drained' */
+ bool drained = isDrained();
+ needToSignalDrained = !drained;
+
+ return (drained ? 0 : 1);
+}
+
+void
+Pipeline::drainResume()
+{
+ DPRINTF(Drain, "Drain resume\n");
+ execute.drainResume();
+}
+
+bool
+Pipeline::isDrained()
+{
+ bool fetch1_drained = fetch1.isDrained();
+ bool fetch2_drained = fetch2.isDrained();
+ bool decode_drained = decode.isDrained();
+ bool execute_drained = execute.isDrained();
+
+ bool f1_to_f2_drained = f1ToF2.empty();
+ bool f2_to_f1_drained = f2ToF1.empty();
+ bool f2_to_d_drained = f2ToD.empty();
+ bool d_to_e_drained = dToE.empty();
+
+ bool ret = fetch1_drained && fetch2_drained &&
+ decode_drained && execute_drained &&
+ f1_to_f2_drained && f2_to_f1_drained &&
+ f2_to_d_drained && d_to_e_drained;
+
+ DPRINTF(MinorCPU, "Pipeline undrained stages state:%s%s%s%s%s%s%s%s\n",
+ (fetch1_drained ? "" : " Fetch1"),
+ (fetch2_drained ? "" : " Fetch2"),
+ (decode_drained ? "" : " Decode"),
+ (execute_drained ? "" : " Execute"),
+ (f1_to_f2_drained ? "" : " F1->F2"),
+ (f2_to_f1_drained ? "" : " F2->F1"),
+ (f2_to_d_drained ? "" : " F2->D"),
+ (d_to_e_drained ? "" : " D->E")
+ );
+
+ return ret;
+}
+
+}
diff --git a/src/cpu/minor/pipeline.hh b/src/cpu/minor/pipeline.hh
new file mode 100644
index 000000000..893efbf50
--- /dev/null
+++ b/src/cpu/minor/pipeline.hh
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * The constructed pipeline. Kept out of MinorCPU to keep the interface
+ * between the CPU and its grubby implementation details clean.
+ */
+
+#ifndef __CPU_MINOR_PIPELINE_HH__
+#define __CPU_MINOR_PIPELINE_HH__
+
+#include "cpu/minor/activity.hh"
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/decode.hh"
+#include "cpu/minor/execute.hh"
+#include "cpu/minor/fetch1.hh"
+#include "cpu/minor/fetch2.hh"
+#include "params/MinorCPU.hh"
+#include "sim/ticked_object.hh"
+
+namespace Minor
+{
+
+/**
+ * @namespace Minor
+ *
+ * Minor contains all the definitions within the MinorCPU apart from the CPU
+ * class itself
+ */
+
+/** The constructed pipeline. Kept out of MinorCPU to keep the interface
+ * between the CPU and its grubby implementation details clean. */
+class Pipeline : public Ticked
+{
+ protected:
+ MinorCPU &cpu;
+
+ /** Allow cycles to be skipped when the pipeline is idle */
+ bool allow_idling;
+
+ Latch<ForwardLineData> f1ToF2;
+ Latch<BranchData> f2ToF1;
+ Latch<ForwardInstData> f2ToD;
+ Latch<ForwardInstData> dToE;
+ Latch<BranchData> eToF1;
+
+ Execute execute;
+ Decode decode;
+ Fetch2 fetch2;
+ Fetch1 fetch1;
+
+ /** Activity recording for the pipeline. This is access through the CPU
+ * by the pipeline stages but belongs to the Pipeline as it is the
+ * cleanest place to initialise it */
+ MinorActivityRecorder activityRecorder;
+
+ public:
+ /** Enumerated ids of the 'stages' for the activity recorder */
+ enum StageId
+ {
+ /* A stage representing wakeup of the whole processor */
+ CPUStageId = 0,
+ /* Real pipeline stages */
+ Fetch1StageId, Fetch2StageId, DecodeStageId, ExecuteStageId,
+ Num_StageId /* Stage count */
+ };
+
+ /** True after drain is called but draining isn't complete */
+ bool needToSignalDrained;
+
+ public:
+ Pipeline(MinorCPU &cpu_, MinorCPUParams &params);
+
+ public:
+ /** Wake up the Fetch unit. This is needed on thread activation esp.
+ * after quiesce wakeup */
+ void wakeupFetch();
+
+ /** Try to drain the CPU */
+ unsigned int drain(DrainManager *manager);
+
+ void drainResume();
+
+ /** Test to see if the CPU is drained */
+ bool isDrained();
+
+ /** A custom evaluate allows report in the right place (between
+ * stages and pipeline advance) */
+ void evaluate();
+
+ void minorTrace() const;
+
+ /** Functions below here are BaseCPU operations passed on to pipeline
+ * stages */
+
+ /** Return the IcachePort belonging to Fetch1 for the CPU */
+ MinorCPU::MinorCPUPort &getInstPort();
+ /** Return the DcachePort belonging to Execute for the CPU */
+ MinorCPU::MinorCPUPort &getDataPort();
+
+ /** To give the activity recorder to the CPU */
+ MinorActivityRecorder *getActivityRecorder() { return &activityRecorder; }
+};
+
+}
+
+#endif /* __CPU_MINOR_PIPELINE_HH__ */
diff --git a/src/cpu/minor/scoreboard.cc b/src/cpu/minor/scoreboard.cc
new file mode 100644
index 000000000..f6b1f7944
--- /dev/null
+++ b/src/cpu/minor/scoreboard.cc
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "arch/registers.hh"
+#include "cpu/minor/scoreboard.hh"
+#include "cpu/reg_class.hh"
+#include "debug/MinorScoreboard.hh"
+#include "debug/MinorTiming.hh"
+
+namespace Minor
+{
+
+bool
+Scoreboard::findIndex(RegIndex reg, Index &scoreboard_index)
+{
+ RegClass reg_class = regIdxToClass(reg);
+ bool ret = false;
+
+ if (reg == TheISA::ZeroReg) {
+ /* Don't bother with the zero register */
+ ret = false;
+ } else {
+ switch (reg_class)
+ {
+ case IntRegClass:
+ scoreboard_index = reg;
+ ret = true;
+ break;
+ case FloatRegClass:
+ scoreboard_index = TheISA::NumIntRegs + TheISA::NumCCRegs +
+ reg - TheISA::FP_Reg_Base;
+ ret = true;
+ break;
+ case CCRegClass:
+ scoreboard_index = TheISA::NumIntRegs + reg - TheISA::FP_Reg_Base;
+ ret = true;
+ break;
+ case MiscRegClass:
+ /* Don't bother with Misc registers */
+ ret = false;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/** Flatten a RegIndex, irrespective of what reg type it's pointing to */
+static TheISA::RegIndex
+flattenRegIndex(TheISA::RegIndex reg, ThreadContext *thread_context)
+{
+ RegClass reg_class = regIdxToClass(reg);
+ TheISA::RegIndex ret = reg;
+
+ switch (reg_class)
+ {
+ case IntRegClass:
+ ret = thread_context->flattenIntIndex(reg);
+ break;
+ case FloatRegClass:
+ ret = thread_context->flattenFloatIndex(reg);
+ break;
+ case CCRegClass:
+ ret = thread_context->flattenCCIndex(reg);
+ break;
+ case MiscRegClass:
+ /* Don't bother to flatten misc regs as we don't need them here */
+ /* return thread_context->flattenMiscIndex(reg); */
+ ret = reg;
+ break;
+ }
+
+ return ret;
+}
+
+void
+Scoreboard::markupInstDests(MinorDynInstPtr inst, Cycles retire_time,
+ ThreadContext *thread_context, bool mark_unpredictable)
+{
+ if (inst->isFault())
+ return;
+
+ StaticInstPtr staticInst = inst->staticInst;
+ unsigned int num_dests = staticInst->numDestRegs();
+
+ /** Mark each destination register */
+ for (unsigned int dest_index = 0; dest_index < num_dests;
+ dest_index++)
+ {
+ RegIndex reg = flattenRegIndex(
+ staticInst->destRegIdx(dest_index), thread_context);
+ Index index;
+
+ if (findIndex(reg, index)) {
+ if (mark_unpredictable)
+ numUnpredictableResults[index]++;
+
+ inst->flatDestRegIdx[dest_index] = reg;
+
+ numResults[index]++;
+ returnCycle[index] = retire_time;
+ /* We should be able to rely on only being given accending
+ * execSeqNums, but sanity check */
+ if (inst->id.execSeqNum > writingInst[index]) {
+ writingInst[index] = inst->id.execSeqNum;
+ fuIndices[index] = inst->fuIndex;
+ }
+
+ DPRINTF(MinorScoreboard, "Marking up inst: %s"
+ " regIndex: %d final numResults: %d returnCycle: %d\n",
+ *inst, index, numResults[index], returnCycle[index]);
+ } else {
+ /* Use ZeroReg to mark invalid/untracked dests */
+ inst->flatDestRegIdx[dest_index] = TheISA::ZeroReg;
+ }
+ }
+}
+
+InstSeqNum
+Scoreboard::execSeqNumToWaitFor(MinorDynInstPtr inst,
+ ThreadContext *thread_context)
+{
+ InstSeqNum ret = 0;
+
+ if (inst->isFault())
+ return ret;
+
+ StaticInstPtr staticInst = inst->staticInst;
+ unsigned int num_srcs = staticInst->numSrcRegs();
+
+ for (unsigned int src_index = 0; src_index < num_srcs; src_index++) {
+ RegIndex reg = flattenRegIndex(staticInst->srcRegIdx(src_index),
+ thread_context);
+ unsigned short int index;
+
+ if (findIndex(reg, index)) {
+ if (writingInst[index] > ret)
+ ret = writingInst[index];
+ }
+ }
+
+ DPRINTF(MinorScoreboard, "Inst: %s depends on execSeqNum: %d\n",
+ *inst, ret);
+
+ return ret;
+}
+
+void
+Scoreboard::clearInstDests(MinorDynInstPtr inst, bool clear_unpredictable)
+{
+ if (inst->isFault())
+ return;
+
+ StaticInstPtr staticInst = inst->staticInst;
+ unsigned int num_dests = staticInst->numDestRegs();
+
+ /** Mark each destination register */
+ for (unsigned int dest_index = 0; dest_index < num_dests;
+ dest_index++)
+ {
+ RegIndex reg = inst->flatDestRegIdx[dest_index];
+ Index index;
+
+ if (findIndex(reg, index)) {
+ if (clear_unpredictable && numUnpredictableResults[index] != 0)
+ numUnpredictableResults[index] --;
+
+ numResults[index] --;
+
+ if (numResults[index] == 0) {
+ returnCycle[index] = Cycles(0);
+ writingInst[index] = 0;
+ fuIndices[index] = -1;
+ }
+
+ DPRINTF(MinorScoreboard, "Clearing inst: %s"
+ " regIndex: %d final numResults: %d\n",
+ *inst, index, numResults[index]);
+ }
+ }
+}
+
+bool
+Scoreboard::canInstIssue(MinorDynInstPtr inst,
+ const std::vector<Cycles> *src_reg_relative_latencies,
+ const std::vector<bool> *cant_forward_from_fu_indices,
+ Cycles now, ThreadContext *thread_context)
+{
+ /* Always allow fault to be issued */
+ if (inst->isFault())
+ return true;
+
+ StaticInstPtr staticInst = inst->staticInst;
+ unsigned int num_srcs = staticInst->numSrcRegs();
+
+ /* Default to saying you can issue */
+ bool ret = true;
+
+ unsigned int num_relative_latencies = 0;
+ Cycles default_relative_latency = Cycles(0);
+
+ /* Where relative latencies are given, the default is the last
+ * one as that allows the rel. lat. list to be shorted than the
+ * number of src. regs */
+ if (src_reg_relative_latencies &&
+ src_reg_relative_latencies->size() != 0)
+ {
+ num_relative_latencies = src_reg_relative_latencies->size();
+ default_relative_latency = (*src_reg_relative_latencies)
+ [num_relative_latencies-1];
+ }
+
+ /* For each source register, find the latest result */
+ unsigned int src_index = 0;
+ while (src_index < num_srcs && /* More registers */
+ ret /* Still possible */)
+ {
+ RegIndex reg = flattenRegIndex(staticInst->srcRegIdx(src_index),
+ thread_context);
+ unsigned short int index;
+
+ if (findIndex(reg, index)) {
+ bool cant_forward = fuIndices[index] != 1 &&
+ cant_forward_from_fu_indices &&
+ index < cant_forward_from_fu_indices->size() &&
+ (*cant_forward_from_fu_indices)[index];
+
+ Cycles relative_latency = (cant_forward ? Cycles(0) :
+ (src_index >= num_relative_latencies ?
+ default_relative_latency :
+ (*src_reg_relative_latencies)[src_index]));
+
+ if (returnCycle[index] > (now + relative_latency) ||
+ numUnpredictableResults[index] != 0)
+ {
+ ret = false;
+ }
+ }
+ src_index++;
+ }
+
+ if (DTRACE(MinorTiming)) {
+ if (ret && num_srcs > num_relative_latencies &&
+ num_relative_latencies != 0)
+ {
+ DPRINTF(MinorTiming, "Warning, inst: %s timing extra decode has"
+ " more src. regs: %d than relative latencies: %d\n",
+ staticInst->disassemble(0), num_srcs, num_relative_latencies);
+ }
+ }
+
+ return ret;
+}
+
+void
+Scoreboard::minorTrace() const
+{
+ std::ostringstream result_stream;
+
+ bool printed_element = false;
+
+ unsigned int i = 0;
+ while (i < numRegs) {
+ unsigned short int num_results = numResults[i];
+ unsigned short int num_unpredictable_results =
+ numUnpredictableResults[i];
+
+ if (!(num_results == 0 && num_unpredictable_results == Cycles(0))) {
+ if (printed_element)
+ result_stream << ',';
+
+ result_stream << '(' << i << ','
+ << num_results << '/'
+ << num_unpredictable_results << '/'
+ << returnCycle[i] << '/'
+ << writingInst[i] << ')';
+
+ printed_element = true;
+ }
+
+ i++;
+ }
+
+ MINORTRACE("busy=%s\n", result_stream.str());
+}
+
+}
diff --git a/src/cpu/minor/scoreboard.hh b/src/cpu/minor/scoreboard.hh
new file mode 100644
index 000000000..711bcafb2
--- /dev/null
+++ b/src/cpu/minor/scoreboard.hh
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * A simple instruction scoreboard for tracking dependencies in Execute.
+ */
+
+#ifndef __CPU_MINOR_SCOREBOARD_HH__
+#define __CPU_MINOR_SCOREBOARD_HH__
+
+#include "cpu/minor/cpu.hh"
+#include "cpu/minor/dyn_inst.hh"
+#include "cpu/minor/trace.hh"
+
+namespace Minor
+{
+
+/** A scoreboard of register dependencies including, for each register:
+ * The number of in-flight instructions which will generate a result for
+ * this register */
+class Scoreboard : public Named
+{
+ public:
+ /** The number of registers in the Scoreboard. These
+ * are just the integer, CC and float registers packed
+ * together with integer regs in the range [0,NumIntRegs-1],
+ * CC regs in the range [NumIntRegs, NumIntRegs+NumCCRegs-1]
+ * and float regs in the range
+ * [NumIntRegs+NumCCRegs, NumFloatRegs+NumIntRegs+NumCCRegs-1] */
+ const unsigned numRegs;
+
+ /** Type to use for thread context registers */
+ typedef TheISA::RegIndex RegIndex;
+
+ /** Type to use when indexing numResults */
+ typedef unsigned short int Index;
+
+ /** Count of the number of in-flight instructions that
+ * have results for each register */
+ std::vector<Index> numResults;
+
+ /** Count of the number of results which can't be predicted */
+ std::vector<Index> numUnpredictableResults;
+
+ /** Index of the FU generating this result */
+ std::vector<int> fuIndices;
+
+ /** The estimated cycle number that the result will be presented.
+ * This can be offset from to allow forwarding to be simulated as
+ * long as instruction completion is *strictly* in order with
+ * respect to instructions with unpredictable result timing */
+ std::vector<Cycles> returnCycle;
+
+ /** The execute sequence number of the most recent inst to generate this
+ * register value */
+ std::vector<InstSeqNum> writingInst;
+
+ public:
+ Scoreboard(const std::string &name) :
+ Named(name),
+ numRegs(TheISA::NumIntRegs + TheISA::NumCCRegs +
+ TheISA::NumFloatRegs),
+ numResults(numRegs, 0),
+ numUnpredictableResults(numRegs, 0),
+ fuIndices(numRegs, 0),
+ returnCycle(numRegs, Cycles(0)),
+ writingInst(numRegs, 0)
+ { }
+
+ public:
+ /** Sets scoreboard_index to the index into numResults of the
+ * given register index. Returns true if the given register
+ * is in the scoreboard and false if it isn't */
+ bool findIndex(RegIndex reg, Index &scoreboard_index);
+
+ /** Mark up an instruction's effects by incrementing
+ * numResults counts. If mark_unpredictable is true, the inst's
+ * destination registers are marked as being unpredictable without
+ * an estimated retire time */
+ void markupInstDests(MinorDynInstPtr inst, Cycles retire_time,
+ ThreadContext *thread_context, bool mark_unpredictable);
+
+ /** Clear down the dependencies for this instruction. clear_unpredictable
+ * must match mark_unpredictable for the same inst. */
+ void clearInstDests(MinorDynInstPtr inst, bool clear_unpredictable);
+
+ /** Returns the exec sequence number of the most recent inst on
+ * which the given inst depends. Useful for determining which
+ * inst must actually be committed before a dependent inst
+ * can call initiateAcc */
+ InstSeqNum execSeqNumToWaitFor(MinorDynInstPtr inst,
+ ThreadContext *thread_context);
+
+ /** Can this instruction be issued. Are any of its source registers
+ * due to be written by other marked-up instructions in flight */
+ bool canInstIssue(MinorDynInstPtr inst,
+ const std::vector<Cycles> *src_reg_relative_latencies,
+ const std::vector<bool> *cant_forward_from_fu_indices,
+ Cycles now, ThreadContext *thread_context);
+
+ /** MinorTraceIF interface */
+ void minorTrace() const;
+};
+
+}
+
+#endif /* __CPU_MINOR_SCOREBOARD_HH__ */
diff --git a/src/cpu/minor/stats.cc b/src/cpu/minor/stats.cc
new file mode 100644
index 000000000..baa0aa7f3
--- /dev/null
+++ b/src/cpu/minor/stats.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "cpu/minor/stats.hh"
+
+namespace Minor
+{
+
+MinorStats::MinorStats()
+{ }
+
+void
+MinorStats::regStats(const std::string &name, BaseCPU &baseCpu)
+{
+ numInsts
+ .name(name + ".committedInsts")
+ .desc("Number of instructions committed");
+
+ numOps
+ .name(name + ".committedOps")
+ .desc("Number of ops (including micro ops) committed");
+
+ numDiscardedOps
+ .name(name + ".discardedOps")
+ .desc("Number of ops (including micro ops) which were discarded "
+ "before commit");
+
+ numFetchSuspends
+ .name(name + ".numFetchSuspends")
+ .desc("Number of times Execute suspended instruction fetching");
+
+ quiesceCycles
+ .name(name + ".quiesceCycles")
+ .desc("Total number of cycles that CPU has spent quiesced or waiting "
+ "for an interrupt")
+ .prereq(quiesceCycles);
+
+ cpi
+ .name(name + ".cpi")
+ .desc("CPI: cycles per instruction")
+ .precision(6);
+ cpi = baseCpu.numCycles / numInsts;
+
+ ipc
+ .name(name + ".ipc")
+ .desc("IPC: instructions per cycle")
+ .precision(6);
+ ipc = numInsts / baseCpu.numCycles;
+}
+
+};
diff --git a/src/cpu/minor/stats.hh b/src/cpu/minor/stats.hh
new file mode 100644
index 000000000..dc246304d
--- /dev/null
+++ b/src/cpu/minor/stats.hh
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * The stats for MinorCPU separated from the CPU definition.
+ */
+
+#ifndef __CPU_MINOR_STATS_HH__
+#define __CPU_MINOR_STATS_HH__
+
+#include "base/statistics.hh"
+#include "cpu/base.hh"
+#include "sim/ticked_object.hh"
+
+namespace Minor
+{
+
+/** Currently unused stats class. */
+class MinorStats
+{
+ public:
+ /** Number of simulated instructions */
+ Stats::Scalar numInsts;
+
+ /** Number of simulated insts and microops */
+ Stats::Scalar numOps;
+
+ /** Number of ops discarded before committing */
+ Stats::Scalar numDiscardedOps;
+
+ /** Number of times fetch was asked to suspend by Execute */
+ Stats::Scalar numFetchSuspends;
+
+ /** Number of cycles in quiescent state */
+ Stats::Scalar quiesceCycles;
+
+ /** CPI/IPC for total cycle counts and macro insts */
+ Stats::Formula cpi;
+ Stats::Formula ipc;
+
+ public:
+ MinorStats();
+
+ public:
+ void regStats(const std::string &name, BaseCPU &baseCpu);
+};
+
+}
+
+#endif /* __CPU_MINOR_STATS_HH__ */
diff --git a/src/cpu/minor/trace.hh b/src/cpu/minor/trace.hh
new file mode 100644
index 000000000..9bbe09750
--- /dev/null
+++ b/src/cpu/minor/trace.hh
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * This file contains miscellaneous classes and functions for formatting
+ * general trace information and also MinorTrace information.
+ *
+ * MinorTrace is this model's cycle-by-cycle trace information for use by
+ * minorview.
+ */
+
+#ifndef __CPU_MINOR_TRACE_HH__
+#define __CPU_MINOR_TRACE_HH__
+
+#include <string>
+
+#include "base/trace.hh"
+#include "debug/MinorTrace.hh"
+
+namespace Minor
+{
+
+/** DPRINTFN for MinorTrace reporting */
+#define MINORTRACE(...) \
+ DPRINTF(MinorTrace, "MinorTrace: " __VA_ARGS__)
+
+/** DPRINTFN for MinorTrace MinorInst line reporting */
+#define MINORINST(sim_object, ...) \
+ DPRINTFS(MinorTrace, (sim_object), "MinorInst: " __VA_ARGS__)
+
+/** DPRINTFN for MinorTrace MinorLine line reporting */
+#define MINORLINE(sim_object, ...) \
+ DPRINTFS(MinorTrace, (sim_object), "MinorLine: " __VA_ARGS__)
+
+}
+
+#endif /* __CPU_MINOR_TRACE_HH__ */
diff --git a/src/cpu/pred/SConscript b/src/cpu/pred/SConscript
index 5b2ecceef..bb9342f06 100644
--- a/src/cpu/pred/SConscript
+++ b/src/cpu/pred/SConscript
@@ -30,7 +30,8 @@
Import('*')
-if 'InOrderCPU' in env['CPU_MODELS'] or 'O3CPU' in env['CPU_MODELS']:
+if 'InOrderCPU' in env['CPU_MODELS'] or 'O3CPU' in env['CPU_MODELS'] \
+ or 'Minor' in env['CPU_MODELS']:
SimObject('BranchPredictor.py')
Source('bpred_unit.cc')
diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh
index f598c920d..375b7d0ba 100644
--- a/src/cpu/static_inst.hh
+++ b/src/cpu/static_inst.hh
@@ -59,6 +59,11 @@ class CheckerCPU;
class AtomicSimpleCPU;
class TimingSimpleCPU;
class InorderCPU;
+namespace Minor
+{
+ class ExecContext;
+};
+
class SymbolTable;
namespace Trace {
diff --git a/src/cpu/timing_expr.cc b/src/cpu/timing_expr.cc
new file mode 100644
index 000000000..d6d904956
--- /dev/null
+++ b/src/cpu/timing_expr.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "base/intmath.hh"
+#include "cpu/timing_expr.hh"
+
+TimingExprEvalContext::TimingExprEvalContext (StaticInstPtr inst_,
+ ThreadContext *thread_,
+ TimingExprLet *let_) :
+ inst(inst_), thread(thread_), let(let_)
+{
+ /* Reserve space to hold the results of evaluating the
+ * let expressions */
+ if (let) {
+ unsigned int num_defns = let->defns.size();
+
+ results.resize(num_defns, 0);
+ resultAvailable.resize(num_defns, false);
+ }
+}
+
+uint64_t TimingExprSrcReg::eval(TimingExprEvalContext &context)
+{
+ return context.inst->srcRegIdx(index);
+}
+
+uint64_t TimingExprReadIntReg::eval(TimingExprEvalContext &context)
+{
+ return context.thread->readIntReg(reg->eval(context));
+}
+
+uint64_t TimingExprLet::eval(TimingExprEvalContext &context)
+{
+ TimingExprEvalContext new_context(context.inst,
+ context.thread, this);
+
+ return expr->eval(new_context);
+}
+
+uint64_t TimingExprRef::eval(TimingExprEvalContext &context)
+{
+ /* Lookup the result, evaluating if necessary. @todo, this
+ * should have more error checking */
+ if (!context.resultAvailable[index]) {
+ context.results[index] = context.let->defns[index]->eval(context);
+ context.resultAvailable[index] = true;
+ }
+
+ return context.results[index];
+}
+
+uint64_t TimingExprUn::eval(TimingExprEvalContext &context)
+{
+ uint64_t arg_value = arg->eval(context);
+ uint64_t ret = 0;
+
+ switch (op) {
+ case Enums::timingExprSizeInBits:
+ if (arg_value == 0)
+ ret = 0;
+ else
+ ret = ceilLog2(arg_value);
+ break;
+ case Enums::timingExprNot:
+ ret = arg_value != 0;
+ break;
+ case Enums::timingExprInvert:
+ ret = ~arg_value;
+ break;
+ case Enums::timingExprSignExtend32To64:
+ ret = static_cast<int64_t>(
+ static_cast<int32_t>(arg_value));
+ break;
+ case Enums::timingExprAbs:
+ if (static_cast<int64_t>(arg_value) < 0)
+ ret = -arg_value;
+ else
+ ret = arg_value;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+uint64_t TimingExprBin::eval(TimingExprEvalContext &context)
+{
+ uint64_t left_value = left->eval(context);
+ uint64_t right_value = right->eval(context);
+ uint64_t ret = 0;
+
+ switch (op) {
+ case Enums::timingExprAdd:
+ ret = left_value + right_value;
+ break;
+ case Enums::timingExprSub:
+ ret = left_value - right_value;
+ break;
+ case Enums::timingExprUMul:
+ ret = left_value * right_value;
+ break;
+ case Enums::timingExprUDiv:
+ if (right_value != 0) {
+ ret = left_value / right_value;
+ }
+ break;
+ case Enums::timingExprUCeilDiv:
+ if (right_value != 0) {
+ ret = (left_value + (right_value - 1)) / right_value;
+ }
+ break;
+ case Enums::timingExprSMul:
+ ret = static_cast<int64_t>(left_value) *
+ static_cast<int64_t>(right_value);
+ break;
+ case Enums::timingExprSDiv:
+ if (right_value != 0) {
+ ret = static_cast<int64_t>(left_value) /
+ static_cast<int64_t>(right_value);
+ }
+ break;
+ case Enums::timingExprEqual:
+ ret = left_value == right_value;
+ break;
+ case Enums::timingExprNotEqual:
+ ret = left_value != right_value;
+ break;
+ case Enums::timingExprULessThan:
+ ret = left_value < right_value;
+ break;
+ case Enums::timingExprUGreaterThan:
+ ret = left_value > right_value;
+ break;
+ case Enums::timingExprSLessThan:
+ ret = static_cast<int64_t>(left_value) <
+ static_cast<int64_t>(right_value);
+ break;
+ case Enums::timingExprSGreaterThan:
+ ret = static_cast<int64_t>(left_value) >
+ static_cast<int64_t>(right_value);
+ break;
+ case Enums::timingExprAnd:
+ ret = (left_value != 0) && (right_value != 0);
+ break;
+ case Enums::timingExprOr:
+ ret = (left_value != 0) || (right_value != 0);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+uint64_t TimingExprIf::eval(TimingExprEvalContext &context)
+{
+ uint64_t cond_value = cond->eval(context);
+
+ if (cond_value != 0)
+ return trueExpr->eval(context);
+ else
+ return falseExpr->eval(context);
+}
+
+TimingExprLiteral *
+TimingExprLiteralParams::create()
+{
+ return new TimingExprLiteral(this);
+}
+
+TimingExprSrcReg *
+TimingExprSrcRegParams::create()
+{
+ return new TimingExprSrcReg(this);
+}
+
+TimingExprReadIntReg *
+TimingExprReadIntRegParams::create()
+{
+ return new TimingExprReadIntReg(this);
+}
+
+TimingExprLet *
+TimingExprLetParams::create()
+{
+ return new TimingExprLet(this);
+}
+
+TimingExprRef *
+TimingExprRefParams::create()
+{
+ return new TimingExprRef(this);
+}
+
+TimingExprUn *
+TimingExprUnParams::create()
+{
+ return new TimingExprUn(this);
+}
+
+TimingExprBin *
+TimingExprBinParams::create()
+{
+ return new TimingExprBin(this);
+}
+
+TimingExprIf *
+TimingExprIfParams::create()
+{
+ return new TimingExprIf(this);
+}
diff --git a/src/cpu/timing_expr.hh b/src/cpu/timing_expr.hh
new file mode 100644
index 000000000..d2c38ea90
--- /dev/null
+++ b/src/cpu/timing_expr.hh
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/*
+ * These classes define an expression language over uint64_t with only
+ * a few operators. This can be used to form expressions for the extra
+ * delay required in variable execution time instructions.
+ *
+ * Expressions, in evaluation, will have access to the ThreadContext and
+ * a StaticInst.
+ */
+
+#ifndef __CPU_TIMING_EXPR_HH__
+#define __CPU_TIMING_EXPR_HH__
+
+#include "cpu/static_inst.hh"
+#include "cpu/thread_context.hh"
+#include "enums/TimingExprOp.hh"
+#include "params/TimingExpr.hh"
+#include "params/TimingExprBin.hh"
+#include "params/TimingExprIf.hh"
+#include "params/TimingExprLet.hh"
+#include "params/TimingExprLiteral.hh"
+#include "params/TimingExprReadIntReg.hh"
+#include "params/TimingExprRef.hh"
+#include "params/TimingExprSrcReg.hh"
+#include "params/TimingExprUn.hh"
+#include "sim/sim_object.hh"
+
+/** These classes are just the C++ counterparts for those in Expr.py and
+ * are, therefore, documented there */
+
+class TimingExprLet;
+
+/** Object to gather the visible context for evaluation */
+class TimingExprEvalContext
+{
+ public:
+ /** Special visible context */
+ StaticInstPtr inst;
+ ThreadContext *thread;
+
+ /** Context visible as sub expressions. results will hold the results
+ * of (lazily) evaluating let's expressions. resultAvailable elements
+ * are true when a result has actually been evaluated */
+ TimingExprLet *let;
+ std::vector<uint64_t> results;
+ std::vector<bool > resultAvailable;
+
+ TimingExprEvalContext(StaticInstPtr inst_,
+ ThreadContext *thread_, TimingExprLet *let_);
+};
+
+class TimingExpr : public SimObject
+{
+ public:
+ TimingExpr(const TimingExprParams *params) :
+ SimObject(params)
+ { }
+
+ virtual uint64_t eval(TimingExprEvalContext &context) = 0;
+};
+
+class TimingExprLiteral : public TimingExpr
+{
+ public:
+ uint64_t value;
+
+ TimingExprLiteral(const TimingExprLiteralParams *params) :
+ TimingExpr(params),
+ value(params->value)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context) { return value; }
+};
+
+class TimingExprSrcReg : public TimingExpr
+{
+ public:
+ unsigned int index;
+
+ TimingExprSrcReg(const TimingExprSrcRegParams *params) :
+ TimingExpr(params),
+ index(params->index)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprReadIntReg : public TimingExpr
+{
+ public:
+ TimingExpr *reg;
+
+ TimingExprReadIntReg(const TimingExprReadIntRegParams *params) :
+ TimingExpr(params),
+ reg(params->reg)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprLet : public TimingExpr
+{
+ public:
+ std::vector<TimingExpr *> defns;
+ TimingExpr *expr;
+
+ TimingExprLet(const TimingExprLetParams *params) :
+ TimingExpr(params),
+ defns(params->defns),
+ expr(params->expr)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprRef : public TimingExpr
+{
+ public:
+ unsigned int index;
+
+ TimingExprRef(const TimingExprRefParams *params) :
+ TimingExpr(params),
+ index(params->index)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprUn : public TimingExpr
+{
+ public:
+ Enums::TimingExprOp op;
+ TimingExpr *arg;
+
+ TimingExprUn(const TimingExprUnParams *params) :
+ TimingExpr(params),
+ op(params->op),
+ arg(params->arg)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprBin : public TimingExpr
+{
+ public:
+ Enums::TimingExprOp op;
+ TimingExpr *left;
+ TimingExpr *right;
+
+ TimingExprBin(const TimingExprBinParams *params) :
+ TimingExpr(params),
+ op(params->op),
+ left(params->left),
+ right(params->right)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+class TimingExprIf : public TimingExpr
+{
+ public:
+ TimingExpr *cond;
+ TimingExpr *trueExpr;
+ TimingExpr *falseExpr;
+
+ TimingExprIf(const TimingExprIfParams *params) :
+ TimingExpr(params),
+ cond(params->cond),
+ trueExpr(params->trueExpr),
+ falseExpr(params->falseExpr)
+ { }
+
+ uint64_t eval(TimingExprEvalContext &context);
+};
+
+#endif
diff --git a/src/doc/inside-minor.doxygen b/src/doc/inside-minor.doxygen
new file mode 100644
index 000000000..e55f61c01
--- /dev/null
+++ b/src/doc/inside-minor.doxygen
@@ -0,0 +1,1091 @@
+# Copyright (c) 2014 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+namespace Minor
+{
+
+/*!
+
+\page minor Inside the Minor CPU model
+
+\tableofcontents
+
+This document contains a description of the structure and function of the
+Minor gem5 in-order processor model. It is recommended reading for anyone who
+wants to understand Minor's internal organisation, design decisions, C++
+implementation and Python configuration. A familiarity with gem5 and some of
+its internal structures is assumed. This document is meant to be read
+alongside the Minor source code and to explain its general structure without
+being too slavish about naming every function and data type.
+
+\section whatis What is Minor?
+
+Minor is an in-order processor model with a fixed pipeline but configurable
+data structures and execute behaviour. It is intended to be used to model
+processors with strict in-order execution behaviour and allows visualisation
+of an instruction's position in the pipeline through the
+MinorTrace/minorview.py format/tool. The intention is to provide a framework
+for micro-architecturally correlating the model with a particular, chosen
+processor with similar capabilities.
+
+\section philo Design philosophy
+
+\subsection mt Multithreading
+
+The model isn't currently capable of multithreading but there are THREAD
+comments in key places where stage data needs to be arrayed to support
+multithreading.
+
+\subsection structs Data structures
+
+Decorating data structures with large amounts of life-cycle information is
+avoided. Only instructions (MinorDynInst) contain a significant proportion of
+their data content whose values are not set at construction.
+
+All internal structures have fixed sizes on construction. Data held in queues
+and FIFOs (MinorBuffer, FUPipeline) should have a BubbleIF interface to
+allow a distinct 'bubble'/no data value option for each type.
+
+Inter-stage 'struct' data is packaged in structures which are passed by value.
+Only MinorDynInst, the line data in ForwardLineData and the memory-interfacing
+objects Fetch1::FetchRequest and LSQ::LSQRequest are '::new' allocated while
+running the model.
+
+\section model Model structure
+
+Objects of class MinorCPU are provided by the model to gem5. MinorCPU
+implements the interfaces of (cpu.hh) and can provide data and
+instruction interfaces for connection to a cache system. The model is
+configured in a similar way to other gem5 models through Python. That
+configuration is passed on to MinorCPU::pipeline (of class Pipeline) which
+actually implements the processor pipeline.
+
+The hierarchy of major unit ownership from MinorCPU down looks like this:
+
+<ul>
+<li>MinorCPU</li>
+<ul>
+ <li>Pipeline - container for the pipeline, owns the cyclic 'tick'
+ event mechanism and the idling (cycle skipping) mechanism.</li>
+ <ul>
+ <li>Fetch1 - instruction fetch unit responsible for fetching cache
+ lines (or parts of lines from the I-cache interface)</li>
+ <ul>
+ <li>Fetch1::IcachePort - interface to the I-cache from
+ Fetch1</li>
+ </ul>
+ <li>Fetch2 - line to instruction decomposition</li>
+ <li>Decode - instruction to micro-op decomposition</li>
+ <li>Execute - instruction execution and data memory
+ interface</li>
+ <ul>
+ <li>LSQ - load store queue for memory ref. instructions</li>
+ <li>LSQ::DcachePort - interface to the D-cache from
+ Execute</li>
+ </ul>
+ </ul>
+ </ul>
+</ul>
+
+\section keystruct Key data structures
+
+\subsection ids Instruction and line identity: InstId (dyn_inst.hh)
+
+An InstId contains the sequence numbers and thread numbers that describe the
+life cycle and instruction stream affiliations of individual fetched cache
+lines and instructions.
+
+An InstId is printed in one of the following forms:
+
+ - T/S.P/L - for fetched cache lines
+ - T/S.P/L/F - for instructions before Decode
+ - T/S.P/L/F.E - for instructions from Decode onwards
+
+for example:
+
+ - 0/10.12/5/6.7
+
+InstId's fields are:
+
+<table>
+<tr>
+ <td><b>Field</b></td>
+ <td><b>Symbol</b></td>
+ <td><b>Generated by</b></td>
+ <td><b>Checked by</b></td>
+ <td><b>Function</b></td>
+</tr>
+
+<tr>
+ <td>InstId::threadId</td>
+ <td>T</td>
+ <td>Fetch1</td>
+ <td>Everywhere the thread number is needed</td>
+ <td>Thread number (currently always 0).</td>
+</tr>
+
+<tr>
+ <td>InstId::streamSeqNum</td>
+ <td>S</td>
+ <td>Execute</td>
+ <td>Fetch1, Fetch2, Execute (to discard lines/insts)</td>
+ <td>Stream sequence number as chosen by Execute. Stream
+ sequence numbers change after changes of PC (branches, exceptions) in
+ Execute and are used to separate pre and post branch instruction
+ streams.</td>
+</tr>
+
+<tr>
+ <td>InstId::predictionSeqNum</td>
+ <td>P</td>
+ <td>Fetch2</td>
+ <td>Fetch2 (while discarding lines after prediction)</td>
+ <td>Prediction sequence numbers represent branch prediction decisions.
+ This is used by Fetch2 to mark lines/instructions according to the last
+ followed branch prediction made by Fetch2. Fetch2 can signal to Fetch1
+ that it should change its fetch address and mark lines with a new
+ prediction sequence number (which it will only do if the stream sequence
+ number Fetch1 expects matches that of the request). </td> </tr>
+
+<tr>
+<td>InstId::lineSeqNum</td>
+<td>L</td>
+<td>Fetch1</td>
+<td>(Just for debugging)</td>
+<td>Line fetch sequence number of this cache line or the line
+ this instruction was extracted from.
+ </td>
+</tr>
+
+<tr>
+<td>InstId::fetchSeqNum</td>
+<td>F</td>
+<td>Fetch2</td>
+<td>Fetch2 (as the inst. sequence number for branches)</td>
+<td>Instruction fetch order assigned by Fetch2 when lines
+ are decomposed into instructions.
+ </td>
+</tr>
+
+<tr>
+<td>InstId::execSeqNum</td>
+<td>E</td>
+<td>Decode</td>
+<td>Execute (to check instruction identity in queues/FUs/LSQ)</td>
+<td>Instruction order after micro-op decomposition.</td>
+</tr>
+
+</table>
+
+The sequence number fields are all independent of each other and although, for
+instance, InstId::execSeqNum for an instruction will always be >=
+InstId::fetchSeqNum, the comparison is not useful.
+
+The originating stage of each sequence number field keeps a counter for that
+field which can be incremented in order to generate new, unique numbers.
+
+\subsection insts Instructions: MinorDynInst (dyn_inst.hh)
+
+MinorDynInst represents an instruction's progression through the pipeline. An
+instruction can be three things:
+
+<table>
+<tr>
+ <td><b>Thing</b></td>
+ <td><b>Predicate</b></td>
+ <td><b>Explanation</b></td>
+</tr>
+<tr>
+ <td>A bubble</td>
+ <td>MinorDynInst::isBubble()</td>
+ <td>no instruction at all, just a space-filler</td>
+</tr>
+<tr>
+ <td>A fault</td>
+ <td>MinorDynInst::isFault()</td>
+ <td>a fault to pass down the pipeline in an instruction's clothing</td>
+</tr>
+<tr>
+ <td>A decoded instruction</td>
+ <td>MinorDynInst::isInst()</td>
+ <td>instructions are actually passed to the gem5 decoder in Fetch2 and so
+ are created fully decoded. MinorDynInst::staticInst is the decoded
+ instruction form.</td>
+</tr>
+</table>
+
+Instructions are reference counted using the gem5 RefCountingPtr
+(base/refcnt.hh) wrapper. They therefore usually appear as MinorDynInstPtr in
+code. Note that as RefCountingPtr initialises as nullptr rather than an
+object that supports BubbleIF::isBubble, passing raw MinorDynInstPtrs to
+Queue%s and other similar structures from stage.hh without boxing is
+dangerous.
+
+\subsection fld ForwardLineData (pipe_data.hh)
+
+ForwardLineData is used to pass cache lines from Fetch1 to Fetch2. Like
+MinorDynInst%s, they can be bubbles (ForwardLineData::isBubble()),
+fault-carrying or can contain a line (partial line) fetched by Fetch1. The
+data carried by ForwardLineData is owned by a Packet object returned from
+memory and is explicitly memory managed and do must be deleted once processed
+(by Fetch2 deleting the Packet).
+
+\subsection fid ForwardInstData (pipe_data.hh)
+
+ForwardInstData can contain up to ForwardInstData::width() instructions in its
+ForwardInstData::insts vector. This structure is used to carry instructions
+between Fetch2, Decode and Execute and to store input buffer vectors in Decode
+and Execute.
+
+\subsection fr Fetch1::FetchRequest (fetch1.hh)
+
+FetchRequests represent I-cache line fetch requests. The are used in the
+memory queues of Fetch1 and are pushed into/popped from Packet::senderState
+while traversing the memory system.
+
+FetchRequests contain a memory system Request (mem/request.hh) for that fetch
+access, a packet (Packet, mem/packet.hh), if the request gets to memory, and a
+fault field that can be populated with a TLB-sourced prefetch fault (if any).
+
+\subsection lsqr LSQ::LSQRequest (execute.hh)
+
+LSQRequests are similar to FetchRequests but for D-cache accesses. They carry
+the instruction associated with a memory access.
+
+\section pipeline The pipeline
+
+\verbatim
+------------------------------------------------------------------------------
+ Key:
+
+ [] : inter-stage BufferBuffer
+ ,--.
+ | | : pipeline stage
+ `--'
+ ---> : forward communication
+ <--- : backward communication
+
+ rv : reservation information for input buffers
+
+ ,------. ,------. ,------. ,-------.
+ (from --[]-v->|Fetch1|-[]->|Fetch2|-[]->|Decode|-[]->|Execute|--> (to Fetch1
+ Execute) | | |<-[]-| |<-rv-| |<-rv-| | & Fetch2)
+ | `------'<-rv-| | | | | |
+ `-------------->| | | | | |
+ `------' `------' `-------'
+------------------------------------------------------------------------------
+\endverbatim
+
+The four pipeline stages are connected together by MinorBuffer FIFO
+(stage.hh, derived ultimately from TimeBuffer) structures which allow
+inter-stage delays to be modelled. There is a MinorBuffer%s between adjacent
+stages in the forward direction (for example: passing lines from Fetch1 to
+Fetch2) and, between Fetch2 and Fetch1, a buffer in the backwards direction
+carrying branch predictions.
+
+Stages Fetch2, Decode and Execute have input buffers which, each cycle, can
+accept input data from the previous stage and can hold that data if the stage
+is not ready to process it. Input buffers store data in the same form as it
+is received and so Decode and Execute's input buffers contain the output
+instruction vector (ForwardInstData (pipe_data.hh)) from their previous stages
+with the instructions and bubbles in the same positions as a single buffer
+entry.
+
+Stage input buffers provide a Reservable (stage.hh) interface to their
+previous stages, to allow slots to be reserved in their input buffers, and
+communicate their input buffer occupancy backwards to allow the previous stage
+to plan whether it should make an output in a given cycle.
+
+\subsection events Event handling: MinorActivityRecorder (activity.hh,
+pipeline.hh)
+
+Minor is essentially a cycle-callable model with some ability to skip cycles
+based on pipeline activity. External events are mostly received by callbacks
+(e.g. Fetch1::IcachePort::recvTimingResp) and cause the pipeline to be woken
+up to service advancing request queues.
+
+Ticked (sim/ticked.hh) is a base class bringing together an evaluate
+member function and a provided SimObject. It provides a Ticked::start/stop
+interface to start and pause clock events from being periodically issued.
+Pipeline is a derived class of Ticked.
+
+During evaluate calls, stages can signal that they still have work to do in
+the next cycle by calling either MinorCPU::activityRecorder->activity() (for
+non-callable related activity) or MinorCPU::wakeupOnEvent(<stageId>) (for
+stage callback-related 'wakeup' activity).
+
+Pipeline::evaluate contains calls to evaluate for each unit and a test for
+pipeline idling which can turns off the clock tick if no unit has signalled
+that it may become active next cycle.
+
+Within Pipeline (pipeline.hh), the stages are evaluated in reverse order (and
+so will ::evaluate in reverse order) and their backwards data can be
+read immediately after being written in each cycle allowing output decisions
+to be 'perfect' (allowing synchronous stalling of the whole pipeline). Branch
+predictions from Fetch2 to Fetch1 can also be transported in 0 cycles making
+fetch1ToFetch2BackwardDelay the only configurable delay which can be set as
+low as 0 cycles.
+
+The MinorCPU::activateContext and MinorCPU::suspendContext interface can be
+called to start and pause threads (threads in the MT sense) and to start and
+pause the pipeline. Executing instructions can call this interface
+(indirectly through the ThreadContext) to idle the CPU/their threads.
+
+\subsection stages Each pipeline stage
+
+In general, the behaviour of a stage (each cycle) is:
+
+\verbatim
+ evaluate:
+ push input to inputBuffer
+ setup references to input/output data slots
+
+ do 'every cycle' 'step' tasks
+
+ if there is input and there is space in the next stage:
+ process and generate a new output
+ maybe re-activate the stage
+
+ send backwards data
+
+ if the stage generated output to the following FIFO:
+ signal pipe activity
+
+ if the stage has more processable input and space in the next stage:
+ re-activate the stage for the next cycle
+
+ commit the push to the inputBuffer if that data hasn't all been used
+\endverbatim
+
+The Execute stage differs from this model as its forward output (branch) data
+is unconditionally sent to Fetch1 and Fetch2. To allow this behaviour, Fetch1
+and Fetch2 must be unconditionally receptive to that data.
+
+\subsection fetch1 Fetch1 stage
+
+Fetch1 is responsible for fetching cache lines or partial cache lines from the
+I-cache and passing them on to Fetch2 to be decomposed into instructions. It
+can receive 'change of stream' indications from both Execute and Fetch2 to
+signal that it should change its internal fetch address and tag newly fetched
+lines with new stream or prediction sequence numbers. When both Execute and
+Fetch2 signal changes of stream at the same time, Fetch1 takes Execute's
+change.
+
+Every line issued by Fetch1 will bear a unique line sequence number which can
+be used for debugging stream changes.
+
+When fetching from the I-cache, Fetch1 will ask for data from the current
+fetch address (Fetch1::pc) up to the end of the 'data snap' size set in the
+parameter fetch1LineSnapWidth. Subsequent autonomous line fetches will fetch
+whole lines at a snap boundary and of size fetch1LineWidth.
+
+Fetch1 will only initiate a memory fetch if it can reserve space in Fetch2
+input buffer. That input buffer serves an the fetch queue/LFL for the system.
+
+Fetch1 contains two queues: requests and transfers to handle the stages of
+translating the address of a line fetch (via the TLB) and accommodating the
+request/response of fetches to/from memory.
+
+Fetch requests from Fetch1 are pushed into the requests queue as newly
+allocated FetchRequest objects once they have been sent to the ITLB with a
+call to itb->translateTiming.
+
+A response from the TLB moves the request from the requests queue to the
+transfers queue. If there is more than one entry in each queue, it is
+possible to get a TLB response for request which is not at the head of the
+requests queue. In that case, the TLB response is marked up as a state change
+to Translated in the request object, and advancing the request to transfers
+(and the memory system) is left to calls to Fetch1::stepQueues which is called
+in the cycle following any event is received.
+
+Fetch1::tryToSendToTransfers is responsible for moving requests between the
+two queues and issuing requests to memory. Failed TLB lookups (prefetch
+aborts) continue to occupy space in the queues until they are recovered at the
+head of transfers.
+
+Responses from memory change the request object state to Complete and
+Fetch1::evaluate can pick up response data, package it in the ForwardLineData
+object, and forward it to Fetch2%'s input buffer.
+
+As space is always reserved in Fetch2::inputBuffer, setting the input buffer's
+size to 1 results in non-prefetching behaviour.
+
+When a change of stream occurs, translated requests queue members and
+completed transfers queue members can be unconditionally discarded to make way
+for new transfers.
+
+\subsection fetch2 Fetch2 stage
+
+Fetch2 receives a line from Fetch1 into its input buffer. The data in the
+head line in that buffer is iterated over and separated into individual
+instructions which are packed into a vector of instructions which can be
+passed to Decode. Packing instructions can be aborted early if a fault is
+found in either the input line as a whole or a decomposed instruction.
+
+\subsubsection bp Branch prediction
+
+Fetch2 contains the branch prediction mechanism. This is a wrapper around the
+branch predictor interface provided by gem5 (cpu/pred/...).
+
+Branches are predicted for any control instructions found. If prediction is
+attempted for an instruction, the MinorDynInst::triedToPredict flag is set on
+that instruction.
+
+When a branch is predicted to take, the MinorDynInst::predictedTaken flag is
+set and MinorDynInst::predictedTarget is set to the predicted target PC value.
+The predicted branch instruction is then packed into Fetch2%'s output vector,
+the prediction sequence number is incremented, and the branch is communicated
+to Fetch1.
+
+After signalling a prediction, Fetch2 will discard its input buffer contents
+and will reject any new lines which have the same stream sequence number as
+that branch but have a different prediction sequence number. This allows
+following sequentially fetched lines to be rejected without ignoring new lines
+generated by a change of stream indicated from a 'real' branch from Execute
+(which will have a new stream sequence number).
+
+The program counter value provided to Fetch2 by Fetch1 packets is only updated
+when there is a change of stream. Fetch2::havePC indicates whether the PC
+will be picked up from the next processed input line. Fetch2::havePC is
+necessary to allow line-wrapping instructions to be tracked through decode.
+
+Branches (and instructions predicted to branch) which are processed by Execute
+will generate BranchData (pipe_data.hh) data explaining the outcome of the
+branch which is sent forwards to Fetch1 and Fetch2. Fetch1 uses this data to
+change stream (and update its stream sequence number and address for new
+lines). Fetch2 uses it to update the branch predictor. Minor does not
+communicate branch data to the branch predictor for instructions which are
+discarded on the way to commit.
+
+BranchData::BranchReason (pipe_data.hh) encodes the possible branch scenarios:
+
+<table>
+<tr>
+ <td>Branch enum val.</td>
+ <td>In Execute</td>
+ <td>Fetch1 reaction</td>
+ <td>Fetch2 reaction</td>
+</tr>
+<tr>
+ <td>NoBranch</td>
+ <td>(output bubble data)</td>
+ <td>-</td>
+ <td>-</td>
+</tr>
+<tr>
+ <td>CorrectlyPredictedBranch</td>
+ <td>Predicted, taken</td>
+ <td>-</td>
+ <td>Update BP as taken branch</td>
+</tr>
+<tr>
+ <td>UnpredictedBranch</td>
+ <td>Not predicted, taken and was taken</td>
+ <td>New stream</td>
+ <td>Update BP as taken branch</td>
+</tr>
+<tr>
+ <td>BadlyPredictedBranch</td>
+ <td>Predicted, not taken</td>
+ <td>New stream to restore to old inst. source</td>
+ <td>Update BP as not taken branch</td>
+</tr>
+<tr>
+ <td>BadlyPredictedBranchTarget</td>
+ <td>Predicted, taken, but to a different target than predicted one</td>
+ <td>New stream</td>
+ <td>Update BTB to new target</td>
+</tr>
+<tr>
+ <td>SuspendThread</td>
+ <td>Hint to suspend fetching</td>
+ <td>Suspend fetch for this thread (branch to next inst. as wakeup
+ fetch addr)</td>
+ <td>-</td>
+</tr>
+<tr>
+ <td>Interrupt</td>
+ <td>Interrupt detected</td>
+ <td>New stream</td>
+ <td>-</td>
+</tr>
+</table>
+
+The parameter decodeInputWidth sets the number of instructions which can be
+packed into the output per cycle. If the parameter fetch2CycleInput is true,
+Decode can try to take instructions from more than one entry in its input
+buffer per cycle.
+
+\subsection decode Decode stage
+
+Decode takes a vector of instructions from Fetch2 (via its input buffer) and
+decomposes those instructions into micro-ops (if necessary) and packs them
+into its output instruction vector.
+
+The parameter executeInputWidth sets the number of instructions which can be
+packed into the output per cycle. If the parameter decodeCycleInput is true,
+Decode can try to take instructions from more than one entry in its input
+buffer per cycle.
+
+\subsection execute Execute stage
+
+Execute provides all the instruction execution and memory access mechanisms.
+An instructions passage through Execute can take multiple cycles with its
+precise timing modelled by a functional unit pipeline FIFO.
+
+A vector of instructions (possibly including fault 'instructions') is provided
+to Execute by Decode and can be queued in the Execute input buffer before
+being issued. Setting the parameter executeCycleInput allows execute to
+examine more than one input buffer entry (more than one instruction vector).
+The number of instructions in the input vector can be set with
+executeInputWidth and the depth of the input buffer can be set with parameter
+executeInputBufferSize.
+
+\subsubsection fus Functional units
+
+The Execute stage contains pipelines for each functional unit comprising the
+computational core of the CPU. Functional units are configured via the
+executeFuncUnits parameter. Each functional unit has a number of instruction
+classes it supports, a stated delay between instruction issues, and a delay
+from instruction issue to (possible) commit and an optional timing annotation
+capable of more complicated timing.
+
+Each active cycle, Execute::evaluate performs this action:
+
+\verbatim
+ Execute::evaluate:
+ push input to inputBuffer
+ setup references to input/output data slots and branch output slot
+
+ step D-cache interface queues (similar to Fetch1)
+
+ if interrupt posted:
+ take interrupt (signalling branch to Fetch1/Fetch2)
+ else
+ commit instructions
+ issue new instructions
+
+ advance functional unit pipelines
+
+ reactivate Execute if the unit is still active
+
+ commit the push to the inputBuffer if that data hasn't all been used
+\endverbatim
+
+\subsubsection fifos Functional unit FIFOs
+
+Functional units are implemented as SelfStallingPipelines (stage.hh). These
+are TimeBuffer FIFOs with two distinct 'push' and 'pop' wires. They respond
+to SelfStallingPipeline::advance in the same way as TimeBuffers <b>unless</b>
+there is data at the far, 'pop', end of the FIFO. A 'stalled' flag is
+provided for signalling stalling and to allow a stall to be cleared. The
+intention is to provide a pipeline for each functional unit which will never
+advance an instruction out of that pipeline until it has been processed and
+the pipeline is explicitly unstalled.
+
+The actions 'issue', 'commit', and 'advance' act on the functional units.
+
+\subsubsection issue Issue
+
+Issuing instructions involves iterating over both the input buffer
+instructions and the heads of the functional units to try and issue
+instructions in order. The number of instructions which can be issued each
+cycle is limited by the parameter executeIssueLimit, how executeCycleInput is
+set, the availability of pipeline space and the policy used to choose a
+pipeline in which the instruction can be issued.
+
+At present, the only issue policy is strict round-robin visiting of each
+pipeline with the given instructions in sequence. For greater flexibility,
+better (and more specific policies) will need to be possible.
+
+Memory operation instructions traverse their functional units to perform their
+EA calculations. On 'commit', the ExecContext::initiateAcc execution phase is
+performed and any memory access is issued (via. ExecContext::{read,write}Mem
+calling LSQ::pushRequest) to the LSQ.
+
+Note that faults are issued as if they are instructions and can (currently) be
+issued to *any* functional unit.
+
+Every issued instruction is also pushed into the Execute::inFlightInsts queue.
+Memory ref. instructions are pushing into Execute::inFUMemInsts queue.
+
+\subsubsection commit Commit
+
+Instructions are committed by examining the head of the Execute::inFlightInsts
+queue (which is decorated with the functional unit number to which the
+instruction was issued). Instructions which can then be found in their
+functional units are executed and popped from Execute::inFlightInsts.
+
+Memory operation instructions are committed into the memory queues (as
+described above) and exit their functional unit pipeline but are not popped
+from the Execute::inFlightInsts queue. The Execute::inFUMemInsts queue
+provides ordering to memory operations as they pass through the functional
+units (maintaining issue order). On entering the LSQ, instructions are popped
+from Execute::inFUMemInsts.
+
+If the parameter executeAllowEarlyMemoryIssue is set, memory operations can be
+sent from their FU to the LSQ before reaching the head of
+Execute::inFlightInsts but after their dependencies are met.
+MinorDynInst::instToWaitFor is marked up with the latest dependent instruction
+execSeqNum required to be committed for a memory operation to progress to the
+LSQ.
+
+Once a memory response is available (by testing the head of
+Execute::inFlightInsts against LSQ::findResponse), commit will process that
+response (ExecContext::completeAcc) and pop the instruction from
+Execute::inFlightInsts.
+
+Any branch, fault or interrupt will cause a stream sequence number change and
+signal a branch to Fetch1/Fetch2. Only instructions with the current stream
+sequence number will be issued and/or committed.
+
+\subsubsection advance Advance
+
+All non-stalled pipeline are advanced and may, thereafter, become stalled.
+Potential activity in the next cycle is signalled if there are any
+instructions remaining in any pipeline.
+
+\subsubsection sb Scoreboard
+
+The scoreboard (Scoreboard) is used to control instruction issue. It contains
+a count of the number of in flight instructions which will write each general
+purpose CPU integer or float register. Instructions will only be issued where
+the scoreboard contains a count of 0 instructions which will write to one of
+the instructions source registers.
+
+Once an instruction is issued, the scoreboard counts for each destination
+register for an instruction will be incremented.
+
+The estimated delivery time of the instruction's result is marked up in the
+scoreboard by adding the length of the issued-to FU to the current time. The
+timings parameter on each FU provides a list of additional rules for
+calculating the delivery time. These are documented in the parameter comments
+in MinorCPU.py.
+
+On commit, (for memory operations, memory response commit) the scoreboard
+counters for an instruction's source registers are decremented. will be
+decremented.
+
+\subsubsection ifi Execute::inFlightInsts
+
+The Execute::inFlightInsts queue will always contain all instructions in
+flight in Execute in the correct issue order. Execute::issue is the only
+process which will push an instruction into the queue. Execute::commit is the
+only process that can pop an instruction.
+
+\subsubsection lsq LSQ
+
+The LSQ can support multiple outstanding transactions to memory in a number of
+conservative cases.
+
+There are three queues to contain requests: requests, transfers and the store
+buffer. The requests and transfers queue operate in a similar manner to the
+queues in Fetch1. The store buffer is used to decouple the delay of
+completing store operations from following loads.
+
+Requests are issued to the DTLB as their instructions leave their functional
+unit. At the head of requests, cacheable load requests can be sent to memory
+and on to the transfers queue. Cacheable stores will be passed to transfers
+unprocessed and progress that queue maintaining order with other transactions.
+
+The conditions in LSQ::tryToSendToTransfers dictate when requests can
+be sent to memory.
+
+All uncacheable transactions, split transactions and locked transactions are
+processed in order at the head of requests. Additionally, store results
+residing in the store buffer can have their data forwarded to cacheable loads
+(removing the need to perform a read from memory) but no cacheable load can be
+issue to the transfers queue until that queue's stores have drained into the
+store buffer.
+
+At the end of transfers, requests which are LSQ::LSQRequest::Complete (are
+faulting, are cacheable stores, or have been sent to memory and received a
+response) can be picked off by Execute and either committed
+(ExecContext::completeAcc) and, for stores, be sent to the store buffer.
+
+Barrier instructions do not prevent cacheable loads from progressing to memory
+but do cause a stream change which will discard that load. Stores will not be
+committed to the store buffer if they are in the shadow of the barrier but
+before the new instruction stream has arrived at Execute. As all other memory
+transactions are delayed at the end of the requests queue until they are at
+the head of Execute::inFlightInsts, they will be discarded by any barrier
+stream change.
+
+After commit, LSQ::BarrierDataRequest requests are inserted into the
+store buffer to track each barrier until all preceding memory transactions
+have drained from the store buffer. No further memory transactions will be
+issued from the ends of FUs until after the barrier has drained.
+
+\subsubsection drain Draining
+
+Draining is mostly handled by the Execute stage. When initiated by calling
+MinorCPU::drain, Pipeline::evaluate checks the draining status of each unit
+each cycle and keeps the pipeline active until draining is complete. It is
+Pipeline that signals the completion of draining. Execute is triggered by
+MinorCPU::drain and starts stepping through its Execute::DrainState state
+machine, starting from state Execute::NotDraining, in this order:
+
+<table>
+<tr>
+ <td><b>State</b></td>
+ <td><b>Meaning</b></td>
+</tr>
+<tr>
+ <td>Execute::NotDraining</td>
+ <td>Not trying to drain, normal execution</td>
+</tr>
+<tr>
+ <td>Execute::DrainCurrentInst</td>
+ <td>Draining micro-ops to complete inst.</td>
+</tr>
+<tr>
+ <td>Execute::DrainHaltFetch</td>
+ <td>Halt fetching instructions</td>
+</tr>
+<tr>
+ <td>Execute::DrainAllInsts</td>
+ <td>Discarding all instructions presented</td>
+</tr>
+</table>
+
+When complete, a drained Execute unit will be in the Execute::DrainAllInsts
+state where it will continue to discard instructions but has no knowledge of
+the drained state of the rest of the model.
+
+\section debug Debug options
+
+The model provides a number of debug flags which can be passed to gem5 with
+the --debug-flags option.
+
+The available flags are:
+
+<table>
+<tr>
+ <td><b>Debug flag</b></td>
+ <td><b>Unit which will generate debugging output</b></td>
+</tr>
+<tr>
+ <td>Activity</td>
+ <td>Debug ActivityMonitor actions</td>
+</tr>
+<tr>
+ <td>Branch</td>
+ <td>Fetch2 and Execute branch prediction decisions</td>
+</tr>
+<tr>
+ <td>MinorCPU</td>
+ <td>CPU global actions such as wakeup/thread suspension</td>
+</tr>
+<tr>
+ <td>Decode</td>
+ <td>Decode</td>
+</tr>
+<tr>
+ <td>MinorExec</td>
+ <td>Execute behaviour</td>
+</tr>
+<tr>
+ <td>Fetch</td>
+ <td>Fetch1 and Fetch2</td>
+</tr>
+<tr>
+ <td>MinorInterrupt</td>
+ <td>Execute interrupt handling</td>
+</tr>
+<tr>
+ <td>MinorMem</td>
+ <td>Execute memory interactions</td>
+</tr>
+<tr>
+ <td>MinorScoreboard</td>
+ <td>Execute scoreboard activity</td>
+</tr>
+<tr>
+ <td>MinorTrace</td>
+ <td>Generate MinorTrace cyclic state trace output (see below)</td>
+</tr>
+<tr>
+ <td>MinorTiming</td>
+ <td>MinorTiming instruction timing modification operations</td>
+</tr>
+</table>
+
+The group flag Minor enables all of the flags beginning with Minor.
+
+\section trace MinorTrace and minorview.py
+
+The debug flag MinorTrace causes cycle-by-cycle state data to be printed which
+can then be processed and viewed by the minorview.py tool. This output is
+very verbose and so it is recommended it only be used for small examples.
+
+\subsection traceformat MinorTrace format
+
+There are three types of line outputted by MinorTrace:
+
+\subsubsection state MinorTrace - Ticked unit cycle state
+
+For example:
+
+\verbatim
+ 110000: system.cpu.dcachePort: MinorTrace: state=MemoryRunning in_tlb_mem=0/0
+\endverbatim
+
+For each time step, the MinorTrace flag will cause one MinorTrace line to be
+printed for every named element in the model.
+
+\subsubsection traceunit MinorInst - summaries of instructions issued by \
+ Decode
+
+For example:
+
+\verbatim
+ 140000: system.cpu.execute: MinorInst: id=0/1.1/1/1.1 addr=0x5c \
+ inst=" mov r0, #0" class=IntAlu
+\endverbatim
+
+MinorInst lines are currently only generated for instructions which are
+committed.
+
+\subsubsection tracefetch1 MinorLine - summaries of line fetches issued by \
+ Fetch1
+
+For example:
+
+\verbatim
+ 92000: system.cpu.icachePort: MinorLine: id=0/1.1/1 size=36 \
+ vaddr=0x5c paddr=0x5c
+\endverbatim
+
+\subsection minorview minorview.py
+
+Minorview (util/minorview.py) can be used to visualise the data created by
+MinorTrace.
+
+\verbatim
+usage: minorview.py [-h] [--picture picture-file] [--prefix name]
+ [--start-time time] [--end-time time] [--mini-views]
+ event-file
+
+Minor visualiser
+
+positional arguments:
+ event-file
+
+optional arguments:
+ -h, --help show this help message and exit
+ --picture picture-file
+ markup file containing blob information (default:
+ <minorview-path>/minor.pic)
+ --prefix name name prefix in trace for CPU to be visualised
+ (default: system.cpu)
+ --start-time time time of first event to load from file
+ --end-time time time of last event to load from file
+ --mini-views show tiny views of the next 10 time steps
+\endverbatim
+
+Raw debugging output can be passed to minorview.py as the event-file. It will
+pick out the MinorTrace lines and use other lines where units in the
+simulation are named (such as system.cpu.dcachePort in the above example) will
+appear as 'comments' when units are clicked on the visualiser.
+
+Clicking on a unit which contains instructions or lines will bring up a speech
+bubble giving extra information derived from the MinorInst/MinorLine lines.
+
+--start-time and --end-time allow only sections of debug files to be loaded.
+
+--prefix allows the name prefix of the CPU to be inspected to be supplied.
+This defaults to 'system.cpu'.
+
+In the visualiser, The buttons Start, End, Back, Forward, Play and Stop can be
+used to control the displayed simulation time.
+
+The diagonally striped coloured blocks are showing the InstId of the
+instruction or line they represent. Note that lines in Fetch1 and f1ToF2.F
+only show the id fields of a line and that instructions in Fetch2, f2ToD, and
+decode.inputBuffer do not yet have execute sequence numbers. The T/S.P/L/F.E
+buttons can be used to toggle parts of InstId on and off to make it easier to
+understand the display. Useful combinations are:
+
+<table>
+<tr>
+ <td><b>Combination</b></td>
+ <td><b>Reason</b></td>
+</tr>
+<tr>
+ <td>E</td>
+ <td>just show the final execute sequence number</td>
+</tr>
+<tr>
+ <td>F/E</td>
+ <td>show the instruction-related numbers</td>
+</tr>
+<tr>
+ <td>S/P</td>
+ <td>show just the stream-related numbers (watch the stream sequence
+ change with branches and not change with predicted branches)</td>
+</tr>
+<tr>
+ <td>S/E</td>
+ <td>show instructions and their stream</td>
+</tr>
+</table>
+
+The key to the right shows all the displayable colours (some of the colour
+choices are quite bad!):
+
+<table>
+<tr>
+ <td><b>Symbol</b></td>
+ <td><b>Meaning</b></td>
+</tr>
+<tr>
+ <td>U</td>
+ <td>Unknown data</td>
+</tr>
+<tr>
+ <td>B</td>
+ <td>Blocked stage</td>
+</tr>
+<tr>
+ <td>-</td>
+ <td>Bubble</td>
+</tr>
+<tr>
+ <td>E</td>
+ <td>Empty queue slot</td>
+</tr>
+<tr>
+ <td>R</td>
+ <td>Reserved queue slot</td>
+</tr>
+<tr>
+ <td>F</td>
+ <td>Fault</td>
+</tr>
+<tr>
+ <td>r</td>
+ <td>Read (used as the leftmost stripe on data in the dcachePort)</td>
+</tr>
+<tr>
+ <td>w</td>
+ <td>Write " "</td>
+</tr>
+<tr>
+ <td>0 to 9</td>
+ <td>last decimal digit of the corresponding data</td>
+</tr>
+</table>
+
+\verbatim
+
+ ,---------------. .--------------. *U
+ | |=|->|=|->|=| | ||=|||->||->|| | *- <- Fetch queues/LSQ
+ `---------------' `--------------' *R
+ === ====== *w <- Activity/Stage activity
+ ,--------------. *1
+ ,--. ,. ,. | ============ | *3 <- Scoreboard
+ | |-\[]-\||-\[]-\||-\[]-\| ============ | *5 <- Execute::inFlightInsts
+ | | :[] :||-/[]-/||-/[]-/| -. -------- | *7
+ | |-/[]-/|| ^ || | | --------- | *9
+ | | || | || | | ------ |
+[]->| | ->|| | || | | ---- |
+ | |<-[]<-||<-+-<-||<-[]<-| | ------ |->[] <- Execute to Fetch1,
+ '--` `' ^ `' | -' ------ | Fetch2 branch data
+ ---. | ---. `--------------'
+ ---' | ---' ^ ^
+ | ^ | `------------ Execute
+ MinorBuffer ----' input `-------------------- Execute input buffer
+ buffer
+\endverbatim
+
+Stages show the colours of the instructions currently being
+generated/processed.
+
+Forward FIFOs between stages show the data being pushed into them at the
+current tick (to the left), the data in transit, and the data available at
+their outputs (to the right).
+
+The backwards FIFO between Fetch2 and Fetch1 shows branch prediction data.
+
+In general, all displayed data is correct at the end of a cycle's activity at
+the time indicated but before the inter-stage FIFOs are ticked. Each FIFO
+has, therefore an extra slot to show the asserted new input data, and all the
+data currently within the FIFO.
+
+Input buffers for each stage are shown below the corresponding stage and show
+the contents of those buffers as horizontal strips. Strips marked as reserved
+(cyan by default) are reserved to be filled by the previous stage. An input
+buffer with all reserved or occupied slots will, therefore, block the previous
+stage from generating output.
+
+Fetch queues and LSQ show the lines/instructions in the queues of each
+interface and show the number of lines/instructions in TLB and memory in the
+two striped colours of the top of their frames.
+
+Inside Execute, the horizontal bars represent the individual FU pipelines.
+The vertical bar to the left is the input buffer and the bar to the right, the
+instructions committed this cycle. The background of Execute shows
+instructions which are being committed this cycle in their original FU
+pipeline positions.
+
+The strip at the top of the Execute block shows the current streamSeqNum that
+Execute is committing. A similar stripe at the top of Fetch1 shows that
+stage's expected streamSeqNum and the stripe at the top of Fetch2 shows its
+issuing predictionSeqNum.
+
+The scoreboard shows the number of instructions in flight which will commit a
+result to the register in the position shown. The scoreboard contains slots
+for each integer and floating point register.
+
+The Execute::inFlightInsts queue shows all the instructions in flight in
+Execute with the oldest instruction (the next instruction to be committed) to
+the right.
+
+'Stage activity' shows the signalled activity (as E/1) for each stage (with
+CPU miscellaneous activity to the left)
+
+'Activity' show a count of stage and pipe activity.
+
+\subsection picformat minor.pic format
+
+The minor.pic file (src/minor/minor.pic) describes the layout of the
+models blocks on the visualiser. Its format is described in the supplied
+minor.pic file.
+
+*/
+
+}
diff --git a/src/sim/SConscript b/src/sim/SConscript
index 5a5c1ab8a..9f9022f30 100644
--- a/src/sim/SConscript
+++ b/src/sim/SConscript
@@ -32,6 +32,7 @@ Import('*')
SimObject('BaseTLB.py')
SimObject('ClockedObject.py')
+SimObject('TickedObject.py')
SimObject('Root.py')
SimObject('ClockDomain.py')
SimObject('VoltageDomain.py')
@@ -51,6 +52,7 @@ Source('serialize.cc')
Source('drain.cc')
Source('sim_events.cc')
Source('sim_object.cc')
+Source('ticked_object.cc')
Source('simulate.cc')
Source('stat_control.cc')
Source('clock_domain.cc')
diff --git a/src/sim/TickedObject.py b/src/sim/TickedObject.py
new file mode 100644
index 000000000..a566aac92
--- /dev/null
+++ b/src/sim/TickedObject.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2014 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+from ClockedObject import ClockedObject
+
+class TickedObject(ClockedObject):
+ type = 'TickedObject'
+ abstract = True
+ cxx_header = "sim/ticked_object.hh"
diff --git a/src/sim/ticked_object.cc b/src/sim/ticked_object.cc
new file mode 100644
index 000000000..22a149388
--- /dev/null
+++ b/src/sim/ticked_object.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+#include "sim/ticked_object.hh"
+
+Ticked::Ticked(ClockedObject &object_,
+ Stats::Scalar *imported_num_cycles,
+ Event::Priority priority) :
+ object(object_),
+ event(*this, priority),
+ running(false),
+ lastStopped(0),
+ /* Allocate numCycles if an external stat wasn't passed in */
+ numCyclesLocal((imported_num_cycles ? NULL : new Stats::Scalar)),
+ numCycles((imported_num_cycles ? *imported_num_cycles :
+ *numCyclesLocal))
+{ }
+
+void
+Ticked::regStats()
+{
+ if (numCyclesLocal) {
+ numCycles
+ .name(object.name() + ".tickCycles")
+ .desc("Number of cycles that the object ticked or was stopped");
+ }
+
+ tickCycles
+ .name(object.name() + ".tickCycles")
+ .desc("Number of cycles that the object actually ticked");
+
+ idleCycles
+ .name(object.name() + ".idleCycles")
+ .desc("Total number of cycles that the object has spent stopped");
+ idleCycles = numCycles - tickCycles;
+}
+
+void
+Ticked::serialize(std::ostream &os)
+{
+ uint64_t lastStoppedUint = lastStopped;
+
+ paramOut(os, "lastStopped", lastStoppedUint);
+}
+
+void
+Ticked::unserialize(Checkpoint *cp, const std::string &section)
+{
+ uint64_t lastStoppedUint;
+
+ paramIn(cp, section, "lastStopped", lastStoppedUint);
+
+ lastStopped = Cycles(lastStoppedUint);
+}
+
+TickedObject::TickedObject(TickedObjectParams *params,
+ Event::Priority priority) :
+ ClockedObject(params),
+ /* Make numCycles in Ticked */
+ Ticked(*this, NULL, priority)
+{ }
+
+void
+TickedObject::regStats()
+{
+ Ticked::regStats();
+}
+
+void
+TickedObject::serialize(std::ostream &os)
+{
+ Ticked::serialize(os);
+ ClockedObject::serialize(os);
+}
+void
+TickedObject::unserialize(Checkpoint *cp, const std::string &section)
+{
+ Ticked::unserialize(cp, section);
+ ClockedObject::unserialize(cp, section);
+}
diff --git a/src/sim/ticked_object.hh b/src/sim/ticked_object.hh
new file mode 100644
index 000000000..5bca92443
--- /dev/null
+++ b/src/sim/ticked_object.hh
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013-2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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: Andrew Bardsley
+ */
+
+/**
+ * @file
+ *
+ * Base classes for ClockedObjects which have evaluate functions to
+ * look like clock ticking operations. TickedObject attaches gem5's event
+ * queue to Ticked to apply actual scheduling.
+ */
+
+#ifndef __SIM_TICKED_OBJECT_HH__
+#define __SIM_TICKED_OBJECT_HH__
+
+#include "params/TickedObject.hh"
+#include "sim/clocked_object.hh"
+
+/** Ticked attaches gem5's event queue/scheduler to evaluate
+ * calls and provides a start/stop interface to ticking.
+ *
+ * Ticked is not a ClockedObject but can be attached to one by
+ * inheritance and by calling regStats, serialize/unserialize */
+class Ticked
+{
+ protected:
+ /** An event to call process periodically */
+ class ClockEvent : public Event
+ {
+ public:
+ Ticked &owner;
+
+ ClockEvent(Ticked &owner_, Priority priority) :
+ Event(priority),
+ owner(owner_)
+ { }
+
+ /** Evaluate and reschedule */
+ void
+ process()
+ {
+ ++owner.tickCycles;
+ ++owner.numCycles;
+ owner.evaluate();
+ if (owner.running) {
+ owner.object.schedule(this,
+ owner.object.clockEdge(Cycles(1)));
+ }
+ }
+ };
+
+ friend class ClockEvent;
+
+ /** ClockedObject who is responsible for this Ticked's actions/stats */
+ ClockedObject &object;
+
+ /** The single instance of ClockEvent used */
+ ClockEvent event;
+
+ /** Have I been started? and am not stopped */
+ bool running;
+
+ /** Time of last stop event to calculate run time */
+ Cycles lastStopped;
+
+ private:
+ /** Locally allocated stats */
+ Stats::Scalar *numCyclesLocal;
+
+ protected:
+ /** Total number of cycles either ticked or spend stopped */
+ Stats::Scalar &numCycles;
+
+ /** Number of cycles ticked */
+ Stats::Scalar tickCycles;
+
+ /** Number of cycles stopped */
+ Stats::Formula idleCycles;
+
+ public:
+ Ticked(ClockedObject &object_,
+ Stats::Scalar *imported_num_cycles = NULL,
+ Event::Priority priority = Event::CPU_Tick_Pri);
+
+ virtual ~Ticked() { }
+
+ /** Register {num,ticks}Cycles if necessary. If numCycles is
+ * imported, be sure to register it *before* calling this regStats */
+ void regStats();
+
+ /** Start ticking */
+ void
+ start()
+ {
+ if (!running) {
+ if (!event.scheduled())
+ object.schedule(event, object.clockEdge(Cycles(1)));
+ running = true;
+ numCycles += cyclesSinceLastStopped();
+ }
+ }
+
+ /** How long have we been stopped for? */
+ Cycles
+ cyclesSinceLastStopped() const
+ {
+ return object.curCycle() - lastStopped;
+ }
+
+ /** Reset stopped time to current time */
+ void
+ resetLastStopped()
+ {
+ lastStopped = object.curCycle();
+ }
+
+ /** Cancel the next tick event and issue no more */
+ void
+ stop()
+ {
+ if (running) {
+ if (event.scheduled())
+ object.deschedule(event);
+ running = false;
+ resetLastStopped();
+ }
+ }
+
+ /** Checkpoint lastStopped */
+ void serialize(std::ostream &os);
+ void unserialize(Checkpoint *cp, const std::string &section);
+
+ /** Action to call on the clock tick */
+ virtual void evaluate() = 0;
+};
+
+/** TickedObject attaches Ticked to ClockedObject and can be used as
+ * a base class where ticked operation */
+class TickedObject : public ClockedObject, public Ticked
+{
+ public:
+ TickedObject(TickedObjectParams *params,
+ Event::Priority priority = Event::CPU_Tick_Pri);
+
+ /** Disambiguate to make these functions overload correctly */
+ using ClockedObject::regStats;
+ using ClockedObject::serialize;
+ using ClockedObject::unserialize;
+
+ /** Pass on regStats, serialize etc. onto Ticked */
+ void regStats();
+ void serialize(std::ostream &os);
+ void unserialize(Checkpoint *cp, const std::string &section);
+};
+
+#endif /* __SIM_TICKED_OBJECT_HH__ */
diff --git a/util/minorview.py b/util/minorview.py
new file mode 100755
index 000000000..1a6b47b01
--- /dev/null
+++ b/util/minorview.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+#
+# minorview.py: Minorview visuliser for MinorCPU model MinorTrace output
+#
+
+import gtk
+import os
+import sys
+import argparse
+
+# Find MinorView modules even if not called from minorview directory
+minorviewDir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(minorviewDir)
+
+from minorview.model import BlobModel
+from minorview.view import BlobView, BlobController, BlobWindow
+from minorview.point import Point
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Minor visualiser')
+
+ parser.add_argument('--picture', metavar='picture-file',
+ default=minorviewDir + '/minorview/minor.pic',
+ help='markup file containing blob information '
+ + '(default: <minorview-path>/minor.pic)')
+ parser.add_argument('--prefix', metavar='name', default='system.cpu',
+ help='name prefix in trace for CPU to be visualised (default: '
+ + 'system.cpu)')
+ parser.add_argument('--start-time', metavar='time', type=int, default=0,
+ help='time of first event to load from file')
+ parser.add_argument('--end-time', metavar='time', type=int, default=None,
+ help='time of last event to load from file')
+ parser.add_argument('--mini-views', action='store_true', default=False,
+ help='show tiny views of the next 10 time steps')
+ parser.add_argument('eventFile', metavar='event-file', default='ev')
+
+ args = parser.parse_args(sys.argv[1:])
+
+ model = BlobModel(unitNamePrefix=args.prefix)
+
+ if args.picture and os.access(args.picture, os.O_RDONLY):
+ model.load_picture(args.picture)
+ else:
+ parser.error('Can\'t read picture file: ' + args.picture)
+
+ # Make the key objects
+ view = BlobView(model)
+ controller = BlobController(model, view,
+ defaultEventFile=args.eventFile,
+ defaultPictureFile=args.picture)
+ window = BlobWindow(model, view, controller)
+ window.add_control_bar(controller.bar)
+
+ # Miniviews allow future timesteps to appear at the bottom of the
+ # display.
+ if args.mini_views:
+ window.miniViewCount = 10
+
+ window.show_window()
+
+ if args.eventFile and os.access(args.eventFile, os.O_RDONLY):
+ controller.startTime = args.start_time
+ controller.endTime = args.end_time
+ model.load_events(args.eventFile, startTime=args.start_time,
+ endTime=args.end_time)
+ controller.set_time_index(0)
+ else:
+ parser.error('Can\'t read event file: ' + args.eventFile)
+
+ gtk.main()
diff --git a/util/minorview/__init__.py b/util/minorview/__init__.py
new file mode 100644
index 000000000..0b957dcec
--- /dev/null
+++ b/util/minorview/__init__.py
@@ -0,0 +1,36 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
diff --git a/util/minorview/blobs.py b/util/minorview/blobs.py
new file mode 100644
index 000000000..32d08dfa2
--- /dev/null
+++ b/util/minorview/blobs.py
@@ -0,0 +1,461 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+#
+# blobs.py: Blobs are the visual blocks, arrows and other coloured
+# objects on the visualiser. This file contains Blob definition and
+# their rendering instructions in pygtk/cairo.
+#
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+import cairo
+import re
+import math
+
+from point import Point
+import parse
+import colours
+from colours import backgroundColour, black
+import model
+
+def centre_size_to_sides(centre, size):
+ """Returns a 4-tuple of the relevant ordinates of the left,
+ right, top and bottom sides of the described rectangle"""
+ (x, y) = centre.to_pair()
+ (half_width, half_height) = (size.scale(0.5)).to_pair()
+ left = x - half_width
+ right = x + half_width
+ top = y - half_height
+ bottom = y + half_height
+ return (left, right, top, bottom)
+
+def box(cr, centre, size):
+ """Draw a simple box"""
+ (left, right, top, bottom) = centre_size_to_sides(centre, size)
+ cr.move_to(left, top)
+ cr.line_to(right, top)
+ cr.line_to(right, bottom)
+ cr.line_to(left, bottom)
+ cr.close_path()
+
+def stroke_and_fill(cr, colour):
+ """Stroke with the current colour then fill the same path with the
+ given colour"""
+ join = cr.get_line_join()
+ cr.set_line_join(gtk.gdk.JOIN_ROUND)
+ cr.close_path()
+ cr.set_source_color(backgroundColour)
+ cr.stroke_preserve()
+ cr.set_source_color(colour)
+ cr.fill()
+ cr.set_line_join(join)
+
+def striped_box(cr, centre, size, colours):
+ """Fill a rectangle (without outline) striped with the colours given"""
+ num_colours = len(colours)
+ if num_colours == 0:
+ box(cr, centre, size)
+ cr.set_source_color(backgroundColour)
+ cr.fill()
+ elif num_colours == 1:
+ box(cr, centre, size)
+ stroke_and_fill(cr, colours[0])
+ else:
+ (left, right, top, bottom) = centre_size_to_sides(centre, size)
+ (width, height) = size.to_pair()
+ x_stripe_width = width / num_colours
+ half_x_stripe_width = x_stripe_width / 2.0
+ # Left triangle
+ cr.move_to(left, bottom)
+ cr.line_to(left + half_x_stripe_width, bottom)
+ cr.line_to(left + x_stripe_width + half_x_stripe_width, top)
+ cr.line_to(left, top)
+ stroke_and_fill(cr, colours[0])
+ # Stripes
+ for i in xrange(1, num_colours - 1):
+ xOffset = x_stripe_width * i
+ cr.move_to(left + xOffset - half_x_stripe_width, bottom)
+ cr.line_to(left + xOffset + half_x_stripe_width, bottom)
+ cr.line_to(left + xOffset + x_stripe_width +
+ half_x_stripe_width, top)
+ cr.line_to(left + xOffset + x_stripe_width -
+ half_x_stripe_width, top)
+ stroke_and_fill(cr, colours[i])
+ # Right triangle
+ cr.move_to((right - x_stripe_width) - half_x_stripe_width, bottom)
+ cr.line_to(right, bottom)
+ cr.line_to(right, top)
+ cr.line_to((right - x_stripe_width) + half_x_stripe_width, top)
+ stroke_and_fill(cr, colours[num_colours - 1])
+
+def speech_bubble(cr, top_left, size, unit):
+ """Draw a speech bubble with 'size'-sized internal space with its
+ top left corner at Point(2.0 * unit, 2.0 * unit)"""
+ def local_arc(centre, angleFrom, angleTo):
+ cr.arc(centre.x, centre.y, unit, angleFrom * math.pi,
+ angleTo * math.pi)
+
+ cr.move_to(*top_left.to_pair())
+ cr.rel_line_to(unit * 2.0, unit)
+ cr.rel_line_to(size.x, 0.0)
+ local_arc(top_left + Point(size.x + unit * 2.0, unit * 2.0), -0.5, 0.0)
+ cr.rel_line_to(0.0, size.y)
+ local_arc(top_left + Point(size.x + unit * 2.0, size.y + unit * 2.0),
+ 0, 0.5)
+ cr.rel_line_to(-size.x, 0.0)
+ local_arc(top_left + Point(unit * 2.0, size.y + unit * 2.0), 0.5, 1.0)
+ cr.rel_line_to(0, -size.y)
+ cr.close_path()
+
+def open_bottom(cr, centre, size):
+ """Draw a box with left, top and right sides"""
+ (left, right, top, bottom) = centre_size_to_sides(centre, size)
+ cr.move_to(left, bottom)
+ cr.line_to(left, top)
+ cr.line_to(right, top)
+ cr.line_to(right, bottom)
+
+def fifo(cr, centre, size):
+ """Draw just the vertical sides of a box"""
+ (left, right, top, bottom) = centre_size_to_sides(centre, size)
+ cr.move_to(left, bottom)
+ cr.line_to(left, top)
+ cr.move_to(right, bottom)
+ cr.line_to(right, top)
+
+def cross(cr, centre, size):
+ """Draw a cross parallel with the axes"""
+ (left, right, top, bottom) = centre_size_to_sides(centre, size)
+ (x, y) = centre.to_pair()
+ cr.move_to(left, y)
+ cr.line_to(right, y)
+ cr.move_to(x, top)
+ cr.line_to(x, bottom)
+
+class Blob(object):
+ """Blob super class"""
+ def __init__(self, picChar, unit, topLeft, colour, size = Point(1,1)):
+ self.picChar = picChar
+ self.unit = unit
+ self.displayName = unit
+ self.nameLoc = 'top'
+ self.topLeft = topLeft
+ self.colour = colour
+ self.size = size
+ self.border = 1.0
+ self.dataSelect = model.BlobDataSelect()
+ self.shorten = 0
+
+ def render(self, cr, view, event, select, time):
+ """Render this blob with the given event's data. Returns either
+ None or a pair of (centre, size) in device coordinates for the drawn
+ blob. The return value can be used to detect if mouse clicks on
+ the canvas are within the blob"""
+ return None
+
+class Block(Blob):
+ """Blocks are rectangular blogs colourable with a 2D grid of striped
+ blocks. visualDecoder specifies how event data becomes this coloured
+ grid"""
+ def __init__(self, picChar, unit, topLeft=Point(0,0),
+ colour=colours.black,
+ size=Point(1,1)):
+ super(Block,self).__init__(picChar, unit, topLeft, colour,
+ size = size)
+ # {horiz, vert}
+ self.stripDir = 'horiz'
+ # {LR, RL}: LR means the first strip will be on the left/top,
+ # RL means the first strip will be on the right/bottom
+ self.stripOrd = 'LR'
+ # Number of blank strips if this is a frame
+ self.blankStrips = 0
+ # {box, fifo, openBottom}
+ self.shape = 'box'
+ self.visualDecoder = None
+
+ def render(self, cr, view, event, select, time):
+ # Find the right event, visuals and sizes for things
+ if event is None or self.displayName.startswith('_'):
+ event = model.BlobEvent(self.unit, time)
+
+ if self.picChar in event.visuals:
+ strips = event.visuals[self.picChar].to_striped_block(
+ select & self.dataSelect)
+ else:
+ strips = [[[colours.unknownColour]]]
+
+ if self.stripOrd == 'RL':
+ strips.reverse()
+
+ if len(strips) == 0:
+ strips = [[colours.errorColour]]
+ print 'Problem with the colour of event:', event
+
+ num_strips = len(strips)
+ strip_proportion = 1.0 / num_strips
+ first_strip_offset = (num_strips / 2.0) - 0.5
+
+ # Adjust blocks with 'shorten' attribute to the length of the data
+ size = Point(*self.size.to_pair())
+ if self.shorten != 0 and self.size.x > (num_strips * self.shorten):
+ size.x = num_strips * self.shorten
+
+ box_size = size - view.blobIndentFactor.scale(2)
+
+ # Now do cr sensitive things
+ cr.save()
+ cr.scale(*view.pitch.to_pair())
+ cr.translate(*self.topLeft.to_pair())
+ cr.translate(*(size - Point(1,1)).scale(0.5).to_pair())
+
+ translated_centre = Point(*cr.user_to_device(0.0, 0.0))
+ translated_size = \
+ Point(*cr.user_to_device_distance(*size.to_pair()))
+
+ # The 2D grid is a grid of strips of blocks. Data [[1,2],[3]]
+ # is 2 strips of 2 and 1 blocks respectively.
+ # if stripDir == 'horiz', strips are stacked vertically
+ # from top to bottom if stripOrd == 'LR' or bottom to top if
+ # stripOrd == 'RL'.
+ # if stripDir == 'vert', strips are stacked horizontally
+ # from left to right if stripOf == 'LR' or right to left if
+ # stripOrd == 'RL'.
+
+ strip_is_horiz = self.stripDir == 'horiz'
+
+ if strip_is_horiz:
+ strip_step_base = Point(1.0,0.0)
+ block_step_base = Point(0.0,1.0)
+ else:
+ strip_step_base = Point(0.0,1.0)
+ block_step_base = Point(1.0,0.0)
+
+ strip_size = (box_size * (strip_step_base.scale(strip_proportion) +
+ block_step_base))
+ strip_step = strip_size * strip_step_base
+ strip_centre = Point(0,0) - (strip_size *
+ strip_step_base.scale(first_strip_offset))
+
+ cr.set_line_width(view.midLineWidth / view.pitch.x)
+
+ # Draw the strips and their blocks
+ for strip_index in xrange(0, num_strips):
+ num_blocks = len(strips[strip_index])
+ block_proportion = 1.0 / num_blocks
+ firstBlockOffset = (num_blocks / 2.0) - 0.5
+
+ block_size = (strip_size *
+ (block_step_base.scale(block_proportion) +
+ strip_step_base))
+ block_step = block_size * block_step_base
+ block_centre = (strip_centre + strip_step.scale(strip_index) -
+ (block_size * block_step_base.scale(firstBlockOffset)))
+
+ for block_index in xrange(0, num_blocks):
+ striped_box(cr, block_centre +
+ block_step.scale(block_index), block_size,
+ strips[strip_index][block_index])
+
+ cr.set_font_size(0.7)
+ if self.border > 0.5:
+ weight = cairo.FONT_WEIGHT_BOLD
+ else:
+ weight = cairo.FONT_WEIGHT_NORMAL
+ cr.select_font_face('Helvetica', cairo.FONT_SLANT_NORMAL,
+ weight)
+
+ xb, yb, width, height, dx, dy = cr.text_extents(self.displayName)
+
+ text_comfort_space = 0.15
+
+ if self.nameLoc == 'left':
+ # Position text vertically along left side, top aligned
+ cr.save()
+ cr.rotate(- (math.pi / 2.0))
+ text_point = Point(size.y, size.x).scale(0.5) * Point(-1, -1)
+ text_point += Point(max(0, size.y - width), 0)
+ text_point += Point(-text_comfort_space, -text_comfort_space)
+ else: # Including top
+ # Position text above the top left hand corner
+ text_point = size.scale(0.5) * Point(-1,-1)
+ text_point += Point(0.00, -text_comfort_space)
+
+ if (self.displayName != '' and
+ not self.displayName.startswith('_')):
+ cr.set_source_color(self.colour)
+ cr.move_to(*text_point.to_pair())
+ cr.show_text(self.displayName)
+
+ if self.nameLoc == 'left':
+ cr.restore()
+
+ # Draw the outline shape
+ cr.save()
+ if strip_is_horiz:
+ cr.rotate(- (math.pi / 2.0))
+ box_size = Point(box_size.y, box_size.x)
+
+ if self.stripOrd == "RL":
+ cr.rotate(math.pi)
+
+ if self.shape == 'box':
+ box(cr, Point(0,0), box_size)
+ elif self.shape == 'openBottom':
+ open_bottom(cr, Point(0,0), box_size)
+ elif self.shape == 'fifo':
+ fifo(cr, Point(0,0), box_size)
+ cr.restore()
+
+ # Restore scale and stroke the outline
+ cr.restore()
+ cr.set_source_color(self.colour)
+ cr.set_line_width(view.thickLineWidth * self.border)
+ cr.stroke()
+
+ # Return blob size/position
+ if self.unit == '_':
+ return None
+ else:
+ return (translated_centre, translated_size)
+
+class Key(Blob):
+ """Draw a key to the special (and numeric colours) with swatches of the
+ colours half as wide as the key"""
+ def __init__(self, picChar, unit, topLeft, colour=colours.black,
+ size=Point(1,1)):
+ super(Key,self).__init__(picChar, unit, topLeft, colour, size = size)
+ self.colours = 'BBBB'
+ self.displayName = unit
+
+ def render(self, cr, view, event, select, time):
+ cr.save()
+ cr.scale(*view.pitch.to_pair())
+ cr.translate(*self.topLeft.to_pair())
+ # cr.translate(*(self.size - Point(1,1)).scale(0.5).to_pair())
+ half_width = self.size.x / 2.0
+ cr.translate(*(self.size - Point(1.0 + half_width,1.0)).scale(0.5).
+ to_pair())
+
+ num_colours = len(self.colours)
+ cr.set_line_width(view.midLineWidth / view.pitch.x)
+
+ blob_size = (Point(half_width,0.0) +
+ (self.size * Point(0.0,1.0 / num_colours)))
+ blob_step = Point(0.0,1.0) * blob_size
+ first_blob_centre = (Point(0.0,0.0) -
+ blob_step.scale((num_colours / 2.0) - 0.5))
+
+ cr.set_source_color(self.colour)
+ cr.set_line_width(view.thinLineWidth / view.pitch.x)
+
+ blob_proportion = 0.8
+
+ real_blob_size = blob_size.scale(blob_proportion)
+
+ cr.set_font_size(0.8 * blob_size.y * blob_proportion)
+ cr.select_font_face('Helvetica', cairo.FONT_SLANT_NORMAL,
+ cairo.FONT_WEIGHT_BOLD)
+
+ for i in xrange(0, num_colours):
+ centre = first_blob_centre + blob_step.scale(i)
+ box(cr, centre, real_blob_size)
+
+ colour_char = self.colours[i]
+ if colour_char.isdigit():
+ cr.set_source_color(colours.number_to_colour(
+ int(colour_char)))
+ label = '...' + colour_char
+ else:
+ cr.set_source_color(model.special_state_colours[colour_char])
+ label = model.special_state_names[colour_char]
+
+ cr.fill_preserve()
+ cr.set_source_color(self.colour)
+ cr.stroke()
+
+ xb, yb, width, height, dx, dy = cr.text_extents(label)
+
+ text_left = (centre + (Point(0.5,0.0) * blob_size) +
+ Point(0.0, height / 2.0))
+
+ cr.move_to(*text_left.to_pair())
+ cr.show_text(label)
+
+class Arrow(Blob):
+ """Draw a left or right facing arrow"""
+ def __init__(self, unit, topLeft, colour=colours.black,
+ size=Point(1.0,1.0), direc='right'):
+ super(Arrow,self).__init__(unit, unit, topLeft, colour, size = size)
+ self.direc = direc
+
+ def render(self, cr, view, event, select, time):
+ cr.save()
+ cr.scale(*view.pitch.to_pair())
+ cr.translate(*self.topLeft.to_pair())
+ cr.translate(*(self.size - Point(1,1)).scale(0.5).to_pair())
+ cr.scale(*self.size.to_pair())
+ (blob_indent_x, blob_indent_y) = \
+ (view.blobIndentFactor / self.size).to_pair()
+ left = -0.5 - blob_indent_x
+ right = 0.5 + blob_indent_x
+
+ thickness = 0.2
+ flare = 0.2
+
+ if self.direc == 'left':
+ cr.rotate(math.pi)
+
+ cr.move_to(left, -thickness)
+ cr.line_to(0.0, -thickness)
+ cr.line_to(0.0, -(thickness + flare))
+ cr.line_to(right, 0)
+ # Break arrow to prevent the point ruining the appearance of boxes
+ cr.move_to(right, 0)
+ cr.line_to(0.0, (thickness + flare))
+ cr.line_to(0.0, +thickness)
+ cr.line_to(left, +thickness)
+
+ cr.restore()
+
+ # Draw arrow a bit more lightly than the standard line width
+ cr.set_line_width(cr.get_line_width() * 0.75)
+ cr.set_source_color(self.colour)
+ cr.stroke()
+
+ return None
diff --git a/util/minorview/colours.py b/util/minorview/colours.py
new file mode 100644
index 000000000..b8394b019
--- /dev/null
+++ b/util/minorview/colours.py
@@ -0,0 +1,68 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+import gtk
+
+# All the miscellaneous colours used in the interface
+unknownColour = gtk.gdk.color_parse('magenta')
+blockedColour = gtk.gdk.color_parse('grey')
+bubbleColour = gtk.gdk.color_parse('bisque')
+emptySlotColour = gtk.gdk.color_parse('grey90')
+reservedSlotColour = gtk.gdk.color_parse('cyan')
+errorColour = gtk.gdk.color_parse('blue')
+backgroundColour = gtk.gdk.color_parse('white')
+faultColour = gtk.gdk.color_parse('dark cyan')
+readColour = gtk.gdk.color_parse('red')
+writeColour = gtk.gdk.color_parse('white')
+
+black = gtk.gdk.color_parse('black')
+
+def name_to_colour(name):
+ """Convert a colour name to a GdkColor"""
+ try:
+ ret = gtk.gdk.color_parse(name)
+ except:
+ ret = unknownColour
+ return ret
+
+number_colour_code = map(name_to_colour, ['black', 'brown', 'red', 'orange',
+ 'yellow', 'green', 'blue', 'violet', 'grey', 'white'])
+
+def number_to_colour(num):
+ """Convert the last decimal digit of an integer into a resistor
+ stripe colour"""
+ return number_colour_code[num % 10]
diff --git a/util/minorview/minor.pic b/util/minorview/minor.pic
new file mode 100644
index 000000000..52731d66f
--- /dev/null
+++ b/util/minorview/minor.pic
@@ -0,0 +1,154 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+#
+# minor.pic: Pipeline appearance of the Minor pipeline for minorview
+
+# Markup of the pipeline blocks.
+# '>' and '<' are connecting arrows.
+# '/', ':' and '\' mark multi-line arrows.
+# All other (non-space) character rectangles are the shapes of the
+# corresponding blocks below the markup.
+
+<<<
+ IPIPIPIPIPIPIPIPIP LSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLS KKKKKKKKKK
+ IP IP LS LS KK KK
+ IPiririr-\itititIP LSimimim drdrdr-\dtdtdtdt sbsbsbsbsbsbLS KK KK
+ IPiririr-/itititIP LSimimim drdrdr-/dtdtdtdt sbsbsbsbsbsbLS KK KK
+ IPIPIPIPIPIPIPIPIP LSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLSLS KK KK
+ KK KK
+ KK KK
+ acacac sasasasasasasasa EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE KK KK
+ acacac sasasasasasasasa EE EE KK KK
+ EEscscscscscscscscscscscscscscscscscscscEE KK KK
+ EEscscscscscscscscscscscscscscscscscscscEE KK KK
+ EE EE KK KK
+ F1F1F1F1-\1212-\F2F2F2F2-\2D2D-\DDDD-\DEDE-\EEiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqEE KK KK
+ F1 F1 :1212 :F2 F2 :2D2D :DDDD :DEDE :EEiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqiqEE KK KK
+ F1fefeF1 :1212 :F2fifiF2 :2D2D :DDDD :DEDE :EE EE KK KK
+ F1fefeF1 :1212 :F2fifiF2 :2D2D :DDDD :DEDE :EEEiEiEiEi f0f0f0f0f0f0f0f0f0f0f0 ccccEE KK KK
+ F1fefeF1 :1212 :F2fifiF2 :2D2D :DDDD :DEDE :EEEiEiEiEi f0f0f0f0f0f0f0f0f0f0f0 ccccEE KK KK
+ F1fefeF1 :1212 :F2fifiF2-/2D2D-/DDDD-/DEDE-/EEEiEiEiEi f1f1f1f1f1f1f1f1f1f1f1 ccccEE KKKKKKKKKK
+ F1fefeF1 :1212 :F2fifiF2 DDDD EEEiEiEiEi f1f1f1f1f1f1f1f1f1f1f1 ccccEE
+ F1fefeF1-/1212-/F2fifiF2 DDDD EEEiEiEiEi f2f2f2f2f2f2f2f2f2f2f2 ccccEE
+ b2b2-\F1fefeF1 -\F2fifiF2 DDDD EEEiEiEiEi f2f2f2f2f2f2f2f2f2f2f2 ccccEE-\b1b1
+ b2b2-/F1fefeF1 -/F2fifiF2 DDDD EEEiEiEiEi f3f3f3f3f3f3f3f3f3f3f3 ccccEE-/b1b1
+ F1fefeF1/-2121/-F2fifiF2 DDDD EEEiEiEiEi f3f3f3f3f3f3f3f3f3f3f3 ccccEE
+ F1F1F1F1\-2121\-F2F2F2F2 DDDD EEEiEiEiEi f4f4f4f4f4f4f4f4f4f4f4 ccccEE
+ EEEiEiEiEi f4f4f4f4f4f4f4f4f4f4f4 ccccEE
+ FiFiFiFi DiDiDiDi EE f5f5f5f5f5f5f5f5f5f5f5 ccccEE
+ Fi Fi Di Di EE f5f5f5f5f5f5f5f5f5f5f5 ccccEE
+ FiFiFiFi DiDiDiDi EE f6f6f6f6f6f6f6f6f6f6f6 ccccEE
+ EE f6f6f6f6f6f6f6f6f6f6f6 ccccEE
+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+>>>
+
+# Macros for block type appearance. Macros are expanded in the 'block'
+# lines below and can include other macros.
+#
+# Attributes are name=value pairs.
+# Available names (and valid values);
+#
+# shape: {fifo, box, openBottom}
+# stripDir: {vert, horiz}
+# stripOrd: {RL, LR}
+# decoder: {insts, lines, branch, dcache, counts}
+# border: {thin, mid, thick}
+# dataElement: <name>
+# hideId: ({T, S, P, L, F, E})*
+# name: <string>
+# colours: ({U, B, -, E, R, F, r, w, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9})*
+# type: {key, block}
+
+# macro fifo: shape=fifo stripDir=vert
+macro fifo: shape=openBottom stripDir=horiz stripOrd=RL border=mid
+macro fu: decoder=insts border=mid name="" nameLoc=left
+# macro back: decoder=back border=mid dataElement=space name=""
+macro prediction: decoder=branch border=mid dataElement=prediction name=""
+macro forward: border=mid name=""
+macro dcache: fifo decoder=dcache dataElement=addr
+macro icache: fifo decoder=lines
+macro cacheFrame: stripDir=vert decoder=frame blankStrips=5 \
+ dataElement=in_tlb_mem
+macro activity: decoder=counts shorten=2 border=mid
+macro inputBuffer: name="inputBuffer" fifo
+macro seqNum: decoder=counts border=mid
+macro streamFrame: decoder=frame stripDir=vert dataElement=streamSeqNum
+macro predictionFrame: decoder=frame stripDir=vert dataElement=predictionSeqNum
+
+# Block descriptions:
+# description ::= <char>: <unit-name-in-trace>
+# ( {<macro-name>, <name>=<value> )*
+# name ::= ? alphanumeric name with dots ?
+# value ::= "(<char-except-">)*", <char-except-' '>* }
+
+Fi: fetch2.inputBuffer inputBuffer decoder=lines
+Di: decode.inputBuffer inputBuffer decoder=insts hideId=E
+Ei: execute.inputBuffer inputBuffer stripDir=horiz decoder=insts border=mid
+F1: fetch1 streamFrame blankStrips=11 name="Fetch1"
+fe: fetch1 decoder=lines border=thin name="Line"
+F2: fetch2 predictionFrame blankStrips=11 name="Fetch2"
+fi: fetch2 decoder=insts border=thin name="Insts"
+DD: decode decoder=insts name="Decode"
+EE: execute streamFrame blankStrips=21 name="Execute"
+cc: execute decoder=insts name="Commit" border=mid
+12: f1ToF2 forward decoder=lines
+21: f2ToF1 prediction
+2D: f2ToD forward decoder=insts hideId=E
+DE: dToE forward decoder=insts
+b1: eToF1 forward decoder=branch
+b2: eToF1 forward decoder=branch
+IP: fetch1 cacheFrame name="Fetch queues"
+LS: execute.lsq cacheFrame name="LSQ"
+ir: fetch1.requests icache name="Requests"
+it: fetch1.transfers icache name="Transfers"
+dr: execute.lsq.requests dcache name="Requests"
+dt: execute.lsq.transfers dcache name="Transfers"
+sb: execute.lsq.storeBuffer dcache name="Store buffer"
+KK: _ type=key colours="UB-ERFrw0123456789"
+f0: execute.fu.0 fu shorten=2 name=Int
+f1: execute.fu.1 fu shorten=2 name=Int
+f2: execute.fu.2 fu shorten=2 name=Mul
+f3: execute.fu.3 fu shorten=2 name=Div
+f4: execute.fu.4 fu shorten=2 name=Float
+f5: execute.fu.5 fu shorten=2 name=Mem
+f6: execute.fu.6 fu shorten=2 name=Misc
+iq: execute.inFlightInsts fifo decoder=insts name="inFlightInsts"
+im: execute.inFUMemInsts fifo decoder=insts name="inFU..."
+sc: execute.scoreboard name="scoreboard" decoder=indexedCounts \
+ dataElement=busy border=mid name="scoreboard" strips=38 stripelems=3
+sa: activity dataElement=stages activity name="Stage activity"
+ac: activity dataElement=activity decoder=counts border=mid name="Activity"
diff --git a/util/minorview/model.py b/util/minorview/model.py
new file mode 100644
index 000000000..a120f1f7c
--- /dev/null
+++ b/util/minorview/model.py
@@ -0,0 +1,1109 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+import parse
+import colours
+from colours import unknownColour
+from point import Point
+import re
+import blobs
+from time import time as wall_time
+import os
+
+id_parts = "TSPLFE"
+
+all_ids = set(id_parts)
+no_ids = set([])
+
+class BlobDataSelect(object):
+ """Represents which data is displayed for Ided object"""
+ def __init__(self):
+ # Copy all_ids
+ self.ids = set(all_ids)
+
+ def __and__(self, rhs):
+ """And for filtering"""
+ ret = BlobDataSelect()
+ ret.ids = self.ids.intersection(rhs.ids)
+ return ret
+
+class BlobVisualData(object):
+ """Super class for block data colouring"""
+ def to_striped_block(self, select):
+ """Return an array of colours to use for a striped block"""
+ return unknownColour
+
+ def get_inst(self):
+ """Get an instruction Id (if any) from this data"""
+ return None
+
+ def get_line(self):
+ """Get a line Id (if any) from this data"""
+ return None
+
+ def __repr__(self):
+ return self.__class__.__name__ + '().from_string(' + \
+ self.__str__() + ')'
+
+ def __str__(self):
+ return ''
+
+class Id(BlobVisualData):
+ """A line or instruction id"""
+ def __init__(self):
+ self.isFault = False
+ self.threadId = 0
+ self.streamSeqNum = 0
+ self.predictionSeqNum = 0
+ self.lineSeqNum = 0
+ self.fetchSeqNum = 0
+ self.execSeqNum = 0
+
+ def as_list(self):
+ return [self.threadId, self.streamSeqNum, self.predictionSeqNum,
+ self.lineSeqNum, self.fetchSeqNum, self.execSeqNum]
+
+ def __cmp__(self, right):
+ return cmp(self.as_list(), right.as_list())
+
+ def from_string(self, string):
+ m = re.match('^(F;)?(\d+)/(\d+)\.(\d+)/(\d+)(/(\d+)(\.(\d+))?)?',
+ string)
+
+ def seqnum_from_string(string):
+ if string is None:
+ return 0
+ else:
+ return int(string)
+
+ if m is None:
+ print 'Invalid Id string', string
+ else:
+ elems = m.groups()
+
+ if elems[0] is not None:
+ self.isFault = True
+ else:
+ self.isFault = False
+
+ self.threadId = seqnum_from_string(elems[1])
+ self.streamSeqNum = seqnum_from_string(elems[2])
+ self.predictionSeqNum = seqnum_from_string(elems[3])
+ self.lineSeqNum = seqnum_from_string(elems[4])
+ self.fetchSeqNum = seqnum_from_string(elems[6])
+ self.execSeqNum = seqnum_from_string(elems[8])
+ return self
+
+ def get_inst(self):
+ if self.fetchSeqNum != 0:
+ return self
+ else:
+ return None
+
+ def get_line(self):
+ return self
+
+ def __str__(self):
+ """Returns the usual id T/S.P/L/F.E string"""
+ return (
+ str(self.threadId) + '/' +
+ str(self.streamSeqNum) + '.' +
+ str(self.predictionSeqNum) + '/' +
+ str(self.lineSeqNum) + '/' +
+ str(self.fetchSeqNum) + '.' +
+ str(self.execSeqNum))
+
+ def to_striped_block(self, select):
+ ret = []
+
+ if self.isFault:
+ ret.append(colours.faultColour)
+
+ if 'T' in select.ids:
+ ret.append(colours.number_to_colour(self.threadId))
+ if 'S' in select.ids:
+ ret.append(colours.number_to_colour(self.streamSeqNum))
+ if 'P' in select.ids:
+ ret.append(colours.number_to_colour(self.predictionSeqNum))
+ if 'L' in select.ids:
+ ret.append(colours.number_to_colour(self.lineSeqNum))
+ if self.fetchSeqNum != 0 and 'F' in select.ids:
+ ret.append(colours.number_to_colour(self.fetchSeqNum))
+ if self.execSeqNum != 0 and 'E' in select.ids:
+ ret.append(colours.number_to_colour(self.execSeqNum))
+
+ if len(ret) == 0:
+ ret = [colours.unknownColour]
+
+ if self.isFault:
+ ret.append(colours.faultColour)
+
+ return ret
+
+class Branch(BlobVisualData):
+ """Branch data new stream and prediction sequence numbers, a branch
+ reason and a new PC"""
+ def __init__(self):
+ self.newStreamSeqNum = 0
+ self.newPredictionSeqNum = 0
+ self.newPC = 0
+ self.reason = "NoBranch"
+ self.id = Id()
+
+ def from_string(self, string):
+ m = re.match('^(\w+);(\d+)\.(\d+);([0-9a-fA-Fx]+);(.*)$', string)
+
+ if m is not None:
+ self.reason, newStreamSeqNum, newPredictionSeqNum, \
+ newPC, id = m.groups()
+
+ self.newStreamSeqNum = int(newStreamSeqNum)
+ self.newPredictionSeqNum = int(newPredictionSeqNum)
+ self.newPC = int(newPC, 0)
+ self.id = special_view_decoder(Id)(id)
+ # self.branch = special_view_decoder(Branch)(branch)
+ else:
+ print "Bad Branch data:", string
+ return self
+
+ def to_striped_block(self, select):
+ return [colours.number_to_colour(self.newStreamSeqNum),
+ colours.number_to_colour(self.newPredictionSeqNum),
+ colours.number_to_colour(self.newPC)]
+
+class Counts(BlobVisualData):
+ """Treat the input data as just a /-separated list of count values (or
+ just a single value)"""
+ def __init__(self):
+ self.counts = []
+
+ def from_string(self, string):
+ self.counts = map(int, re.split('/', string))
+ return self
+
+ def to_striped_block(self, select):
+ return map(colours.number_to_colour, self.counts)
+
+class Colour(BlobVisualData):
+ """A fixed colour block, used for special colour decoding"""
+ def __init__(self, colour):
+ self.colour = colour
+
+ def to_striped_block(self, select):
+ return [self.colour]
+
+class DcacheAccess(BlobVisualData):
+ """Data cache accesses [RW];id"""
+ def __init__(self):
+ self.direc = 'R'
+ self.id = Id()
+
+ def from_string(self, string):
+ self.direc, id = re.match('^([RW]);([^;]*);.*$', string).groups()
+ self.id.from_string(id)
+ return self
+
+ def get_inst(self):
+ return self.id
+
+ def to_striped_block(self, select):
+ if self.direc == 'R':
+ direc_colour = colours.readColour
+ elif self.direc == 'R':
+ direc_colour = colours.writeColour
+ else:
+ direc_colour = colours.errorColour
+ return [direc_colour] + self.id.to_striped_block(select)
+
+class ColourPattern(object):
+ """Super class for decoders that make 2D grids rather than just single
+ striped blocks"""
+ def elems(self):
+ return []
+
+ def to_striped_block(self, select):
+ return [[[colours.errorColour]]]
+
+def special_view_decoder(class_):
+ """Generate a decode function that checks for special character
+ arguments first (and generates a fixed colour) before building a
+ BlobVisualData of the given class"""
+ def decode(symbol):
+ if symbol in special_state_colours:
+ return Colour(special_state_colours[symbol])
+ else:
+ return class_().from_string(symbol)
+ return decode
+
+class TwoDColours(ColourPattern):
+ """A 2D grid pattern decoder"""
+ def __init__(self, blockss):
+ self.blockss = blockss
+
+ @classmethod
+ def decoder(class_, elemClass, dataName):
+ """Factory for making decoders for particular block types"""
+ def decode(pairs):
+ if dataName not in pairs:
+ print 'TwoDColours: no event data called:', \
+ dataName, 'in:', pairs
+ return class_([[Colour(colours.errorColour)]])
+ else:
+ parsed = parse.list_parser(pairs[dataName])
+ return class_(parse.map2(special_view_decoder(elemClass), \
+ parsed))
+ return decode
+
+ @classmethod
+ def indexed_decoder(class_, elemClass, dataName, picPairs):
+ """Factory for making decoders for particular block types but
+ where the list elements are pairs of (index, data) and
+ strip and stripelems counts are picked up from the pair
+ data on the decoder's picture file. This gives a 2D layout
+ of the values with index 0 at strip=0, elem=0 and index 1
+ at strip=0, elem=1"""
+ def decode(pairs):
+ if dataName not in pairs:
+ print 'TwoDColours: no event data called:', \
+ dataName, 'in:', pairs
+ return class_([[Colour(colours.errorColour)]])
+ else:
+ strips = int(picPairs['strips'])
+ strip_elems = int(picPairs['stripelems'])
+
+ raw_iv_pairs = pairs[dataName]
+
+ parsed = parse.parse_indexed_list(raw_iv_pairs)
+
+ array = [[Colour(colours.emptySlotColour)
+ for i in xrange(0, strip_elems)]
+ for j in xrange(0, strips)]
+
+ for index, value in parsed:
+ try:
+ array[index % strips][index / strips] = \
+ special_view_decoder(elemClass)(value)
+ except:
+ print "Element out of range strips: %d," \
+ " stripelems %d, index: %d" % (strips,
+ strip_elems, index)
+
+ # return class_(array)
+ return class_(array)
+ return decode
+
+ def elems(self):
+ """Get a flat list of all elements"""
+ ret = []
+ for blocks in self.blockss:
+ ret += blocks
+ return ret
+
+ def to_striped_block(self, select):
+ return parse.map2(lambda d: d.to_striped_block(select), self.blockss)
+
+class FrameColours(ColourPattern):
+ """Decode to a 2D grid which has a single occupied row from the event
+ data and some blank rows forming a frame with the occupied row as a
+ 'title' coloured stripe"""
+ def __init__(self, block, numBlankSlots):
+ self.numBlankSlots = numBlankSlots
+ self.block = block
+
+ @classmethod
+ def decoder(class_, elemClass, numBlankSlots, dataName):
+ """Factory for element type"""
+ def decode(pairs):
+ if dataName not in pairs:
+ print 'FrameColours: no event data called:', dataName, \
+ 'in:', pairs
+ return class_([Colour(colours.errorColour)])
+ else:
+ parsed = parse.list_parser(pairs[dataName])
+ return class_(special_view_decoder(elemClass)
+ (parsed[0][0]), numBlankSlots)
+ return decode
+
+ def elems(self):
+ return [self.block]
+
+ def to_striped_block(self, select):
+ return ([[self.block.to_striped_block(select)]] +
+ (self.numBlankSlots * [[[colours.backgroundColour]]]))
+
+special_state_colours = {
+ 'U': colours.unknownColour,
+ 'B': colours.blockedColour,
+ '-': colours.bubbleColour,
+ '': colours.emptySlotColour,
+ 'E': colours.emptySlotColour,
+ 'R': colours.reservedSlotColour,
+ 'X': colours.errorColour,
+ 'F': colours.faultColour,
+ 'r': colours.readColour,
+ 'w': colours.writeColour
+ }
+
+special_state_names = {
+ 'U': '(U)nknown',
+ 'B': '(B)locked',
+ '-': '(-)Bubble',
+ '': '()Empty',
+ 'E': '(E)mpty',
+ 'R': '(R)eserved',
+ 'X': '(X)Error',
+ 'F': '(F)ault',
+ 'r': '(r)ead',
+ 'w': '(w)rite'
+ }
+
+special_state_chars = special_state_colours.keys()
+
+# The complete set of available block data types
+decoder_element_classes = {
+ 'insts': Id,
+ 'lines': Id,
+ 'branch': Branch,
+ 'dcache': DcacheAccess,
+ 'counts': Counts
+ }
+
+indexed_decoder_element_classes = {
+ 'indexedCounts' : Counts
+ }
+
+def find_colour_decoder(stripSpace, decoderName, dataName, picPairs):
+ """Make a colour decoder from some picture file blob attributes"""
+ if decoderName == 'frame':
+ return FrameColours.decoder(Counts, stripSpace, dataName)
+ elif decoderName in decoder_element_classes:
+ return TwoDColours.decoder(decoder_element_classes[decoderName],
+ dataName)
+ elif decoderName in indexed_decoder_element_classes:
+ return TwoDColours.indexed_decoder(
+ indexed_decoder_element_classes[decoderName], dataName, picPairs)
+ else:
+ return None
+
+class IdedObj(object):
+ """An object identified by an Id carrying paired data.
+ The super class for Inst and Line"""
+
+ def __init__(self, id, pairs={}):
+ self.id = id
+ self.pairs = pairs
+
+ def __cmp__(self, right):
+ return cmp(self.id, right.id)
+
+ def table_line(self):
+ """Represent the object as a list of table row data"""
+ return []
+
+ # FIXME, add a table column titles?
+
+ def __repr__(self):
+ return ' '.join(self.table_line())
+
+class Inst(IdedObj):
+ """A non-fault instruction"""
+ def __init__(self, id, disassembly, addr, pairs={}):
+ super(Inst,self).__init__(id, pairs)
+ if 'nextAddr' in pairs:
+ self.nextAddr = int(pairs['nextAddr'], 0)
+ del pairs['nextAddr']
+ else:
+ self.nextAddr = None
+ self.disassembly = disassembly
+ self.addr = addr
+
+ def table_line(self):
+ if self.nextAddr is not None:
+ addrStr = '0x%x->0x%x' % (self.addr, self.nextAddr)
+ else:
+ addrStr = '0x%x' % self.addr
+ ret = [addrStr, self.disassembly]
+ for name, value in self.pairs.iteritems():
+ ret.append("%s=%s" % (name, str(value)))
+ return ret
+
+class InstFault(IdedObj):
+ """A fault instruction"""
+ def __init__(self, id, fault, addr, pairs={}):
+ super(InstFault,self).__init__(id, pairs)
+ self.fault = fault
+ self.addr = addr
+
+ def table_line(self):
+ ret = ["0x%x" % self.addr, self.fault]
+ for name, value in self.pairs:
+ ret.append("%s=%s", name, str(value))
+ return ret
+
+class Line(IdedObj):
+ """A fetched line"""
+ def __init__(self, id, vaddr, paddr, size, pairs={}):
+ super(Line,self).__init__(id, pairs)
+ self.vaddr = vaddr
+ self.paddr = paddr
+ self.size = size
+
+ def table_line(self):
+ ret = ["0x%x/0x%x" % (self.vaddr, self.paddr), "%d" % self.size]
+ for name, value in self.pairs:
+ ret.append("%s=%s", name, str(value))
+ return ret
+
+class LineFault(IdedObj):
+ """A faulting line"""
+ def __init__(self, id, fault, vaddr, pairs={}):
+ super(LineFault,self).__init__(id, pairs)
+ self.vaddr = vaddr
+ self.fault = fault
+
+ def table_line(self):
+ ret = ["0x%x" % self.vaddr, self.fault]
+ for name, value in self.pairs:
+ ret.append("%s=%s", name, str(value))
+ return ret
+
+class BlobEvent(object):
+ """Time event for a single blob"""
+ def __init__(self, unit, time, pairs = {}):
+ # blob's unit name
+ self.unit = unit
+ self.time = time
+ # dict of picChar (blob name) to visual data
+ self.visuals = {}
+ # Miscellaneous unparsed MinorTrace line data
+ self.pairs = pairs
+ # Non-MinorTrace debug printout for this unit at this time
+ self.comments = []
+
+ def find_ided_objects(self, model, picChar, includeInstLines):
+ """Find instructions/lines mentioned in the blob's event
+ data"""
+ ret = []
+ if picChar in self.visuals:
+ blocks = self.visuals[picChar].elems()
+ def find_inst(data):
+ instId = data.get_inst()
+ lineId = data.get_line()
+ if instId is not None:
+ inst = model.find_inst(instId)
+ line = model.find_line(instId)
+ if inst is not None:
+ ret.append(inst)
+ if includeInstLines and line is not None:
+ ret.append(line)
+ elif lineId is not None:
+ line = model.find_line(lineId)
+ if line is not None:
+ ret.append(line)
+ map(find_inst, blocks)
+ return sorted(ret)
+
+class BlobModel(object):
+ """Model bringing together blob definitions and parsed events"""
+ def __init__(self, unitNamePrefix=''):
+ self.blobs = []
+ self.unitNameToBlobs = {}
+ self.unitEvents = {}
+ self.clear_events()
+ self.picSize = Point(20,10)
+ self.lastTime = 0
+ self.unitNamePrefix = unitNamePrefix
+
+ def clear_events(self):
+ """Drop all events and times"""
+ self.lastTime = 0
+ self.times = []
+ self.insts = {}
+ self.lines = {}
+ self.numEvents = 0
+
+ for unit, events in self.unitEvents.iteritems():
+ self.unitEvents[unit] = []
+
+ def add_blob(self, blob):
+ """Add a parsed blob to the model"""
+ self.blobs.append(blob)
+ if blob.unit not in self.unitNameToBlobs:
+ self.unitNameToBlobs[blob.unit] = []
+
+ self.unitNameToBlobs[blob.unit].append(blob)
+
+ def add_inst(self, inst):
+ """Add a MinorInst instruction definition to the model"""
+ # Is this a non micro-op instruction. Microops (usually) get their
+ # fetchSeqNum == 0 varient stored first
+ macroop_key = (inst.id.fetchSeqNum, 0)
+ full_key = (inst.id.fetchSeqNum, inst.id.execSeqNum)
+
+ if inst.id.execSeqNum != 0 and macroop_key not in self.insts:
+ self.insts[macroop_key] = inst
+
+ self.insts[full_key] = inst
+
+ def find_inst(self, id):
+ """Find an instruction either as a microop or macroop"""
+ macroop_key = (id.fetchSeqNum, 0)
+ full_key = (id.fetchSeqNum, id.execSeqNum)
+
+ if full_key in self.insts:
+ return self.insts[full_key]
+ elif macroop_key in self.insts:
+ return self.insts[macroop_key]
+ else:
+ return None
+
+ def add_line(self, line):
+ """Add a MinorLine line to the model"""
+ self.lines[line.id.lineSeqNum] = line
+
+ def add_unit_event(self, event):
+ """Add a single event to the model. This must be an event at a
+ time >= the current maximum time"""
+ if event.unit in self.unitEvents:
+ events = self.unitEvents[event.unit]
+ if len(events) > 0 and events[len(events)-1].time > event.time:
+ print "Bad event ordering"
+ events.append(event)
+ self.numEvents += 1
+ self.lastTime = max(self.lastTime, event.time)
+
+ def extract_times(self):
+ """Extract a list of all the times from the seen events. Call after
+ reading events to give a safe index list to use for time indices"""
+ times = {}
+ for unitEvents in self.unitEvents.itervalues():
+ for event in unitEvents:
+ times[event.time] = 1
+ self.times = times.keys()
+ self.times.sort()
+
+ def find_line(self, id):
+ """Find a line by id"""
+ key = id.lineSeqNum
+ return self.lines.get(key, None)
+
+ def find_event_bisection(self, unit, time, events,
+ lower_index, upper_index):
+ """Find an event by binary search on time indices"""
+ while lower_index <= upper_index:
+ pivot = (upper_index + lower_index) / 2
+ pivotEvent = events[pivot]
+ event_equal = (pivotEvent.time == time or
+ (pivotEvent.time < time and
+ (pivot == len(events) - 1 or
+ events[pivot + 1].time > time)))
+
+ if event_equal:
+ return pivotEvent
+ elif time > pivotEvent.time:
+ if pivot == upper_index:
+ return None
+ else:
+ lower_index = pivot + 1
+ elif time < pivotEvent.time:
+ if pivot == lower_index:
+ return None
+ else:
+ upper_index = pivot - 1
+ else:
+ return None
+ return None
+
+ def find_unit_event_by_time(self, unit, time):
+ """Find the last event for the given unit at time <= time"""
+ if unit in self.unitEvents:
+ events = self.unitEvents[unit]
+ ret = self.find_event_bisection(unit, time, events,
+ 0, len(events)-1)
+
+ return ret
+ else:
+ return None
+
+ def find_time_index(self, time):
+ """Find a time index close to the given time (where
+ times[return] <= time and times[return+1] > time"""
+ ret = 0
+ lastIndex = len(self.times) - 1
+ while ret < lastIndex and self.times[ret + 1] <= time:
+ ret += 1
+ return ret
+
+ def add_minor_inst(self, rest):
+ """Parse and add a MinorInst line to the model"""
+ pairs = parse.parse_pairs(rest)
+ other_pairs = dict(pairs)
+
+ id = Id().from_string(pairs['id'])
+ del other_pairs['id']
+
+ addr = int(pairs['addr'], 0)
+ del other_pairs['addr']
+
+ if 'inst' in other_pairs:
+ del other_pairs['inst']
+
+ # Collapse unnecessary spaces in disassembly
+ disassembly = re.sub(' *', ' ',
+ re.sub('^ *', '', pairs['inst']))
+
+ inst = Inst(id, disassembly, addr, other_pairs)
+ self.add_inst(inst)
+ elif 'fault' in other_pairs:
+ del other_pairs['fault']
+
+ inst = InstFault(id, pairs['fault'], addr, other_pairs)
+
+ self.add_inst(inst)
+
+ def add_minor_line(self, rest):
+ """Parse and add a MinorLine line to the model"""
+ pairs = parse.parse_pairs(rest)
+ other_pairs = dict(pairs)
+
+ id = Id().from_string(pairs['id'])
+ del other_pairs['id']
+
+ vaddr = int(pairs['vaddr'], 0)
+ del other_pairs['vaddr']
+
+ if 'paddr' in other_pairs:
+ del other_pairs['paddr']
+ del other_pairs['size']
+ paddr = int(pairs['paddr'], 0)
+ size = int(pairs['size'], 0)
+
+ self.add_line(Line(id,
+ vaddr, paddr, size, other_pairs))
+ elif 'fault' in other_pairs:
+ del other_pairs['fault']
+
+ self.add_line(LineFault(id, pairs['fault'], vaddr, other_pairs))
+
+ def load_events(self, file, startTime=0, endTime=None):
+ """Load an event file and add everything to this model"""
+ def update_comments(comments, time):
+ # Add a list of comments to an existing event, if there is one at
+ # the given time, or create a new, correctly-timed, event from
+ # the last event and attach the comments to that
+ for commentUnit, commentRest in comments:
+ event = self.find_unit_event_by_time(commentUnit, time)
+ # Find an event to which this comment can be attached
+ if event is None:
+ # No older event, make a new empty one
+ event = BlobEvent(commentUnit, time, {})
+ self.add_unit_event(event)
+ elif event.time != time:
+ # Copy the old event and make a new one with the right
+ # time and comment
+ newEvent = BlobEvent(commentUnit, time, event.pairs)
+ newEvent.visuals = dict(event.visuals)
+ event = newEvent
+ self.add_unit_event(event)
+ event.comments.append(commentRest)
+
+ self.clear_events()
+
+ # A negative time will *always* be different from an event time
+ time = -1
+ time_events = {}
+ last_time_lines = {}
+ minor_trace_line_count = 0
+ comments = []
+
+ default_colour = [[colours.unknownColour]]
+ next_progress_print_event_count = 1000
+
+ if not os.access(file, os.R_OK):
+ print 'Can\'t open file', file
+ exit(1)
+ else:
+ print 'Opening file', file
+
+ f = open(file)
+
+ start_wall_time = wall_time()
+
+ # Skip leading events
+ still_skipping = True
+ l = f.readline()
+ while l and still_skipping:
+ match = re.match('^\s*(\d+):', l)
+ if match is not None:
+ event_time = match.groups()
+ if int(event_time[0]) >= startTime:
+ still_skipping = False
+ else:
+ l = f.readline()
+ else:
+ l = f.readline()
+
+ match_line_re = re.compile(
+ '^\s*(\d+):\s*([\w\.]+):\s*(Minor\w+:)?\s*(.*)$')
+
+ # Parse each line of the events file, accumulating comments to be
+ # attached to MinorTrace events when the time changes
+ reached_end_time = False
+ while not reached_end_time and l:
+ match = match_line_re.match(l)
+ if match is not None:
+ event_time, unit, line_type, rest = match.groups()
+ event_time = int(event_time)
+
+ unit = re.sub('^' + self.unitNamePrefix + '\.?(.*)$',
+ '\\1', unit)
+
+ # When the time changes, resolve comments
+ if event_time != time:
+ if self.numEvents > next_progress_print_event_count:
+ print ('Parsed to time: %d' % event_time)
+ next_progress_print_event_count = (
+ self.numEvents + 1000)
+ update_comments(comments, time)
+ comments = []
+ time = event_time
+
+ if line_type is None:
+ # Treat this line as just a 'comment'
+ comments.append((unit, rest))
+ elif line_type == 'MinorTrace:':
+ minor_trace_line_count += 1
+
+ # Only insert this event if it's not the same as
+ # the last event we saw for this unit
+ if last_time_lines.get(unit, None) != rest:
+ event = BlobEvent(unit, event_time, {})
+ pairs = parse.parse_pairs(rest)
+ event.pairs = pairs
+
+ # Try to decode the colour data for this event
+ blobs = self.unitNameToBlobs.get(unit, [])
+ for blob in blobs:
+ if blob.visualDecoder is not None:
+ event.visuals[blob.picChar] = (
+ blob.visualDecoder(pairs))
+
+ self.add_unit_event(event)
+ last_time_lines[unit] = rest
+ elif line_type == 'MinorInst:':
+ self.add_minor_inst(rest)
+ elif line_type == 'MinorLine:':
+ self.add_minor_line(rest)
+
+ if endTime is not None and time > endTime:
+ reached_end_time = True
+
+ l = f.readline()
+
+ update_comments(comments, time)
+ self.extract_times()
+ f.close()
+
+ end_wall_time = wall_time()
+
+ print 'Total events:', minor_trace_line_count, 'unique events:', \
+ self.numEvents
+ print 'Time to parse:', end_wall_time - start_wall_time
+
+ def add_blob_picture(self, offset, pic, nameDict):
+ """Add a parsed ASCII-art pipeline markup to the model"""
+ pic_width = 0
+ for line in pic:
+ pic_width = max(pic_width, len(line))
+ pic_height = len(pic)
+
+ # Number of horizontal characters per 'pixel'. Should be 2
+ charsPerPixel = 2
+
+ # Clean up pic_width to a multiple of charsPerPixel
+ pic_width = (pic_width + charsPerPixel - 1) // 2
+
+ self.picSize = Point(pic_width, pic_height)
+
+ def pic_at(point):
+ """Return the char pair at the given point.
+ Returns None for characters off the picture"""
+ x, y = point.to_pair()
+ x *= 2
+ if y >= len(pic) or x >= len(pic[y]):
+ return None
+ else:
+ return pic[y][x:x + charsPerPixel]
+
+ def clear_pic_at(point):
+ """Clear the chars at point so we don't trip over them again"""
+ line = pic[point.y]
+ x = point.x * charsPerPixel
+ pic[point.y] = line[0:x] + (' ' * charsPerPixel) + \
+ line[x + charsPerPixel:]
+
+ def skip_same_char(start, increment):
+ """Skip characters which match pic_at(start)"""
+ char = pic_at(start)
+ hunt = start
+ while pic_at(hunt) == char:
+ hunt += increment
+ return hunt
+
+ def find_size(start):
+ """Find the size of a rectangle with top left hand corner at
+ start consisting of (at least) a -. shaped corner describing
+ the top right corner of a rectangle of the same char"""
+ char = pic_at(start)
+ hunt_x = skip_same_char(start, Point(1,0))
+ hunt_y = skip_same_char(start, Point(0,1))
+ off_bottom_right = (hunt_x * Point(1,0)) + (hunt_y * Point(0,1))
+ return off_bottom_right - start
+
+ def point_return(point):
+ """Carriage return, line feed"""
+ return Point(0, point.y + 1)
+
+ def find_arrow(start):
+ """Find a simple 1-char wide arrow"""
+
+ def body(endChar, contChar, direc):
+ arrow_point = start
+ arrow_point += Point(0, 1)
+ clear_pic_at(start)
+ while pic_at(arrow_point) == contChar:
+ clear_pic_at(arrow_point)
+ arrow_point += Point(0, 1)
+
+ if pic_at(arrow_point) == endChar:
+ clear_pic_at(arrow_point)
+ self.add_blob(blobs.Arrow('_', start + offset,
+ direc = direc,
+ size = (Point(1, 1) + arrow_point - start)))
+ else:
+ print 'Bad arrow', start
+
+ char = pic_at(start)
+ if char == '-\\':
+ body('-/', ' :', 'right')
+ elif char == '/-':
+ body('\\-', ': ', 'left')
+
+ blank_chars = [' ', ' :', ': ']
+
+ # Traverse the picture left to right, top to bottom to find blobs
+ seen_dict = {}
+ point = Point(0,0)
+ while pic_at(point) is not None:
+ while pic_at(point) is not None:
+ char = pic_at(point)
+ if char == '->':
+ self.add_blob(blobs.Arrow('_', point + offset,
+ direc = 'right'))
+ elif char == '<-':
+ self.add_blob(blobs.Arrow('_', point + offset,
+ direc = 'left'))
+ elif char == '-\\' or char == '/-':
+ find_arrow(point)
+ elif char in blank_chars:
+ pass
+ else:
+ if char not in seen_dict:
+ size = find_size(point)
+ topLeft = point + offset
+ if char not in nameDict:
+ # Unnamed blobs
+ self.add_blob(blobs.Block(char,
+ nameDict.get(char, '_'),
+ topLeft, size = size))
+ else:
+ # Named blobs, set visual info.
+ blob = nameDict[char]
+ blob.size = size
+ blob.topLeft = topLeft
+ self.add_blob(blob)
+ seen_dict[char] = True
+ point = skip_same_char(point, Point(1,0))
+ point = point_return(point)
+
+ def load_picture(self, filename):
+ """Load a picture file into the model"""
+ def parse_blob_description(char, unit, macros, pairsList):
+ # Parse the name value pairs in a blob-describing line
+ def expand_macros(pairs, newPairs):
+ # Recursively expand macros
+ for name, value in newPairs:
+ if name in macros:
+ expand_macros(pairs, macros[name])
+ else:
+ pairs[name] = value
+ return pairs
+
+ pairs = expand_macros({}, pairsList)
+
+ ret = None
+
+ typ = pairs.get('type', 'block')
+ colour = colours.name_to_colour(pairs.get('colour', 'black'))
+
+ if typ == 'key':
+ ret = blobs.Key(char, unit, Point(0,0), colour)
+ elif typ == 'block':
+ ret = blobs.Block(char, unit, Point(0,0), colour)
+ else:
+ print "Bad picture blog type:", typ
+
+ if 'hideId' in pairs:
+ hide = pairs['hideId']
+ ret.dataSelect.ids -= set(hide)
+
+ if typ == 'block':
+ ret.displayName = pairs.get('name', unit)
+ ret.nameLoc = pairs.get('nameLoc', 'top')
+ ret.shape = pairs.get('shape', 'box')
+ ret.stripDir = pairs.get('stripDir', 'horiz')
+ ret.stripOrd = pairs.get('stripOrd', 'LR')
+ ret.blankStrips = int(pairs.get('blankStrips', '0'))
+ ret.shorten = int(pairs.get('shorten', '0'))
+
+ if 'decoder' in pairs:
+ decoderName = pairs['decoder']
+ dataElement = pairs.get('dataElement', decoderName)
+
+ decoder = find_colour_decoder(ret.blankStrips,
+ decoderName, dataElement, pairs)
+ if decoder is not None:
+ ret.visualDecoder = decoder
+ else:
+ print 'Bad visualDecoder requested:', decoderName
+
+ if 'border' in pairs:
+ border = pairs['border']
+ if border == 'thin':
+ ret.border = 0.2
+ elif border == 'mid':
+ ret.border = 0.5
+ else:
+ ret.border = 1.0
+ elif typ == 'key':
+ ret.colours = pairs.get('colours', ret.colours)
+
+ return ret
+
+ def line_is_comment(line):
+ """Returns true if a line starts with #, returns False
+ for lines which are None"""
+ return line is not None \
+ and re.match('^\s*#', line) is not None
+
+ def get_line(f):
+ """Get a line from file f extending that line if it ends in
+ '\' and dropping lines that start with '#'s"""
+ ret = f.readline()
+
+ # Discard comment lines
+ while line_is_comment(ret):
+ ret = f.readline()
+
+ if ret is not None:
+ extend_match = re.match('^(.*)\\\\$', ret)
+
+ while extend_match is not None:
+ new_line = f.readline()
+
+ if new_line is not None and not line_is_comment(new_line):
+ line_wo_backslash, = extend_match.groups()
+ ret = line_wo_backslash + new_line
+ extend_match = re.match('^(.*)\\\\$', ret)
+ else:
+ extend_match = None
+
+ return ret
+
+ # Macros are recursively expanded into name=value pairs
+ macros = {}
+
+ if not os.access(filename, os.R_OK):
+ print 'Can\'t open file', filename
+ exit(1)
+ else:
+ print 'Opening file', filename
+
+ f = open(filename)
+ l = get_line(f)
+ picture = []
+ blob_char_dict = {}
+
+ self.unitEvents = {}
+ self.clear_events()
+
+ # Actually parse the file
+ in_picture = False
+ while l:
+ l = parse.remove_trailing_ws(l)
+ l = re.sub('#.*', '', l)
+
+ if re.match("^\s*$", l) is not None:
+ pass
+ elif l == '<<<':
+ in_picture = True
+ elif l == '>>>':
+ in_picture = False
+ elif in_picture:
+ picture.append(re.sub('\s*$', '', l))
+ else:
+ line_match = re.match(
+ '^([a-zA-Z0-9][a-zA-Z0-9]):\s+([\w.]+)\s*(.*)', l)
+ macro_match = re.match('macro\s+(\w+):(.*)', l)
+
+ if macro_match is not None:
+ name, defn = macro_match.groups()
+ macros[name] = parse.parse_pairs_list(defn)
+ elif line_match is not None:
+ char, unit, pairs = line_match.groups()
+ blob = parse_blob_description(char, unit, macros,
+ parse.parse_pairs_list(pairs))
+ blob_char_dict[char] = blob
+ # Setup the events structure
+ self.unitEvents[unit] = []
+ else:
+ print 'Problem with Blob line:', l
+
+ l = get_line(f)
+
+ self.blobs = []
+ self.add_blob_picture(Point(0,1), picture, blob_char_dict)
diff --git a/util/minorview/parse.py b/util/minorview/parse.py
new file mode 100644
index 000000000..352371428
--- /dev/null
+++ b/util/minorview/parse.py
@@ -0,0 +1,109 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+import re
+
+def list_parser(names):
+ """Parse a list of elements, some of which might be one-level sublists
+ within parentheses, into a a list of lists of those elements. For
+ example: list_parser('(a,b),c') -> [['a', 'b'], 'c']"""
+ elems = re.split(',', names)
+ ret = []
+ accum = []
+ for elem in elems:
+ if re.search('^\((.*)\)$', elem):
+ accum.append(re.sub('^\((.*)\)', '\\1', elem))
+ ret.append(accum)
+ accum = []
+ elif re.search('^\(', elem):
+ accum.append(re.sub('^\(', '', elem))
+ elif re.search('\)$', elem):
+ accum.append(re.sub('\)$', '', elem))
+ ret.append(accum)
+ accum = []
+ elif len(accum) != 0:
+ accum.append(elem)
+ else:
+ ret.append([elem])
+
+ if len(accum) > 0:
+ print 'Non matching brackets in', names
+
+ return ret
+
+def map2(f, ls):
+ """map to a depth of 2. That is, given a list of lists, apply
+ f to those innermost elements """
+ return map(lambda l: map(f, l), ls)
+
+def remove_trailing_ws(line):
+ return re.sub('\s*$', '', line)
+
+def remove_leading_and_trailing_ws(line):
+ return re.sub('\s*$', '', re.sub('^\s*', '', line))
+
+def parse_pairs_list(pairString):
+ """parse a string like 'name=value name2=value2' into a
+ list of pairs of ('name', 'value') ..."""
+ ret = []
+ pairs = re.finditer('(\w+)(=("[^"]*"|[^\s]*))?', pairString)
+ for pair in pairs:
+ name, rest, value = pair.groups()
+ if value is not None:
+ value = re.sub('^"(.*)"$', '\\1', value)
+ ret.append((name, value))
+ else:
+ ret.append((name, ''))
+ return ret
+
+def parse_indexed_list(string):
+ """parse a string of the form "(index,value),(index,value)..."
+ into a list of index, value pairs"""
+
+ ret = []
+ pairs = list_parser(string)
+ for pair in pairs:
+ if len(pair) == 2:
+ index, value = pair
+ ret.append((int(index), value))
+
+ return ret
+
+def parse_pairs(pairString):
+ """parse a string like 'name=value name2=value2' into a
+ dictionary of {'name': 'value', 'name2': 'value2'} """
+ return dict(parse_pairs_list(pairString))
diff --git a/util/minorview/point.py b/util/minorview/point.py
new file mode 100644
index 000000000..75097e2c2
--- /dev/null
+++ b/util/minorview/point.py
@@ -0,0 +1,78 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+class Point(object):
+ """2D point coordinates/size type"""
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ def __add__(self, rhs):
+ return Point(self.x + rhs.x, self.y + rhs.y)
+
+ def __sub__(self, rhs):
+ return Point(self.x - rhs.x, self.y - rhs.y)
+
+ def __mul__(self, rhs):
+ return Point(self.x * rhs.x, self.y * rhs.y)
+
+ def __div__(self, rhs):
+ return Point(float(self.x) / rhs.x, float(self.y) / rhs.y)
+
+ def scale(self, factor):
+ return Point(self.x * factor, self.y * factor)
+
+ def to_pair(self):
+ return (self.x, self.y)
+
+ def __str__(self):
+ return "Point(%f,%f)" % (self.x, self.y)
+
+ def __repr__(self):
+ return "Point(%f,%f)" % (self.x, self.y)
+
+ def is_within_box(self, box):
+ """Is this point inside the (centre, size) box box"""
+ centre, size = box
+ half_size = size.scale(0.5)
+ top_left = centre - half_size
+ bottom_right = centre + half_size
+ return (top_left.x < self.x and
+ top_left.y < self.y and
+ bottom_right.x > self.x and
+ bottom_right.y > self.y)
+
diff --git a/util/minorview/view.py b/util/minorview/view.py
new file mode 100644
index 000000000..8a9aaffea
--- /dev/null
+++ b/util/minorview/view.py
@@ -0,0 +1,524 @@
+# Copyright (c) 2013 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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: Andrew Bardsley
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+import cairo
+import re
+
+from point import Point
+import parse
+import colours
+import model
+from model import Id, BlobModel, BlobDataSelect, special_state_chars
+import blobs
+
+class BlobView(object):
+ """The canvas view of the pipeline"""
+ def __init__(self, model):
+ # A unit blob will appear at size blobSize inside a space of
+ # size pitch.
+ self.blobSize = Point(45.0, 45.0)
+ self.pitch = Point(60.0, 60.0)
+ self.origin = Point(50.0, 50.0)
+ # Some common line definitions to cut down on arbitrary
+ # set_line_widths
+ self.thickLineWidth = 10.0
+ self.thinLineWidth = 4.0
+ self.midLineWidth = 6.0
+ # The scale from the units of pitch to device units (nominally
+ # pixels for 1.0 to 1.0
+ self.masterScale = Point(1.0,1.0)
+ self.model = model
+ self.fillColour = colours.emptySlotColour
+ self.timeIndex = 0
+ self.time = 0
+ self.positions = []
+ self.controlbar = None
+ # The sequence number selector state
+ self.dataSelect = BlobDataSelect()
+ # Offset of this view's time from self.time used for miniviews
+ # This is actually an offset of the index into the array of times
+ # seen in the event file)
+ self.timeOffset = 0
+ # Maximum view size for initial window mapping
+ self.initialHeight = 600.0
+
+ # Overlays are speech bubbles explaining blob data
+ self.overlays = []
+
+ self.da = gtk.DrawingArea()
+ def draw(arg1, arg2):
+ self.redraw()
+ self.da.connect('expose_event', draw)
+
+ # Handy offsets from the blob size
+ self.blobIndent = (self.pitch - self.blobSize).scale(0.5)
+ self.blobIndentFactor = self.blobIndent / self.pitch
+
+ def add_control_bar(self, controlbar):
+ """Add a BlobController to this view"""
+ self.controlbar = controlbar
+
+ def draw_to_png(self, filename):
+ """Draw the view to a PNG file"""
+ surface = cairo.ImageSurface(
+ cairo.FORMAT_ARGB32,
+ self.da.get_allocation().width,
+ self.da.get_allocation().height)
+ cr = gtk.gdk.CairoContext(cairo.Context(surface))
+ self.draw_to_cr(cr)
+ surface.write_to_png(filename)
+
+ def draw_to_cr(self, cr):
+ """Draw to a given CairoContext"""
+ cr.set_source_color(colours.backgroundColour)
+ cr.set_line_width(self.thickLineWidth)
+ cr.paint()
+ cr.save()
+ cr.scale(*self.masterScale.to_pair())
+ cr.translate(*self.origin.to_pair())
+
+ positions = [] # {}
+
+ # Draw each blob
+ for blob in self.model.blobs:
+ blob_event = self.model.find_unit_event_by_time(
+ blob.unit, self.time)
+
+ cr.save()
+ pos = blob.render(cr, self, blob_event, self.dataSelect,
+ self.time)
+ cr.restore()
+ if pos is not None:
+ (centre, size) = pos
+ positions.append((blob, centre, size))
+
+ # Draw all the overlays over the top
+ for overlay in self.overlays:
+ overlay.show(cr)
+
+ cr.restore()
+
+ return positions
+
+ def redraw(self):
+ """Redraw the whole view"""
+ buffer = cairo.ImageSurface(
+ cairo.FORMAT_ARGB32,
+ self.da.get_allocation().width,
+ self.da.get_allocation().height)
+
+ cr = gtk.gdk.CairoContext(cairo.Context(buffer))
+ positions = self.draw_to_cr(cr)
+
+ # Assume that blobs are in order for depth so we want to
+ # hit the frontmost blob first if we search by position
+ positions.reverse()
+ self.positions = positions
+
+ # Paint the drawn buffer onto the DrawingArea
+ dacr = self.da.window.cairo_create()
+ dacr.set_source_surface(buffer, 0.0, 0.0)
+ dacr.paint()
+
+ buffer.finish()
+
+ def set_time_index(self, time):
+ """Set the time index for the view. A time index is an index into
+ the model's times array of seen event times"""
+ self.timeIndex = time + self.timeOffset
+ if len(self.model.times) != 0:
+ if self.timeIndex >= len(self.model.times):
+ self.time = self.model.times[len(self.model.times) - 1]
+ else:
+ self.time = self.model.times[self.timeIndex]
+ else:
+ self.time = 0
+
+ def get_pic_size(self):
+ """Return the size of ASCII-art picture of the pipeline scaled by
+ the blob pitch"""
+ return (self.origin + self.pitch *
+ (self.model.picSize + Point(1.0,1.0)))
+
+ def set_da_size(self):
+ """Set the DrawingArea size after scaling"""
+ self.da.set_size_request(10 , int(self.initialHeight))
+
+class BlobController(object):
+ """The controller bar for the viewer"""
+ def __init__(self, model, view,
+ defaultEventFile="", defaultPictureFile=""):
+ self.model = model
+ self.view = view
+ self.playTimer = None
+ self.filenameEntry = gtk.Entry()
+ self.filenameEntry.set_text(defaultEventFile)
+ self.pictureEntry = gtk.Entry()
+ self.pictureEntry.set_text(defaultPictureFile)
+ self.timeEntry = None
+ self.defaultEventFile = defaultEventFile
+ self.startTime = None
+ self.endTime = None
+
+ self.otherViews = []
+
+ def make_bar(elems):
+ box = gtk.HBox(homogeneous=False, spacing=2)
+ box.set_border_width(2)
+ for widget, signal, handler in elems:
+ if signal is not None:
+ widget.connect(signal, handler)
+ box.pack_start(widget, False, True, 0)
+ return box
+
+ self.timeEntry = gtk.Entry()
+
+ t = gtk.ToggleButton('T')
+ t.set_active(False)
+ s = gtk.ToggleButton('S')
+ s.set_active(True)
+ p = gtk.ToggleButton('P')
+ p.set_active(True)
+ l = gtk.ToggleButton('L')
+ l.set_active(True)
+ f = gtk.ToggleButton('F')
+ f.set_active(True)
+ e = gtk.ToggleButton('E')
+ e.set_active(True)
+
+ # Should really generate this from above
+ self.view.dataSelect.ids = set("SPLFE")
+
+ self.bar = gtk.VBox()
+ self.bar.set_homogeneous(False)
+
+ row1 = make_bar([
+ (gtk.Button('Start'), 'clicked', self.time_start),
+ (gtk.Button('End'), 'clicked', self.time_end),
+ (gtk.Button('Back'), 'clicked', self.time_back),
+ (gtk.Button('Forward'), 'clicked', self.time_forward),
+ (gtk.Button('Play'), 'clicked', self.time_play),
+ (gtk.Button('Stop'), 'clicked', self.time_stop),
+ (self.timeEntry, 'activate', self.time_set),
+ (gtk.Label('Visible ids:'), None, None),
+ (t, 'clicked', self.toggle_id('T')),
+ (gtk.Label('/'), None, None),
+ (s, 'clicked', self.toggle_id('S')),
+ (gtk.Label('.'), None, None),
+ (p, 'clicked', self.toggle_id('P')),
+ (gtk.Label('/'), None, None),
+ (l, 'clicked', self.toggle_id('L')),
+ (gtk.Label('/'), None, None),
+ (f, 'clicked', self.toggle_id('F')),
+ (gtk.Label('.'), None, None),
+ (e, 'clicked', self.toggle_id('E')),
+ (self.filenameEntry, 'activate', self.load_events),
+ (gtk.Button('Reload'), 'clicked', self.load_events)
+ ])
+
+ self.bar.pack_start(row1, False, True, 0)
+ self.set_time_index(0)
+
+ def toggle_id(self, id):
+ """One of the sequence number selector buttons has been toggled"""
+ def toggle(button):
+ if button.get_active():
+ self.view.dataSelect.ids.add(id)
+ else:
+ self.view.dataSelect.ids.discard(id)
+
+ # Always leave one thing visible
+ if len(self.view.dataSelect.ids) == 0:
+ self.view.dataSelect.ids.add(id)
+ button.set_active(True)
+ self.view.redraw()
+ return toggle
+
+ def set_time_index(self, time):
+ """Set the time index in the view"""
+ self.view.set_time_index(time)
+
+ for view in self.otherViews:
+ view.set_time_index(time)
+ view.redraw()
+
+ self.timeEntry.set_text(str(self.view.time))
+
+ def time_start(self, button):
+ """Start pressed"""
+ self.set_time_index(0)
+ self.view.redraw()
+
+ def time_end(self, button):
+ """End pressed"""
+ self.set_time_index(len(self.model.times) - 1)
+ self.view.redraw()
+
+ def time_forward(self, button):
+ """Step forward pressed"""
+ self.set_time_index(min(self.view.timeIndex + 1,
+ len(self.model.times) - 1))
+ self.view.redraw()
+ gtk.gdk.flush()
+
+ def time_back(self, button):
+ """Step back pressed"""
+ self.set_time_index(max(self.view.timeIndex - 1, 0))
+ self.view.redraw()
+
+ def time_set(self, entry):
+ """Time dialogue changed. Need to find a suitable time
+ <= the entry's time"""
+ newTime = self.model.find_time_index(int(entry.get_text()))
+ self.set_time_index(newTime)
+ self.view.redraw()
+
+ def time_step(self):
+ """Time step while playing"""
+ if not self.playTimer \
+ or self.view.timeIndex == len(self.model.times) - 1:
+ self.time_stop(None)
+ return False
+ else:
+ self.time_forward(None)
+ return True
+
+ def time_play(self, play):
+ """Automatically advance time every 100 ms"""
+ if not self.playTimer:
+ self.playTimer = gobject.timeout_add(100, self.time_step)
+
+ def time_stop(self, play):
+ """Stop play pressed"""
+ if self.playTimer:
+ gobject.source_remove(self.playTimer)
+ self.playTimer = None
+
+ def load_events(self, button):
+ """Reload events file"""
+ self.model.load_events(self.filenameEntry.get_text(),
+ startTime=self.startTime, endTime=self.endTime)
+ self.set_time_index(min(len(self.model.times) - 1,
+ self.view.timeIndex))
+ self.view.redraw()
+
+class Overlay(object):
+ """An Overlay is a speech bubble explaining the data in a blob"""
+ def __init__(self, model, view, point, blob):
+ self.model = model
+ self.view = view
+ self.point = point
+ self.blob = blob
+
+ def find_event(self):
+ """Find the event for a changing time and a fixed blob"""
+ return self.model.find_unit_event_by_time(self.blob.unit,
+ self.view.time)
+
+ def show(self, cr):
+ """Draw the overlay"""
+ event = self.find_event()
+
+ if event is None:
+ return
+
+ insts = event.find_ided_objects(self.model, self.blob.picChar,
+ False)
+
+ cr.set_line_width(self.view.thinLineWidth)
+ cr.translate(*(Point(0.0,0.0) - self.view.origin).to_pair())
+ cr.scale(*(Point(1.0,1.0) / self.view.masterScale).to_pair())
+
+ # Get formatted data from the insts to format into a table
+ lines = list(inst.table_line() for inst in insts)
+
+ text_size = 10.0
+ cr.set_font_size(text_size)
+
+ def text_width(str):
+ xb, yb, width, height, dx, dy = cr.text_extents(str)
+ return width
+
+ # Find the maximum number of columns and the widths of each column
+ num_columns = 0
+ for line in lines:
+ num_columns = max(num_columns, len(line))
+
+ widths = [0] * num_columns
+ for line in lines:
+ for i in xrange(0, len(line)):
+ widths[i] = max(widths[i], text_width(line[i]))
+
+ # Calculate the size of the speech bubble
+ column_gap = 1 * text_size
+ id_width = 6 * text_size
+ total_width = sum(widths) + id_width + column_gap * (num_columns + 1)
+ gap_step = Point(1.0, 0.0).scale(column_gap)
+
+ text_point = self.point
+ text_step = Point(0.0, text_size)
+
+ size = Point(total_width, text_size * len(insts))
+
+ # Draw the speech bubble
+ blobs.speech_bubble(cr, self.point, size, text_size)
+ cr.set_source_color(colours.backgroundColour)
+ cr.fill_preserve()
+ cr.set_source_color(colours.black)
+ cr.stroke()
+
+ text_point += Point(1.0,1.0).scale(2.0 * text_size)
+
+ id_size = Point(id_width, text_size)
+
+ # Draw the rows in the table
+ for i in xrange(0, len(insts)):
+ row_point = text_point
+ inst = insts[i]
+ line = lines[i]
+ blobs.striped_box(cr, row_point + id_size.scale(0.5),
+ id_size, inst.id.to_striped_block(self.view.dataSelect))
+ cr.set_source_color(colours.black)
+
+ row_point += Point(1.0, 0.0).scale(id_width)
+ row_point += text_step
+ # Draw the columns of each row
+ for j in xrange(0, len(line)):
+ row_point += gap_step
+ cr.move_to(*row_point.to_pair())
+ cr.show_text(line[j])
+ row_point += Point(1.0, 0.0).scale(widths[j])
+
+ text_point += text_step
+
+class BlobWindow(object):
+ """The top-level window and its mouse control"""
+ def __init__(self, model, view, controller):
+ self.model = model
+ self.view = view
+ self.controller = controller
+ self.controlbar = None
+ self.window = None
+ self.miniViewCount = 0
+
+ def add_control_bar(self, controlbar):
+ self.controlbar = controlbar
+
+ def show_window(self):
+ self.window = gtk.Window()
+
+ self.vbox = gtk.VBox()
+ self.vbox.set_homogeneous(False)
+ if self.controlbar:
+ self.vbox.pack_start(self.controlbar, False, True, 0)
+ self.vbox.add(self.view.da)
+
+ if self.miniViewCount > 0:
+ self.miniViews = []
+ self.miniViewHBox = gtk.HBox(homogeneous=True, spacing=2)
+
+ # Draw mini views
+ for i in xrange(1, self.miniViewCount + 1):
+ miniView = BlobView(self.model)
+ miniView.set_time_index(0)
+ miniView.masterScale = Point(0.1, 0.1)
+ miniView.set_da_size()
+ miniView.timeOffset = i + 1
+ self.miniViews.append(miniView)
+ self.miniViewHBox.pack_start(miniView.da, False, True, 0)
+
+ self.controller.otherViews = self.miniViews
+ self.vbox.add(self.miniViewHBox)
+
+ self.window.add(self.vbox)
+
+ def show_event(picChar, event):
+ print '**** Comments for', event.unit, \
+ 'at time', self.view.time
+ for name, value in event.pairs.iteritems():
+ print name, '=', value
+ for comment in event.comments:
+ print comment
+ if picChar in event.visuals:
+ # blocks = event.visuals[picChar].elems()
+ print '**** Colour data'
+ objs = event.find_ided_objects(self.model, picChar, True)
+ for obj in objs:
+ print ' '.join(obj.table_line())
+
+ def clicked_da(da, b):
+ point = Point(b.x, b.y)
+
+ overlay = None
+ for blob, centre, size in self.view.positions:
+ if point.is_within_box((centre, size)):
+ event = self.model.find_unit_event_by_time(blob.unit,
+ self.view.time)
+ if event is not None:
+ if overlay is None:
+ overlay = Overlay(self.model, self.view, point,
+ blob)
+ show_event(blob.picChar, event)
+ if overlay is not None:
+ self.view.overlays = [overlay]
+ else:
+ self.view.overlays = []
+
+ self.view.redraw()
+
+ # Set initial size and event callbacks
+ self.view.set_da_size()
+ self.view.da.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self.view.da.connect('button-press-event', clicked_da)
+ self.window.connect('destroy', lambda(widget): gtk.main_quit())
+
+ def resize(window, event):
+ """Resize DrawingArea to match new window size"""
+ size = Point(float(event.width), float(event.height))
+ proportion = size / self.view.get_pic_size()
+ # Preserve aspect ratio
+ daScale = min(proportion.x, proportion.y)
+ self.view.masterScale = Point(daScale, daScale)
+ self.view.overlays = []
+
+ self.view.da.connect('configure-event', resize)
+
+ self.window.show_all()