summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/arch/arm/ArmISA.py4
-rw-r--r--src/arch/arm/ArmPMU.py83
-rw-r--r--src/arch/arm/SConscript4
-rw-r--r--src/arch/arm/isa.cc67
-rw-r--r--src/arch/arm/isa.hh8
-rw-r--r--src/arch/arm/isa_device.cc62
-rw-r--r--src/arch/arm/isa_device.hh99
-rw-r--r--src/arch/arm/pmu.cc559
-rw-r--r--src/arch/arm/pmu.hh495
9 files changed, 1353 insertions, 28 deletions
diff --git a/src/arch/arm/ArmISA.py b/src/arch/arm/ArmISA.py
index eaec92f4d..f5c56cfd5 100644
--- a/src/arch/arm/ArmISA.py
+++ b/src/arch/arm/ArmISA.py
@@ -40,6 +40,8 @@ from m5.params import *
from m5.proxy import *
from m5.SimObject import SimObject
+from ArmPMU import ArmPMU
+
class ArmISA(SimObject):
type = 'ArmISA'
cxx_class = 'ArmISA::ISA'
@@ -47,6 +49,8 @@ class ArmISA(SimObject):
system = Param.System(Parent.any, "System this ISA object belongs to")
+ pmu = Param.ArmPMU(NULL, "Performance Monitoring Unit")
+
midr = Param.UInt32(0x410fc0f0, "MIDR value")
# See section B4.1.93 - B4.1.94 of the ARM ARM
diff --git a/src/arch/arm/ArmPMU.py b/src/arch/arm/ArmPMU.py
new file mode 100644
index 000000000..dc393be69
--- /dev/null
+++ b/src/arch/arm/ArmPMU.py
@@ -0,0 +1,83 @@
+# -*- mode:python -*-
+# Copyright (c) 2009-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: Matt Horsnell
+# Andreas Sandberg
+
+from m5.defines import buildEnv
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.params import isNullPointer
+from m5.proxy import *
+
+class ArmPMU(SimObject):
+ type = 'ArmPMU'
+ cxx_class = 'ArmISA::PMU'
+ cxx_header = 'arch/arm/pmu.hh'
+
+ @classmethod
+ def export_methods(cls, code):
+ code('''
+ void addEventProbe(unsigned int id,
+ SimObject *obj, const char *name);
+''')
+
+ # To prevent cycles in the configuration hierarchy, we don't keep
+ # a list of supported events as a configuration param. Instead, we
+ # keep them in a local list and register them using the
+ # addEventProbe interface when other SimObjects register their
+ # probe listeners.
+ _deferred_event_types = []
+ # Override the normal SimObject::regProbeListeners method and
+ # register deferred event handlers.
+ def regProbeListeners(self):
+ for event_id, obj, name in self._deferred_event_types:
+ self.getCCObject().addEventProbe(event_id, obj.getCCObject(), name)
+
+ self.getCCObject().regProbeListeners()
+
+ def addEventProbe(self, event_id, obj, *args):
+ """Add a probe-based event to the PMU if obj is not None."""
+
+ if obj is None:
+ return
+
+ for name in args:
+ self._deferred_event_types.append((event_id, obj, name))
+
+ platform = Param.Platform(Parent.any, "Platform this device is part of.")
+ eventCounters = Param.Int(31, "Number of supported PMU counters")
+ pmuInterrupt = Param.Int(68, "PMU GIC interrupt number")
diff --git a/src/arch/arm/SConscript b/src/arch/arm/SConscript
index 78c0b01e9..92c46304c 100644
--- a/src/arch/arm/SConscript
+++ b/src/arch/arm/SConscript
@@ -62,11 +62,13 @@ if env['TARGET_ISA'] == 'arm':
Source('insts/fplib.cc')
Source('interrupts.cc')
Source('isa.cc')
+ Source('isa_device.cc')
Source('linux/linux.cc')
Source('linux/process.cc')
Source('linux/system.cc')
Source('miscregs.cc')
Source('nativetrace.cc')
+ Source('pmu.cc')
Source('process.cc')
Source('remote_gdb.cc')
Source('stacktrace.cc')
@@ -83,10 +85,12 @@ if env['TARGET_ISA'] == 'arm':
SimObject('ArmNativeTrace.py')
SimObject('ArmSystem.py')
SimObject('ArmTLB.py')
+ SimObject('ArmPMU.py')
DebugFlag('Arm')
DebugFlag('Decoder', "Instructions returned by the predecoder")
DebugFlag('Faults', "Trace Exceptions, interrupts, svc/swi")
+ DebugFlag('PMUVerbose', "Performance Monitor")
DebugFlag('TLBVerbose')
# Add files generated by the ISA description.
diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc
index e76ff452d..c2250e38a 100644
--- a/src/arch/arm/isa.cc
+++ b/src/arch/arm/isa.cc
@@ -39,8 +39,10 @@
*/
#include "arch/arm/isa.hh"
+#include "arch/arm/pmu.hh"
#include "arch/arm/system.hh"
#include "cpu/checker/cpu.hh"
+#include "cpu/base.hh"
#include "debug/Arm.hh"
#include "debug/MiscRegs.hh"
#include "params/ArmISA.hh"
@@ -122,12 +124,21 @@ const struct ISA::MiscRegInitializerEntry
ISA::ISA(Params *p)
- : SimObject(p), system(NULL), lookUpMiscReg(NUM_MISCREGS, {0,0})
+ : SimObject(p),
+ system(NULL),
+ pmu(p->pmu),
+ lookUpMiscReg(NUM_MISCREGS, {0,0})
{
SCTLR sctlr;
sctlr = 0;
miscRegs[MISCREG_SCTLR_RST] = sctlr;
+ // Hook up a dummy device if we haven't been configured with a
+ // real PMU. By using a dummy device, we don't need to check that
+ // the PMU exist every time we try to access a PMU register.
+ if (!pmu)
+ pmu = &dummyDevice;
+
system = dynamic_cast<ArmSystem *>(p->system);
DPRINTFN("ISA system set to: %p %p\n", system, p->system);
@@ -356,7 +367,10 @@ ISA::clear64(const ArmISAParams *p)
// Initialize configurable id registers
miscRegs[MISCREG_ID_AA64AFR0_EL1] = p->id_aa64afr0_el1;
miscRegs[MISCREG_ID_AA64AFR1_EL1] = p->id_aa64afr1_el1;
- miscRegs[MISCREG_ID_AA64DFR0_EL1] = p->id_aa64dfr0_el1;
+ miscRegs[MISCREG_ID_AA64DFR0_EL1] =
+ (p->id_aa64dfr0_el1 & 0xfffffffffffff0ffULL) |
+ (p->pmu ? 0x0000000000000100ULL : 0); // Enable PMUv3
+
miscRegs[MISCREG_ID_AA64DFR1_EL1] = p->id_aa64dfr1_el1;
miscRegs[MISCREG_ID_AA64ISAR0_EL1] = p->id_aa64isar0_el1;
miscRegs[MISCREG_ID_AA64ISAR1_EL1] = p->id_aa64isar1_el1;
@@ -365,6 +379,11 @@ ISA::clear64(const ArmISAParams *p)
miscRegs[MISCREG_ID_AA64PFR0_EL1] = p->id_aa64pfr0_el1;
miscRegs[MISCREG_ID_AA64PFR1_EL1] = p->id_aa64pfr1_el1;
+ miscRegs[MISCREG_ID_DFR0_EL1] =
+ (p->pmu ? 0x03000000ULL : 0); // Enable PMUv3
+
+ miscRegs[MISCREG_ID_DFR0] = miscRegs[MISCREG_ID_DFR0_EL1];
+
// Enforce consistency with system-level settings...
// EL3
@@ -491,7 +510,6 @@ ISA::readMiscReg(int misc_reg, ThreadContext *tc)
// top bit defined as RES1
return readMiscRegNoEffect(misc_reg) | 0x80000000;
case MISCREG_ID_AFR0: // not implemented, so alias MIDR
- case MISCREG_ID_DFR0: // not implemented, so alias MIDR
case MISCREG_REVIDR: // not implemented, so alias MIDR
case MISCREG_MIDR:
cpsr = readMiscRegNoEffect(MISCREG_CPSR);
@@ -549,12 +567,13 @@ ISA::readMiscReg(int misc_reg, ThreadContext *tc)
case MISCREG_ACTLR:
warn("Not doing anything for miscreg ACTLR\n");
break;
- case MISCREG_PMCR:
- case MISCREG_PMCCNTR:
- case MISCREG_PMSELR:
- warn("Not doing anything for read to miscreg %s\n",
- miscRegName[misc_reg]);
- break;
+
+ case MISCREG_PMXEVTYPER_PMCCFILTR:
+ case MISCREG_PMINTENSET_EL1 ... MISCREG_PMOVSSET_EL0:
+ case MISCREG_PMEVCNTR0_EL0 ... MISCREG_PMEVTYPER5_EL0:
+ case MISCREG_PMCR ... MISCREG_PMOVSSET:
+ return pmu->readMiscReg(misc_reg);
+
case MISCREG_CPSR_Q:
panic("shouldn't be reading this register seperately\n");
case MISCREG_FPSCR_QC:
@@ -640,9 +659,9 @@ ISA::readMiscReg(int misc_reg, ThreadContext *tc)
}
case MISCREG_DBGDIDR:
/* For now just implement the version number.
- * Return 0 as we don't support debug architecture yet.
+ * ARMv7, v7.1 Debug architecture (0b0101 --> 0x5)
*/
- return 0;
+ return 0x5 << 16;
case MISCREG_DBGDSCRint:
return 0;
case MISCREG_ISR:
@@ -1112,6 +1131,7 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc)
case MISCREG_MIDR:
case MISCREG_ID_PFR0:
case MISCREG_ID_PFR1:
+ case MISCREG_ID_DFR0:
case MISCREG_ID_MMFR0:
case MISCREG_ID_MMFR1:
case MISCREG_ID_MMFR2:
@@ -1443,24 +1463,15 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc)
case MISCREG_ACTLR:
warn("Not doing anything for write of miscreg ACTLR\n");
break;
- case MISCREG_PMCR:
- {
- // Performance counters not implemented. Instead, interpret
- // a reset command to this register to reset the simulator
- // statistics.
- // PMCR_E | PMCR_P | PMCR_C
- const int ResetAndEnableCounters = 0x7;
- if (newVal == ResetAndEnableCounters) {
- inform("Resetting all simobject stats\n");
- Stats::schedStatEvent(false, true);
- break;
- }
- }
- case MISCREG_PMCCNTR:
- case MISCREG_PMSELR:
- warn("Not doing anything for write to miscreg %s\n",
- miscRegName[misc_reg]);
+
+ case MISCREG_PMXEVTYPER_PMCCFILTR:
+ case MISCREG_PMINTENSET_EL1 ... MISCREG_PMOVSSET_EL0:
+ case MISCREG_PMEVCNTR0_EL0 ... MISCREG_PMEVTYPER5_EL0:
+ case MISCREG_PMCR ... MISCREG_PMOVSSET:
+ pmu->setMiscReg(misc_reg, newVal);
break;
+
+
case MISCREG_HSTR: // TJDBX, now redifined to be RES0
{
HSTR hstrMask = 0;
diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh
index 8341cd76b..9f7fa111e 100644
--- a/src/arch/arm/isa.hh
+++ b/src/arch/arm/isa.hh
@@ -43,6 +43,7 @@
#ifndef __ARCH_ARM_ISA_HH__
#define __ARCH_ARM_ISA_HH__
+#include "arch/arm/isa_device.hh"
#include "arch/arm/registers.hh"
#include "arch/arm/system.hh"
#include "arch/arm/tlb.hh"
@@ -52,6 +53,7 @@
#include "sim/sim_object.hh"
struct ArmISAParams;
+struct DummyArmISADeviceParams;
class ThreadContext;
class Checkpoint;
class EventManager;
@@ -131,6 +133,12 @@ namespace ArmISA
// Parent system
ArmSystem *system;
+ /** Dummy device for to handle non-existing ISA devices */
+ DummyISADevice dummyDevice;
+
+ // PMU belonging to this ISA
+ BaseISADevice *pmu;
+
// Cached copies of system-level properties
bool haveSecurity;
bool haveLPAE;
diff --git a/src/arch/arm/isa_device.cc b/src/arch/arm/isa_device.cc
new file mode 100644
index 000000000..6fb58c6e1
--- /dev/null
+++ b/src/arch/arm/isa_device.cc
@@ -0,0 +1,62 @@
+/*
+ * 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: Andreas Sandberg
+ */
+
+#include "arch/arm/isa_device.hh"
+
+#include "base/misc.hh"
+
+namespace ArmISA
+{
+
+void
+DummyISADevice::setMiscReg(int misc_reg, MiscReg val)
+{
+ warn("Ignoring write to miscreg %s\n", miscRegName[misc_reg]);
+}
+
+MiscReg
+DummyISADevice::readMiscReg(int misc_reg)
+{
+ warn("Returning zero for read from miscreg %s\n", miscRegName[misc_reg]);
+
+ return 0;
+}
+
+
+}
diff --git a/src/arch/arm/isa_device.hh b/src/arch/arm/isa_device.hh
new file mode 100644
index 000000000..edf43f1cb
--- /dev/null
+++ b/src/arch/arm/isa_device.hh
@@ -0,0 +1,99 @@
+/*
+ * 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: Andreas Sandberg
+ */
+
+#ifndef __ARCH_ARM_ISA_DEVICE_HH__
+#define __ARCH_ARM_ISA_DEVICE_HH__
+
+#include "arch/arm/registers.hh"
+#include "base/compiler.hh"
+
+namespace ArmISA
+{
+
+/**
+ * Base class for devices that use the MiscReg interfaces.
+ *
+ * This class provides a well-defined interface that the ArmISA class
+ * can use when forwarding MiscReg accesses to a device model (e.g., a
+ * PMU or GIC).
+ */
+class BaseISADevice
+{
+ public:
+ BaseISADevice() {}
+ virtual ~BaseISADevice() {}
+
+ /**
+ * Write to a system register belonging to this device.
+ *
+ * @param misc_reg Register number (see miscregs.hh)
+ * @param val Value to store
+ */
+ virtual void setMiscReg(int misc_reg, MiscReg val) = 0;
+
+ /**
+ * Read a system register belonging to this device.
+ *
+ * @param misc_reg Register number (see miscregs.hh)
+ * @return Register value.
+ */
+ virtual MiscReg readMiscReg(int misc_reg) = 0;
+};
+
+/**
+ * Dummy device that prints a warning when it is accessed.
+ *
+ * This device can be used as a placeholder when a real device model
+ * is not present. For example, the ISA code uses it to avoid having
+ * to check for a PMU in the register access code.
+ */
+class DummyISADevice : public BaseISADevice
+{
+ public:
+ DummyISADevice()
+ : BaseISADevice() {}
+ ~DummyISADevice() {}
+
+ void setMiscReg(int misc_reg, MiscReg val) M5_ATTR_OVERRIDE;
+ MiscReg readMiscReg(int misc_reg) M5_ATTR_OVERRIDE;
+};
+
+}
+
+#endif // __ARCH_ARM_ISA_DEVICE_HH__
diff --git a/src/arch/arm/pmu.cc b/src/arch/arm/pmu.cc
new file mode 100644
index 000000000..bb50ec547
--- /dev/null
+++ b/src/arch/arm/pmu.cc
@@ -0,0 +1,559 @@
+/*
+ * 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: Dam Sunwoo
+ * Matt Horsnell
+ * Andreas Sandberg
+ */
+
+#include "arch/arm/pmu.hh"
+
+#include "base/trace.hh"
+#include "cpu/base.hh"
+#include "debug/Checkpoint.hh"
+#include "debug/PMUVerbose.hh"
+#include "dev/arm/base_gic.hh"
+#include "dev/arm/realview.hh"
+#include "params/ArmPMU.hh"
+
+namespace ArmISA {
+
+const MiscReg PMU::reg_pmcr_wr_mask = 0x39;
+
+PMU::PMU(const ArmPMUParams *p)
+ : SimObject(p), BaseISADevice(),
+ reg_pmcnten(0), reg_pmcr(0),
+ reg_pmselr(0), reg_pminten(0), reg_pmovsr(0),
+ reg_pmceid(0),
+ clock_remainder(0),
+ counters(p->eventCounters),
+ reg_pmcr_conf(0),
+ pmuInterrupt(p->pmuInterrupt),
+ platform(p->platform)
+{
+ DPRINTF(PMUVerbose, "Initializing the PMU.\n");
+
+ if (p->eventCounters > 31) {
+ fatal("The PMU can only accept 31 counters, %d counters requested.\n",
+ p->eventCounters);
+ }
+
+ /* Setup the performance counter ID registers */
+ reg_pmcr_conf.imp = 0x41; // ARM Ltd.
+ reg_pmcr_conf.idcode = 0x00;
+ reg_pmcr_conf.n = p->eventCounters;
+
+ // Setup the hard-coded cycle counter, which is equivalent to
+ // architected counter event type 0x11.
+ cycleCounter.eventId = 0x11;
+}
+
+PMU::~PMU()
+{
+}
+
+void
+PMU::addEventProbe(unsigned int id, SimObject *obj, const char *probe_name)
+{
+ DPRINTF(PMUVerbose, "PMU: Adding event type '0x%x' as probe %s:%s\n",
+ id, obj->name(), probe_name);
+ pmuEventTypes.insert(std::make_pair(id, EventType(obj, probe_name)));
+
+ // Flag the event as available in the PMCEID register if it is an
+ // architected event.
+ if (id < 0x40)
+ reg_pmceid |= (1 << id);
+}
+
+void
+PMU::drainResume()
+{
+ // Re-attach enabled counters after a resume in case they changed.
+ updateAllCounters();
+}
+
+void
+PMU::setMiscReg(int misc_reg, MiscReg val)
+{
+ DPRINTF(PMUVerbose, "setMiscReg(%s, 0x%x)\n",
+ miscRegName[unflattenMiscReg(misc_reg)], val);
+
+ switch (unflattenMiscReg(misc_reg)) {
+ case MISCREG_PMCR_EL0:
+ case MISCREG_PMCR:
+ setControlReg(val);
+ return;
+
+ case MISCREG_PMCNTENSET_EL0:
+ case MISCREG_PMCNTENSET:
+ reg_pmcnten |= val;
+ updateAllCounters();
+ return;
+
+ case MISCREG_PMCNTENCLR_EL0:
+ case MISCREG_PMCNTENCLR:
+ reg_pmcnten &= ~val;
+ updateAllCounters();
+ return;
+
+ case MISCREG_PMOVSCLR_EL0:
+ case MISCREG_PMOVSR:
+ reg_pmovsr &= ~val;
+ return;
+
+ case MISCREG_PMSWINC_EL0:
+ case MISCREG_PMSWINC:
+ for (int i = 0; i < counters.size(); ++i) {
+ CounterState &ctr(getCounter(i));
+ if (ctr.enabled && (val & (1 << i)))
+ ++ctr.value;
+ }
+ break;
+
+ case MISCREG_PMCCNTR_EL0:
+ case MISCREG_PMCCNTR:
+ cycleCounter.value = val;
+ return;
+
+ case MISCREG_PMSELR_EL0:
+ case MISCREG_PMSELR:
+ reg_pmselr = val;
+ return;
+
+ case MISCREG_PMCEID0_EL0:
+ case MISCREG_PMCEID0:
+ case MISCREG_PMCEID1_EL0:
+ case MISCREG_PMCEID1:
+ // Ignore writes
+ return;
+
+ case MISCREG_PMEVTYPER0_EL0...MISCREG_PMEVTYPER5_EL0:
+ setCounterTypeRegister(misc_reg - MISCREG_PMEVCNTR0_EL0, val);
+ return;
+
+ case MISCREG_PMCCFILTR:
+ case MISCREG_PMCCFILTR_EL0:
+ DPRINTF(PMUVerbose, "Setting PMCCFILTR: 0x%x\n", val);
+ setCounterTypeRegister(PMCCNTR, val);
+ return;
+
+ case MISCREG_PMXEVTYPER_PMCCFILTR:
+ case MISCREG_PMXEVTYPER_EL0:
+ case MISCREG_PMXEVTYPER:
+ DPRINTF(PMUVerbose, "Setting counter type: "
+ "[PMSELR: 0x%x, PMSELER.sel: 0x%x, EVTYPER: 0x%x]\n",
+ reg_pmselr, reg_pmselr.sel, val);
+ setCounterTypeRegister(reg_pmselr.sel, val);
+ return;
+
+ case MISCREG_PMEVCNTR0_EL0...MISCREG_PMEVCNTR5_EL0:
+ setCounterValue(misc_reg - MISCREG_PMEVCNTR0_EL0, val);
+ return;
+
+ case MISCREG_PMXEVCNTR_EL0:
+ case MISCREG_PMXEVCNTR:
+ setCounterValue(reg_pmselr.sel, val);
+ return;
+
+ case MISCREG_PMUSERENR_EL0:
+ case MISCREG_PMUSERENR:
+ // TODO
+ break;
+
+ case MISCREG_PMINTENSET_EL1:
+ case MISCREG_PMINTENSET:
+ reg_pminten |= val;
+ return;
+
+ case MISCREG_PMINTENCLR_EL1:
+ case MISCREG_PMINTENCLR:
+ reg_pminten &= ~val;
+ return;
+
+ case MISCREG_PMOVSSET_EL0:
+ case MISCREG_PMOVSSET:
+ reg_pmovsr |= val;
+ return;
+
+ default:
+ panic("Unexpected PMU register: %i\n", miscRegName[misc_reg]);
+ }
+
+ warn("Not doing anything for write to miscreg %s\n",
+ miscRegName[misc_reg]);
+}
+
+MiscReg
+PMU::readMiscReg(int misc_reg)
+{
+ MiscReg val(readMiscRegInt(misc_reg));
+ DPRINTF(PMUVerbose, "readMiscReg(%s): 0x%x\n",
+ miscRegName[unflattenMiscReg(misc_reg)], val);
+ return val;
+}
+
+MiscReg
+PMU::readMiscRegInt(int misc_reg)
+{
+ misc_reg = unflattenMiscReg(misc_reg);
+ switch (misc_reg) {
+ case MISCREG_PMCR_EL0:
+ case MISCREG_PMCR:
+ return reg_pmcr_conf | (reg_pmcr & reg_pmcr_wr_mask);
+
+ case MISCREG_PMCNTENSET_EL0:
+ case MISCREG_PMCNTENCLR_EL0:
+ case MISCREG_PMCNTENSET:
+ case MISCREG_PMCNTENCLR:
+ return reg_pmcnten;
+
+ case MISCREG_PMOVSCLR_EL0:
+ case MISCREG_PMOVSSET_EL0:
+ case MISCREG_PMOVSR: // Overflow Status Register
+ case MISCREG_PMOVSSET:
+ return reg_pmovsr;
+
+ case MISCREG_PMSWINC_EL0:
+ case MISCREG_PMSWINC: // Software Increment Register (RAZ)
+ return 0;
+
+ case MISCREG_PMSELR:
+ return reg_pmselr;
+
+ case MISCREG_PMCEID0_EL0:
+ case MISCREG_PMCEID0: // Common Event ID register
+ return reg_pmceid & 0xFFFFFFFF;
+
+ case MISCREG_PMCEID1_EL0:
+ case MISCREG_PMCEID1: // Common Event ID register
+ return (reg_pmceid >> 32) & 0xFFFFFFFF;
+
+ case MISCREG_PMCCNTR_EL0:
+ return cycleCounter.value;
+
+ case MISCREG_PMCCNTR:
+ return cycleCounter.value & 0xFFFFFFFF;
+
+ case MISCREG_PMEVTYPER0_EL0...MISCREG_PMEVTYPER5_EL0:
+ return getCounterTypeRegister(misc_reg - MISCREG_PMEVTYPER0_EL0);
+
+ case MISCREG_PMCCFILTR:
+ case MISCREG_PMCCFILTR_EL0:
+ return getCounterTypeRegister(PMCCNTR);
+
+ case MISCREG_PMXEVTYPER_PMCCFILTR:
+ case MISCREG_PMXEVTYPER_EL0:
+ case MISCREG_PMXEVTYPER:
+ return getCounterTypeRegister(reg_pmselr.sel);
+
+ case MISCREG_PMEVCNTR0_EL0...MISCREG_PMEVCNTR5_EL0:
+ return getCounterValue(misc_reg - MISCREG_PMEVCNTR0_EL0) & 0xFFFFFFFF;
+
+ case MISCREG_PMXEVCNTR_EL0:
+ case MISCREG_PMXEVCNTR:
+ return getCounterValue(reg_pmselr.sel) & 0xFFFFFFFF;
+
+ case MISCREG_PMUSERENR_EL0:
+ case MISCREG_PMUSERENR:
+ // TODO
+ return 0;
+
+ case MISCREG_PMINTENSET_EL1:
+ case MISCREG_PMINTENCLR_EL1:
+ case MISCREG_PMINTENSET:
+ case MISCREG_PMINTENCLR:
+ return reg_pminten;
+
+ default:
+ panic("Unexpected PMU register: %i\n", miscRegName[misc_reg]);
+ }
+
+ warn("Not doing anything for read from miscreg %s\n",
+ miscRegName[misc_reg]);
+ return 0;
+}
+
+void
+PMU::setControlReg(PMCR_t val)
+{
+ DPRINTF(PMUVerbose, "Set Control Reg 0x%08x.\n", val);
+
+ if (val.p) {
+ DPRINTF(PMUVerbose, "PMU reset all events to zero.\n");
+ resetEventCounts();
+ }
+
+ if (val.c) {
+ DPRINTF(PMUVerbose, "PMU reset cycle counter to zero.\n");
+ cycleCounter.value = 0;
+ }
+
+ // Reset the clock remainder if divide by 64-mode is toggled.
+ if (reg_pmcr.d != val.d)
+ clock_remainder = 0;
+
+ reg_pmcr = val & reg_pmcr_wr_mask;
+ updateAllCounters();
+}
+
+void
+PMU::updateAllCounters()
+{
+ const bool global_enable(reg_pmcr.e);
+
+ for (int i = 0; i < counters.size(); ++i) {
+ CounterState &ctr(counters[i]);
+ const bool enable(global_enable && (reg_pmcnten & (1 << i)));
+ if (ctr.enabled != enable) {
+ ctr.enabled = enable;
+ updateCounter(i, ctr);
+ }
+ }
+
+ const bool ccntr_enable(global_enable && (reg_pmcnten & (1 << PMCCNTR)));
+ if (cycleCounter.enabled != ccntr_enable) {
+ cycleCounter.enabled = ccntr_enable;
+ updateCounter(PMCCNTR, cycleCounter);
+ }
+}
+
+void
+PMU::handleEvent(CounterId id, uint64_t delta)
+{
+ CounterState &ctr(getCounter(id));
+ const bool overflowed(reg_pmovsr & (1 << id));
+
+ // Handle the "count every 64 cycles" mode
+ if (id == PMCCNTR && reg_pmcr.d) {
+ clock_remainder += delta;
+ delta = (clock_remainder >> 6);
+ clock_remainder &= 63;
+ }
+
+ // Add delta and handle (new) overflows
+ if (ctr.add(delta) && !overflowed) {
+ DPRINTF(PMUVerbose, "PMU counter '%i' overflowed.\n", id);
+ reg_pmovsr |= (1 << id);
+ // Deliver a PMU interrupt if interrupt delivery is enabled
+ // for this counter.
+ if (reg_pminten & (1 << id))
+ raiseInterrupt();
+ }
+}
+
+void
+PMU::updateCounter(CounterId id, CounterState &ctr)
+{
+ if (!ctr.enabled) {
+ if (!ctr.listeners.empty()) {
+ DPRINTF(PMUVerbose, "updateCounter(%i): Disabling counter\n", id);
+ ctr.listeners.clear();
+ }
+ } else {
+ DPRINTF(PMUVerbose, "updateCounter(%i): Enable event id 0x%x\n",
+ id, ctr.eventId);
+
+ // Attach all probes belonging to this event
+ auto range(pmuEventTypes.equal_range(ctr.eventId));
+ for (auto it = range.first; it != range.second; ++it) {
+ const EventType &et(it->second);
+
+ DPRINTF(PMUVerbose, "\tProbe: %s:%s\n", et.obj->name(), et.name);
+ ctr.listeners.emplace_back(et.create(*this, id));
+ }
+
+ /* The SW_INCR event type is a special case which doesn't need
+ * any probes since it is controlled by software and the PMU
+ * itself.
+ */
+ if (ctr.listeners.empty() && ctr.eventId != ARCH_EVENT_SW_INCR) {
+ warn("Can't enable PMU counter of type '0x%x': "
+ "No such event type.\n", ctr.eventId);
+ }
+ }
+}
+
+
+void
+PMU::resetEventCounts()
+{
+ for (CounterState &ctr : counters)
+ ctr.value = 0;
+}
+
+void
+PMU::setCounterValue(CounterId id, uint64_t val)
+{
+ if (!isValidCounter(id)) {
+ warn_once("Can't change counter value: Counter %i does not exist.\n",
+ id);
+ return;
+ }
+
+ CounterState &ctr(getCounter(id));
+ ctr.value = val;
+}
+
+PMU::PMEVTYPER_t
+PMU::getCounterTypeRegister(CounterId id) const
+{
+ if (!isValidCounter(id))
+ return 0;
+
+ const CounterState &cs(getCounter(id));
+ PMEVTYPER_t type(0);
+
+ // TODO: Re-create filtering settings from counter state
+ type.evtCount = cs.eventId;
+
+ return type;
+}
+
+void
+PMU::setCounterTypeRegister(CounterId id, PMEVTYPER_t val)
+{
+ DPRINTF(PMUVerbose, "Set Event [%d] = 0x%08x\n", id, val);
+ if (!isValidCounter(id)) {
+ warn_once("Can't change counter type: Counter %i does not exist.\n",
+ id);
+ return;
+ }
+
+ CounterState &ctr(getCounter(id));
+ // TODO: Handle filtering (both for general purpose counters and
+ // the cycle counter)
+
+ // If PMCCNTR Register, do not change event type. PMCCNTR can count
+ // processor cycles only.
+ if (id != PMCCNTR) {
+ ctr.eventId = val.evtCount;
+ updateCounter(reg_pmselr.sel, ctr);
+ }
+}
+
+void
+PMU::raiseInterrupt()
+{
+ RealView *rv(dynamic_cast<RealView *>(platform));
+ if (!rv || !rv->gic) {
+ warn_once("ARM PMU: GIC missing, can't raise interrupt.\n");
+ return;
+ }
+
+ DPRINTF(PMUVerbose, "Delivering PMU interrupt.\n");
+ rv->gic->sendInt(pmuInterrupt);
+}
+
+void
+PMU::serialize(std::ostream &os)
+{
+ DPRINTF(Checkpoint, "Serializing Arm PMU\n");
+
+ SERIALIZE_SCALAR(reg_pmcr);
+ SERIALIZE_SCALAR(reg_pmcnten);
+ SERIALIZE_SCALAR(reg_pmselr);
+ SERIALIZE_SCALAR(reg_pminten);
+ SERIALIZE_SCALAR(reg_pmovsr);
+ SERIALIZE_SCALAR(reg_pmceid);
+ SERIALIZE_SCALAR(clock_remainder);
+
+ for (size_t i = 0; i < counters.size(); ++i) {
+ nameOut(os, csprintf("%s.counters.%i", name(), i));
+ counters[i].serialize(os);
+ }
+
+ nameOut(os, csprintf("%s.cycleCounter", name()));
+ cycleCounter.serialize(os);
+}
+
+void
+PMU::unserialize(Checkpoint *cp, const std::string &section)
+{
+ DPRINTF(Checkpoint, "Unserializing Arm PMU\n");
+
+ UNSERIALIZE_SCALAR(reg_pmcr);
+ UNSERIALIZE_SCALAR(reg_pmcnten);
+ UNSERIALIZE_SCALAR(reg_pmselr);
+ UNSERIALIZE_SCALAR(reg_pminten);
+ UNSERIALIZE_SCALAR(reg_pmovsr);
+ UNSERIALIZE_SCALAR(reg_pmceid);
+ UNSERIALIZE_SCALAR(clock_remainder);
+
+ for (size_t i = 0; i < counters.size(); ++i)
+ counters[i].unserialize(cp, csprintf("%s.counters.%i", section, i));
+
+ cycleCounter.unserialize(cp, csprintf("%s.cycleCounter", section));
+}
+
+void
+PMU::CounterState::serialize(std::ostream &os)
+{
+ SERIALIZE_SCALAR(eventId);
+ SERIALIZE_SCALAR(value);
+ SERIALIZE_SCALAR(enabled);
+ SERIALIZE_SCALAR(overflow64);
+}
+
+void
+PMU::CounterState::unserialize(Checkpoint *cp, const std::string &section)
+{
+ UNSERIALIZE_SCALAR(eventId);
+ UNSERIALIZE_SCALAR(value);
+ UNSERIALIZE_SCALAR(enabled);
+ UNSERIALIZE_SCALAR(overflow64);
+}
+
+bool
+PMU::CounterState::add(uint64_t delta)
+{
+ const uint64_t msb(1ULL << (overflow64 ? 63 : 31));
+ const uint64_t old_value(value);
+
+ assert(delta > 0);
+
+ value += delta;
+
+ // Overflow if the msb goes from 1 to 0
+ return (old_value & msb) && !(value & msb);
+}
+
+} // namespace ArmISA
+
+ArmISA::PMU *
+ArmPMUParams::create()
+{
+ return new ArmISA::PMU(this);
+}
diff --git a/src/arch/arm/pmu.hh b/src/arch/arm/pmu.hh
new file mode 100644
index 000000000..989ad76c6
--- /dev/null
+++ b/src/arch/arm/pmu.hh
@@ -0,0 +1,495 @@
+/*
+ * 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: Dam Sunwoo
+ * Matt Horsnell
+ * Andreas Sandberg
+ */
+#ifndef __ARCH_ARM_PMU_HH__
+#define __ARCH_ARM_PMU_HH__
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "arch/arm/isa_device.hh"
+#include "arch/arm/registers.hh"
+#include "sim/probe/probe.hh"
+#include "sim/sim_object.hh"
+
+class ArmPMUParams;
+class Platform;
+class ThreadContext;
+
+namespace ArmISA {
+
+
+/**
+ * Model of an ARM PMU version 3
+ *
+ * This class implements a subset of the ARM PMU v3 specification as
+ * described in the ARMv8 reference manual. It supports most of the
+ * features of the PMU, however the following features are known to be
+ * missing:
+ *
+ * <ul>
+ * <li>Event filtering (e.g., from different privilege levels).
+ * <li>Access controls (the PMU currently ignores the execution level).
+ * <li>The chain counter (event no. 0x1E) is unimplemented.
+ * </ul>
+ *
+ * The PMU itself does not implement any events, in merely provides an
+ * interface for the configuration scripts to hook up probes that
+ * drive events. Configuration scripts should call addEventProbe() to
+ * configure custom events or high-level methods to configure
+ * architected events. The Python implementation of addEventProbe()
+ * automatically delays event type registration until after
+ * instantiation.
+ *
+ * In order to support CPU switching and some combined counters (e.g.,
+ * memory references synthesized from loads and stores), the PMU
+ * allows multiple probes per event type. When creating a system that
+ * switches between CPU models that share the same PMU, PMU events for
+ * all of the CPU models can be registered with the PMU.
+ *
+ * @see The ARM Architecture Refererence Manual (DDI 0487A)
+ *
+ */
+class PMU : public SimObject, public ArmISA::BaseISADevice {
+ public:
+ PMU(const ArmPMUParams *p);
+ ~PMU();
+
+ void addEventProbe(unsigned int id, SimObject *obj, const char *name);
+
+ public: // SimObject and related interfaces
+ void serialize(std::ostream &os) M5_ATTR_OVERRIDE;
+ void unserialize(Checkpoint *cp, const std::string &sec) M5_ATTR_OVERRIDE;
+
+ void drainResume() M5_ATTR_OVERRIDE;
+
+
+ public: // ISA Device interface
+ /**
+ * Set a register within the PMU.
+ *
+ * @param misc_reg Register number (see miscregs.hh)
+ * @param val Value to store
+ */
+ void setMiscReg(int misc_reg, MiscReg val) M5_ATTR_OVERRIDE;
+ /**
+ * Read a register within the PMU.
+ *
+ * @param misc_reg Register number (see miscregs.hh)
+ * @return Register value.
+ */
+ MiscReg readMiscReg(int misc_reg) M5_ATTR_OVERRIDE;
+
+ protected: // PMU register types and constants
+ BitUnion32(PMCR_t)
+ // PMU Enable
+ Bitfield<0> e;
+ // Event counter reset
+ Bitfield<1> p;
+ // Cycle counter reset
+ Bitfield<2> c;
+ // Cycle counter divider enable
+ Bitfield<3> d;
+ // Export enable
+ Bitfield<4> x;
+ // Disable PMCCNTR when event counting is prohibited
+ Bitfield<5> dp;
+ // Long Cycle counter enable
+ Bitfield<6> lc;
+ // Number of event counters implemented
+ Bitfield<15, 11> n;
+ // Implementation ID
+ Bitfield<23, 16> idcode;
+ // Implementer code
+ Bitfield<31, 24> imp;
+ EndBitUnion(PMCR_t)
+
+ BitUnion32(PMSELR_t)
+ // Performance counter selector
+ Bitfield<4, 0> sel;
+ EndBitUnion(PMSELR_t)
+
+ BitUnion32(PMEVTYPER_t)
+ Bitfield<9, 0> evtCount;
+
+ // Secure EL3 filtering
+ Bitfield<26> m;
+ // Non-secure EL2 mode filtering
+ Bitfield<27> nsh;
+ // Non-secure EL0 mode filtering
+ Bitfield<28> nsu;
+ // Non-secure EL1 mode filtering
+ Bitfield<29> nsk;
+ // EL0 filtering
+ Bitfield<30> u;
+ // EL1 filtering
+ Bitfield<31> p;
+ EndBitUnion(PMEVTYPER_t)
+
+ /**
+ * Counter ID within the PMU.
+ *
+ * This value is typically used to index into various registers
+ * controlling interrupts and overflows. The value normally in the
+ * [0, 31] range, where 31 refers to the cycle counter.
+ */
+ typedef unsigned int CounterId;
+
+ /** Cycle Count Register Number */
+ static const CounterId PMCCNTR = 31;
+
+ /**
+ * Event type ID.
+ *
+ * See the PMU documentation for a list of architected IDs.
+ */
+ typedef unsigned int EventTypeId;
+
+ /** ID of the software increment event */
+ static const EventTypeId ARCH_EVENT_SW_INCR = 0x00;
+
+ protected: /* High-level register and interrupt handling */
+ MiscReg readMiscRegInt(int misc_reg);
+
+ /**
+ * PMCR write handling
+ *
+ * The PMCR register needs special handling since writing to it
+ * changes PMU-global state (e.g., resets all counters).
+ *
+ * @param val New PMCR value
+ */
+ void setControlReg(PMCR_t val);
+
+ /**
+ * Reset all event counters excluding the cycle counter to zero.
+ */
+ void resetEventCounts();
+
+ /**
+ * Deliver a PMU interrupt to the GIC
+ */
+ void raiseInterrupt();
+
+ /**
+ * Get the value of a performance counter.
+ *
+ * This method returns the value of a general purpose performance
+ * counter or the fixed-function cycle counter. Non-existing
+ * counters are treated as constant '0'.
+ *
+ * @return Value of the performance counter, 0 if the counter does
+ * not exist.
+ */
+ uint64_t getCounterValue(CounterId id) const {
+ return isValidCounter(id) ? getCounter(id).value : 0;
+ }
+
+ /**
+ * Set the value of a performance counter.
+ *
+ * This method sets the value of a general purpose performance
+ * counter or the fixed-function cycle counter. Writes to
+ * non-existing counters are ignored.
+ */
+ void setCounterValue(CounterId id, uint64_t val);
+
+ /**
+ * Get the type and filter settings of a counter (PMEVTYPER)
+ *
+ * This method implements a read from a PMEVTYPER register. It
+ * returns the type value and filter settings of a general purpose
+ * performance counter or the cycle counter. Non-existing counters
+ * are treated as constant '0'.
+ *
+ * @param id Counter ID within the PMU.
+ * @return Performance counter type ID.
+ */
+ PMEVTYPER_t getCounterTypeRegister(CounterId id) const;
+
+ /**
+ * Set the type and filter settings of a performance counter
+ * (PMEVTYPER)
+ *
+ * This method implements a write to a PMEVTYPER register. It sets
+ * the type value and filter settings of a general purpose
+ * performance counter or the cycle counter. Writes to
+ * non-existing counters are ignored. The method automatically
+ * updates the probes used by the counter if it is enabled.
+ *
+ * @param id Counter ID within the PMU.
+ * @param type Performance counter type and filter configuration..
+ */
+ void setCounterTypeRegister(CounterId id, PMEVTYPER_t type);
+
+ protected: /* Probe handling and counter state */
+ class ProbeListener : public ProbeListenerArgBase<uint64_t>
+ {
+ public:
+ ProbeListener(PMU &_pmu, CounterId _id,
+ ProbeManager *pm, const std::string &name)
+ : ProbeListenerArgBase(pm, name),
+ pmu(_pmu), id(_id) {}
+
+ void notify(const uint64_t &val) M5_ATTR_OVERRIDE
+ {
+ pmu.handleEvent(id, val);
+ }
+
+ protected:
+ PMU &pmu;
+ const CounterId id;
+ };
+ typedef std::unique_ptr<ProbeListener> ProbeListenerUPtr;
+
+ /**
+ * Event type configuration
+ *
+ * The main purpose of this class is to describe how a PMU event
+ * type is sampled. It is implemented as a probe factory that
+ * returns a probe attached to the object the event is mointoring.
+ */
+ struct EventType {
+ /**
+ * @param _obj Target SimObject
+ * @param _name Probe name
+ */
+ EventType(SimObject *_obj, const std::string &_name)
+ : obj(_obj), name(_name) {}
+
+ /**
+ * Create and attach a probe used to drive this event.
+ *
+ * @param pmu PMU owning the probe.
+ * @param CounterID counter ID within the PMU.
+ * @return Pointer to a probe listener.
+ */
+ std::unique_ptr<ProbeListener> create(PMU &pmu, CounterId cid) const
+ {
+ std::unique_ptr<ProbeListener> ptr;
+ ptr.reset(new ProbeListener(pmu, cid,
+ obj->getProbeManager(), name));
+ return ptr;
+ }
+
+ /** SimObject being measured by this probe */
+ SimObject *const obj;
+ /** Probe name within obj */
+ const std::string name;
+
+ private:
+ // Disable the default constructor
+ EventType();
+ };
+
+ /** State of a counter within the PMU. */
+ struct CounterState {
+ CounterState()
+ : eventId(0), value(0), enabled(false),
+ overflow64(false) {
+
+ listeners.reserve(4);
+ }
+
+ void serialize(std::ostream &os);
+ void unserialize(Checkpoint *cp, const std::string &section);
+
+ /**
+ * Add an event count to the counter and check for overflow.
+ *
+ * @param delta Number of events to add to the counter.
+ * @return true on overflow, false otherwise.
+ */
+ bool add(uint64_t delta);
+
+ public: /* Serializable state */
+ /** Counter event ID */
+ EventTypeId eventId;
+
+ /** Current value of the counter */
+ uint64_t value;
+
+ /** Is the counter enabled? */
+ bool enabled;
+
+ /** Is this a 64-bit counter? */
+ bool overflow64;
+
+ public: /* Configuration */
+ /** Probe listeners driving this counter */
+ std::vector<ProbeListenerUPtr> listeners;
+ };
+
+ /**
+ * Handle an counting event triggered by a probe.
+ *
+ * This method is called by the ProbeListener class whenever an
+ * active probe is triggered. Ths method adds the event count from
+ * the probe to the affected counter, checks for overflows, and
+ * delivers an interrupt if needed.
+ *
+ * @param id Counter ID affected by the probe.
+ * @param delta Counter increment
+ */
+ void handleEvent(CounterId id, uint64_t delta);
+
+ /**
+ * Is this a valid counter ID?
+ *
+ * @param id ID of counter within the PMU.
+ *
+ * @return true if counter is within the allowed range or the
+ * cycle counter, false otherwise.
+ */
+ bool isValidCounter(CounterId id) const {
+ return id < counters.size() || id == PMCCNTR;
+ }
+
+ /**
+ * Return the state of a counter.
+ *
+ * @param id ID of counter within the PMU.
+ * @return Reference to a CounterState instance representing the
+ * counter.
+ */
+ CounterState &getCounter(CounterId id) {
+ assert(isValidCounter(id));
+ return id == PMCCNTR ? cycleCounter : counters[id];
+ }
+
+
+ /**
+ * Return the state of a counter.
+ *
+ * @param id ID of counter within the PMU.
+ * @return Reference to a CounterState instance representing the
+ * counter.
+ */
+ const CounterState &getCounter(CounterId id) const {
+ assert(isValidCounter(id));
+ return id == PMCCNTR ? cycleCounter : counters[id];
+ }
+
+ /**
+ * Depending on counter configuration, add or remove the probes
+ * driving the counter.
+ *
+ * Look at the state of a counter and (re-)attach the probes
+ * needed to drive a counter if it is currently active. All probes
+ * for the counter are detached if the counter is inactive.
+ *
+ * @param id ID of counter within the PMU.
+ * @param ctr Reference to the counter's state
+ */
+ void updateCounter(CounterId id, CounterState &ctr);
+
+ /**
+ * Call updateCounter() for each counter in the PMU if the
+ * counter's state has changed..
+ *
+ * @see updateCounter()
+ */
+ void updateAllCounters();
+
+ protected: /* State that needs to be serialized */
+ /** Performance Monitor Count Enable Register */
+ MiscReg reg_pmcnten;
+
+ /** Performance Monitor Control Register */
+ PMCR_t reg_pmcr;
+
+ /** Performance Monitor Selection Register */
+ PMSELR_t reg_pmselr;
+
+ /** Performance Monitor Interrupt Enable Register */
+ MiscReg reg_pminten;
+
+ /** Performance Monitor Overflow Status Register */
+ MiscReg reg_pmovsr;
+
+ /**
+ * Performance counter ID register
+ *
+ * This register contains a bitmask of available architected
+ * counters.
+ */
+ uint64_t reg_pmceid;
+
+ /** Remainder part when the clock counter is divided by 64 */
+ unsigned clock_remainder;
+
+ /** State of all general-purpose counters supported by PMU */
+ std::vector<CounterState> counters;
+ /** State of the cycle counter */
+ CounterState cycleCounter;
+
+ protected: /* Configuration and constants */
+ /** Constant (configuration-dependent) part of the PMCR */
+ PMCR_t reg_pmcr_conf;
+ /** PMCR write mask when accessed from the guest */
+ static const MiscReg reg_pmcr_wr_mask;
+
+ /** Performance monitor interrupt number */
+ const unsigned int pmuInterrupt;
+ /** Platform this device belongs to */
+ Platform *const platform;
+
+ /**
+ * Event types supported by this PMU.
+ *
+ * Each event type ID can map to multiple EventType structures,
+ * which enables the PMU to use multiple probes for a single
+ * event. This can be useful in the following cases:
+ * <ul>
+ * <li>Some events can are increment by multiple different probe
+ * points (e.g., the CPU memory access counter gets
+ * incremented for both loads and stores).
+ *
+ * <li>A system switching between multiple CPU models can
+ * register events for all models that will execute a thread
+ * and tehreby ensure that the PMU continues to work.
+ * </ul>
+ */
+ std::multimap<EventTypeId, EventType> pmuEventTypes;
+};
+
+} // namespace ArmISA
+#endif