diff options
Diffstat (limited to 'src/arch/arm/pmu.hh')
-rw-r--r-- | src/arch/arm/pmu.hh | 495 |
1 files changed, 495 insertions, 0 deletions
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 §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<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 |