Change the definition of PMU events in order to integrate events not cannot easily be represented by probe points. The software increment event is now defined as a special type with its separate implementation in pmu.cc and pmu.hh. Change-Id: I43874b9641bf38c54f6ba2c26386542b6a73e282 Signed-off-by: Jose Marinho <jose.marinho@arm.com> Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com> Reviewed-on: https://gem5-review.googlesource.com/5764 Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
614 lines
18 KiB
C++
614 lines
18 KiB
C++
/*
|
|
* 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
|