summaryrefslogtreecommitdiff
path: root/src/arch/arm/pmu.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/arm/pmu.cc')
-rw-r--r--src/arch/arm/pmu.cc559
1 files changed, 559 insertions, 0 deletions
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);
+}