| /* | |
| * Copyright (c) 2011-2014, 2017 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 "arch/arm/system.hh" | |
| #include "base/cprintf.hh" | |
| #include "cpu/base.hh" | |
| #include "debug/PMUVerbose.hh" | |
| #include "sim/eventq.hh" | |
| #include "sim/sim_object.hh" | |
| #include "sim/system.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); | |
| void addSoftwareIncrementEvent(unsigned int id); | |
| void registerEvent(uint32_t id); | |
| public: // SimObject and related interfaces | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| void drainResume() override; | |
| void regProbeListeners() 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) override; | |
| /** | |
| * Read a register within the PMU. | |
| * | |
| * @param misc_reg Register number (see miscregs.hh) | |
| * @return Register value. | |
| */ | |
| MiscReg readMiscReg(int misc_reg) 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<15, 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; | |
| 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).getValue() : 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 */ | |
| struct CounterState; | |
| /** | |
| * Event definition base class | |
| */ | |
| struct PMUEvent { | |
| PMUEvent() {} | |
| virtual ~PMUEvent() {} | |
| /** | |
| * attach this event to a given counter | |
| * | |
| * @param a pointer to the counter where to attach this event | |
| */ | |
| void attachEvent(PMU::CounterState *user); | |
| /** | |
| * detach this event from a given counter | |
| * | |
| * @param a pointer to the counter where to detach this event from | |
| */ | |
| void detachEvent(PMU::CounterState *user); | |
| /** | |
| * notify an event increment of val units, all the attached counters' | |
| * value is incremented by val units. | |
| * | |
| * @param the quantity by which to increment the attached counter | |
| * values | |
| */ | |
| virtual void increment(const uint64_t val); | |
| /** | |
| * Enable the current event | |
| */ | |
| virtual void enable() = 0; | |
| /** | |
| * Disable the current event | |
| */ | |
| virtual void disable() = 0; | |
| /** | |
| * Method called immediately before a counter access in order for | |
| * the associated event to update its state (if required) | |
| */ | |
| virtual void updateAttachedCounters() {} | |
| protected: | |
| /** set of counters using this event **/ | |
| std::set<PMU::CounterState*> userCounters; | |
| }; | |
| struct RegularEvent : public PMUEvent { | |
| typedef std::pair<SimObject*, std::string> EventTypeEntry; | |
| void addMicroarchitectureProbe(SimObject* object, | |
| std::string name) { | |
| panic_if(!object,"malformed probe-point" | |
| " definition with name %s\n", name); | |
| microArchitectureEventSet.emplace(object, name); | |
| } | |
| protected: | |
| struct RegularProbe: public ProbeListenerArgBase<uint64_t> | |
| { | |
| RegularProbe(RegularEvent *parent, SimObject* obj, | |
| std::string name) | |
| : ProbeListenerArgBase(obj->getProbeManager(), name), | |
| parentEvent(parent) {} | |
| RegularProbe() = delete; | |
| void notify(const uint64_t &val); | |
| protected: | |
| RegularEvent *parentEvent; | |
| }; | |
| /** The set of events driving the event value **/ | |
| std::set<EventTypeEntry> microArchitectureEventSet; | |
| /** Set of probe listeners tapping onto each of the input micro-arch | |
| * events which compose this pmu event | |
| */ | |
| std::vector<std::unique_ptr<RegularProbe>> attachedProbePointList; | |
| void enable() override; | |
| void disable() override; | |
| }; | |
| class SWIncrementEvent : public PMUEvent | |
| { | |
| void enable() override {} | |
| void disable() override {} | |
| public: | |
| /** | |
| * write on the sw increment register inducing an increment of the | |
| * counters with this event selected according to the bitfield written. | |
| * | |
| * @param the bitfield selecting the counters to increment. | |
| */ | |
| void write(uint64_t val); | |
| }; | |
| /** | |
| * Obtain the event of a given id | |
| * | |
| * @param the id of the event to obtain | |
| * @return a pointer to the event with id eventId | |
| */ | |
| PMUEvent* getEvent(uint64_t eventId); | |
| /** State of a counter within the PMU. **/ | |
| struct CounterState : public Serializable { | |
| CounterState(PMU &pmuReference, uint64_t counter_id) | |
| : eventId(0), filter(0), enabled(false), | |
| overflow64(false), sourceEvent(nullptr), | |
| counterId(counter_id), value(0), resetValue(false), | |
| pmu(pmuReference) {} | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| /** | |
| * Add an event count to the counter and check for overflow. | |
| * | |
| * @param delta Number of events to add to the counter. | |
| * @return the quantity remaining until a counter overflow occurs. | |
| */ | |
| uint64_t add(uint64_t delta); | |
| bool isFiltered() const; | |
| /** | |
| * Detach the counter from its event | |
| */ | |
| void detach(); | |
| /** | |
| * Attach this counter to an event | |
| * | |
| * @param the event to attach the counter to | |
| */ | |
| void attach(PMUEvent* event); | |
| /** | |
| * Obtain the counter id | |
| * | |
| * @return the pysical counter id | |
| */ | |
| uint64_t getCounterId() const{ | |
| return counterId; | |
| } | |
| /** | |
| * rReturn the counter value | |
| * | |
| * @return the counter value | |
| */ | |
| uint64_t getValue() const; | |
| /** | |
| * overwrite the value of the counter | |
| * | |
| * @param the new counter value | |
| */ | |
| void setValue(uint64_t val); | |
| public: /* Serializable state */ | |
| /** Counter event ID */ | |
| EventTypeId eventId; | |
| /** Filtering settings (evtCount is unused) */ | |
| PMEVTYPER_t filter; | |
| /** Is the counter enabled? */ | |
| bool enabled; | |
| /** Is this a 64-bit counter? */ | |
| bool overflow64; | |
| protected: /* Configuration */ | |
| /** PmuEvent currently in use (if any) **/ | |
| PMUEvent *sourceEvent; | |
| /** id of the counter instance **/ | |
| uint64_t counterId; | |
| /** Current value of the counter */ | |
| uint64_t value; | |
| /** Flag keeping track if the counter has been reset **/ | |
| bool resetValue; | |
| PMU &pmu; | |
| template <typename ...Args> | |
| void debugCounter(const char* mainString, Args &...args) const { | |
| std::string userString = csprintf(mainString, args...); | |
| warn("[counterId = %d, eventId = %d, sourceEvent = 0x%x] %s", | |
| counterId, eventId, sourceEvent, userString.c_str()); | |
| } | |
| }; | |
| /** | |
| * 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(CounterState &ctr); | |
| /** | |
| * Check if a counter's settings allow it to be counted. | |
| * | |
| * @param ctr Counter state instance representing this counter. | |
| * @return false if the counter is active, true otherwise. | |
| */ | |
| bool isFiltered(const CounterState &ctr) const; | |
| /** | |
| * 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 | |
| * | |
| * These registers contain a bitmask of available architected | |
| * counters. | |
| */ | |
| uint64_t reg_pmceid0; | |
| uint64_t reg_pmceid1; | |
| /** Remainder part when the clock counter is divided by 64 */ | |
| unsigned clock_remainder; | |
| /** The number of regular event counters **/ | |
| uint64_t maximumCounterCount; | |
| /** State of all general-purpose counters supported by PMU */ | |
| std::vector<CounterState> counters; | |
| /** State of the cycle counter */ | |
| CounterState cycleCounter; | |
| /** The id of the counter hardwired to the cpu cycle counter **/ | |
| const uint64_t cycleCounterEventId; | |
| /** The event that implements the software increment **/ | |
| SWIncrementEvent *swIncrementEvent; | |
| 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; | |
| /** | |
| * List of event types supported by this PMU. | |
| */ | |
| std::map<EventTypeId, PMUEvent*> eventMap; | |
| }; | |
| } // namespace ArmISA | |
| #endif |