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(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(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 §ion) +{ + 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 §ion) +{ + 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 +#include +#include + +#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: + * + *
    + *
  • Event filtering (e.g., from different privilege levels). + *
  • Access controls (the PMU currently ignores the execution level). + *
  • The chain counter (event no. 0x1E) is unimplemented. + *
+ * + * 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 + { + 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 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 create(PMU &pmu, CounterId cid) const + { + std::unique_ptr 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 §ion); + + /** + * 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 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 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: + *
    + *
  • Some events can are increment by multiple different probe + * points (e.g., the CPU memory access counter gets + * incremented for both loads and stores). + * + *
  • 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. + *
+ */ + std::multimap pmuEventTypes; +}; + +} // namespace ArmISA +#endif