From 4bb053c3d2a4f7563eca8761f859458f8cc47a05 Mon Sep 17 00:00:00 2001 From: Jose Marinho Date: Tue, 26 Sep 2017 14:33:49 +0100 Subject: arch-arm: ArmPMU refactor 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 Reviewed-by: Andreas Sandberg Reviewed-on: https://gem5-review.googlesource.com/5764 Maintainer: Andreas Sandberg --- src/arch/arm/ArmPMU.py | 81 ++++++++----- src/arch/arm/pmu.cc | 315 +++++++++++++++++++++++++++++++++++++------------ src/arch/arm/pmu.hh | 296 +++++++++++++++++++++++++++++++--------------- 3 files changed, 497 insertions(+), 195 deletions(-) diff --git a/src/arch/arm/ArmPMU.py b/src/arch/arm/ArmPMU.py index 83c7dd48e..a87c20b4a 100644 --- a/src/arch/arm/ArmPMU.py +++ b/src/arch/arm/ArmPMU.py @@ -43,38 +43,61 @@ from m5.params import * from m5.params import isNullPointer from m5.proxy import * +class ProbeEvent(object): + def __init__(self, pmu, _eventId, obj, *listOfNames): + self.obj = obj + self.names = listOfNames + self.eventId = _eventId + self.pmu = pmu + + def register(self): + if self.obj: + for name in self.names: + self.pmu.getCCObject().addEventProbe(self.eventId, + self.obj.getCCObject(), name) + +class SoftwareIncrement(object): + def __init__(self,pmu, _eventId): + self.eventId = _eventId + self.pmu = pmu + + def register(self): + self.pmu.getCCObject().addSoftwareIncrementEvent(self.eventId) + +ARCH_EVENT_CORE_CYCLES = 0x11 + class ArmPMU(SimObject): + type = 'ArmPMU' cxx_class = 'ArmISA::PMU' cxx_header = 'arch/arm/pmu.hh' cxx_exports = [ PyBindMethod("addEventProbe"), + PyBindMethod("addSoftwareIncrementEvent"), ] - # 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 = [] + _events = None + + def addEvent(self, newObject): + if not (isinstance(newObject, ProbeEvent) + or isinstance(newObject, SoftwareIncrement)): + raise TypeError("argument must be of ProbeEvent or " + "SoftwareIncrement type") + + if not self._events: + self._events = [] + + self._events.append(newObject) + # 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) + for event in self._events: + event.register() 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)) - def addArchEvents(self, cpu=None, itb=None, dtb=None, @@ -95,25 +118,28 @@ class ArmPMU(SimObject): bpred = cpu.branchPred if cpu and not isNullPointer(cpu.branchPred) \ else None + self.addEvent(SoftwareIncrement(self,0x00)) # 0x01: L1I_CACHE_REFILL - self.addEventProbe(0x02, itb, "Refills") + self.addEvent(ProbeEvent(self,0x02, itb, "Refills")) # 0x03: L1D_CACHE_REFILL # 0x04: L1D_CACHE - self.addEventProbe(0x05, dtb, "Refills") - self.addEventProbe(0x06, cpu, "RetiredLoads") - self.addEventProbe(0x07, cpu, "RetiredStores") - self.addEventProbe(0x08, cpu, "RetiredInsts") + self.addEvent(ProbeEvent(self,0x05, dtb, "Refills")) + self.addEvent(ProbeEvent(self,0x06, cpu, "RetiredLoads")) + self.addEvent(ProbeEvent(self,0x07, cpu, "RetiredStores")) + self.addEvent(ProbeEvent(self,0x08, cpu, "RetiredInsts")) # 0x09: EXC_TAKEN # 0x0A: EXC_RETURN # 0x0B: CID_WRITE_RETIRED - self.addEventProbe(0x0C, cpu, "RetiredBranches") + self.addEvent(ProbeEvent(self,0x0C, cpu, "RetiredBranches")) # 0x0D: BR_IMMED_RETIRED # 0x0E: BR_RETURN_RETIRED # 0x0F: UNALIGEND_LDST_RETIRED - self.addEventProbe(0x10, bpred, "Misses") - self.addEventProbe(0x11, cpu, "ActiveCycles") - self.addEventProbe(0x12, bpred, "Branches") - self.addEventProbe(0x13, cpu, "RetiredLoads", "RetiredStores") + self.addEvent(ProbeEvent(self,0x10, bpred, "Misses")) + self.addEvent(ProbeEvent(self, ARCH_EVENT_CORE_CYCLES, cpu, + "ActiveCycles")) + self.addEvent(ProbeEvent(self,0x12, bpred, "Branches")) + self.addEvent(ProbeEvent(self,0x13, cpu, "RetiredLoads", + "RetiredStores")) # 0x14: L1I_CACHE # 0x15: L1D_CACHE_WB # 0x16: L2D_CACHE @@ -144,6 +170,7 @@ class ArmPMU(SimObject): # 0x2F: L2D_TLB # 0x30: L2I_TLB + cycleEventId = Param.Int(ARCH_EVENT_CORE_CYCLES, "Cycle event id") 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/pmu.cc b/src/arch/arm/pmu.cc index baf0d1948..0bdae4936 100644 --- a/src/arch/arm/pmu.cc +++ b/src/arch/arm/pmu.cc @@ -37,6 +37,7 @@ * Authors: Dam Sunwoo * Matt Horsnell * Andreas Sandberg + * Jose Marinho */ #include "arch/arm/pmu.hh" @@ -48,6 +49,7 @@ #include "debug/Checkpoint.hh" #include "debug/PMUVerbose.hh" #include "dev/arm/base_gic.hh" +#include "dev/arm/generic_timer.hh" #include "dev/arm/realview.hh" #include "params/ArmPMU.hh" @@ -61,16 +63,19 @@ PMU::PMU(const ArmPMUParams *p) reg_pmselr(0), reg_pminten(0), reg_pmovsr(0), reg_pmceid0(0),reg_pmceid1(0), clock_remainder(0), - counters(p->eventCounters), + maximumCounterCount(p->eventCounters), + cycleCounter(*this, maximumCounterCount), + cycleCounterEventId(p->cycleEventId), + swIncrementEvent(nullptr), reg_pmcr_conf(0), pmuInterrupt(p->pmuInterrupt), platform(p->platform) { DPRINTF(PMUVerbose, "Initializing the PMU.\n"); - if (p->eventCounters > 31) { + if (maximumCounterCount > 31) { fatal("The PMU can only accept 31 counters, %d counters requested.\n", - p->eventCounters); + maximumCounterCount); } /* Setup the performance counter ID registers */ @@ -87,13 +92,57 @@ PMU::~PMU() { } +void +PMU::addSoftwareIncrementEvent(unsigned int id) +{ + auto old_event = eventMap.find(id); + DPRINTF(PMUVerbose, "PMU: Adding SW increment event with id '0x%x'\n", id); + + if (swIncrementEvent) { + fatal_if(old_event == eventMap.end() || + old_event->second != swIncrementEvent, + "Trying to add a software increment event with multiple" + "IDs. This is not supported.\n"); + return; + } + + fatal_if(old_event != eventMap.end(), "An event with id %d has " + "been previously defined\n", id); + + swIncrementEvent = new SWIncrementEvent(); + eventMap[id] = swIncrementEvent; + registerEvent(id); +} + 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))); + DPRINTF(PMUVerbose, "PMU: Adding Probe Driven event with id '0x%x'" + "as probe %s:%s\n",id, obj->name(), probe_name); + + RegularEvent *event = nullptr; + auto event_entry = eventMap.find(id); + if (event_entry == eventMap.end()) { + + event = new RegularEvent(); + eventMap[id] = event; + + } else { + event = dynamic_cast(event_entry->second); + if (!event) { + fatal("Event with id %d is not probe driven\n", id); + } + } + event->addMicroarchitectureProbe(obj, probe_name); + + registerEvent(id); + +} + +void +PMU::registerEvent(uint32_t id) +{ // Flag the event as available in the corresponding PMCEID register if it // is an architected event. if (id < 0x20) { @@ -114,6 +163,22 @@ PMU::drainResume() updateAllCounters(); } +void +PMU::regProbeListeners() +{ + + // at this stage all probe configurations are done + // counters can be configured + for (uint32_t index = 0; index < maximumCounterCount-1; index++) { + counters.emplace_back(*this, index); + } + + PMUEvent *event = getEvent(cycleCounterEventId); + panic_if(!event, "core cycle event is not present\n"); + cycleCounter.enabled = true; + cycleCounter.attach(event); +} + void PMU::setMiscReg(int misc_reg, MiscReg val) { @@ -145,18 +210,14 @@ PMU::setMiscReg(int misc_reg, MiscReg val) 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.eventId == ARCH_EVENT_SW_INCR ) { - ++ctr.value; - } + if (swIncrementEvent) { + swIncrementEvent->write(val); } - break; + return; case MISCREG_PMCCNTR_EL0: case MISCREG_PMCCNTR: - cycleCounter.value = val; + cycleCounter.setValue(val); return; case MISCREG_PMSELR_EL0: @@ -278,10 +339,10 @@ PMU::readMiscRegInt(int misc_reg) return reg_pmceid1 & 0xFFFFFFFF; case MISCREG_PMCCNTR_EL0: - return cycleCounter.value; + return cycleCounter.getValue(); case MISCREG_PMCCNTR: - return cycleCounter.value & 0xFFFFFFFF; + return cycleCounter.getValue() & 0xFFFFFFFF; case MISCREG_PMEVTYPER0_EL0...MISCREG_PMEVTYPER5_EL0: return getCounterTypeRegister(misc_reg - MISCREG_PMEVTYPER0_EL0); @@ -295,8 +356,11 @@ PMU::readMiscRegInt(int misc_reg) 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_PMEVCNTR0_EL0...MISCREG_PMEVCNTR5_EL0: { + return getCounterValue(misc_reg - MISCREG_PMEVCNTR0_EL0) & + 0xFFFFFFFF; + + } case MISCREG_PMXEVCNTR_EL0: case MISCREG_PMXEVCNTR: @@ -334,7 +398,7 @@ PMU::setControlReg(PMCR_t val) if (val.c) { DPRINTF(PMUVerbose, "PMU reset cycle counter to zero.\n"); - cycleCounter.value = 0; + cycleCounter.setValue(0); } // Reset the clock remainder if divide by 64-mode is toggled. @@ -355,25 +419,74 @@ PMU::updateAllCounters() const bool enable(global_enable && (reg_pmcnten & (1 << i))); if (ctr.enabled != enable) { ctr.enabled = enable; - updateCounter(i, ctr); + updateCounter(ctr); } } const bool ccntr_enable(global_enable && (reg_pmcnten & (1 << PMCCNTR))); if (cycleCounter.enabled != ccntr_enable) { cycleCounter.enabled = ccntr_enable; - updateCounter(PMCCNTR, cycleCounter); + updateCounter(cycleCounter); + } +} + +void +PMU::PMUEvent::attachEvent(PMU::CounterState *user) +{ + if (userCounters.empty()) { + enable(); } + userCounters.insert(user); + updateAttachedCounters(); +} + +void +PMU::PMUEvent::increment(const uint64_t val) +{ + for (auto& counter: userCounters) { + counter->add(val); + } +} + +void +PMU::PMUEvent::detachEvent(PMU::CounterState *user) +{ + userCounters.erase(user); + + if (userCounters.empty()) { + disable(); + } +} + +void +PMU::RegularEvent::RegularProbe::notify(const uint64_t &val) +{ + parentEvent->increment(val); +} + +void +PMU::RegularEvent::enable() +{ + for (auto& subEvents: microArchitectureEventSet) { + attachedProbePointList.emplace_back( + new RegularProbe(this, subEvents.first, subEvents.second)); + } +} + +void +PMU::RegularEvent::disable() +{ + attachedProbePointList.clear(); } bool -PMU::isFiltered(const CounterState &ctr) const +PMU::CounterState::isFiltered() const { - assert(isa); + assert(pmu.isa); - const PMEVTYPER_t filter(ctr.filter); - const SCR scr(isa->readMiscRegNoEffect(MISCREG_SCR)); - const CPSR cpsr(isa->readMiscRegNoEffect(MISCREG_CPSR)); + const PMEVTYPER_t filter(this->filter); + const SCR scr(pmu.isa->readMiscRegNoEffect(MISCREG_SCR)); + const CPSR cpsr(pmu.isa->readMiscRegNoEffect(MISCREG_CPSR)); const ExceptionLevel el(opModeToEL((OperatingMode)(uint8_t)cpsr.mode)); const bool secure(inSecureState(scr, cpsr)); @@ -396,60 +509,69 @@ PMU::isFiltered(const CounterState &ctr) const } void -PMU::handleEvent(CounterId id, uint64_t delta) +PMU::CounterState::detach() { - CounterState &ctr(getCounter(id)); - const bool overflowed(reg_pmovsr & (1 << id)); + if (sourceEvent) { + sourceEvent->detachEvent(this); + sourceEvent = nullptr; + } else { + debugCounter("detaching event not currently attached" + " to any event\n"); + } +} - if (isFiltered(ctr)) - return; +void +PMU::CounterState::attach(PMUEvent* event) +{ + value = 0; + sourceEvent = event; + sourceEvent->attachEvent(this); +} - // Handle the "count every 64 cycles" mode - if (id == PMCCNTR && reg_pmcr.d) { - clock_remainder += delta; - delta = (clock_remainder >> 6); - clock_remainder &= 63; +uint64_t +PMU::CounterState::getValue() const +{ + if (sourceEvent) { + sourceEvent->updateAttachedCounters(); + } else { + debugCounter("attempted to get value from a counter without" + " an associated event\n"); } + return value; +} - // 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::CounterState::setValue(uint64_t val) +{ + value = val; + resetValue = true; + + if (sourceEvent) { + sourceEvent->updateAttachedCounters(); + } else { + debugCounter("attempted to set value from a counter without" + " an associated event\n"); } } void -PMU::updateCounter(CounterId id, CounterState &ctr) +PMU::updateCounter(CounterState &ctr) { if (!ctr.enabled) { - if (!ctr.listeners.empty()) { - DPRINTF(PMUVerbose, "updateCounter(%i): Disabling counter\n", id); - ctr.listeners.clear(); - } + DPRINTF(PMUVerbose, "updateCounter(%i): Disabling counter\n", + ctr.getCounterId()); + ctr.detach(); + } 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); + ctr.getCounterId(), ctr.eventId); - 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) { + auto sourceEvent = eventMap.find(ctr.eventId); + if (sourceEvent == eventMap.end()) { warn("Can't enable PMU counter of type '0x%x': " "No such event type.\n", ctr.eventId); + } else { + ctr.attach(sourceEvent->second); } } } @@ -459,7 +581,7 @@ void PMU::resetEventCounts() { for (CounterState &ctr : counters) - ctr.value = 0; + ctr.setValue(0); } void @@ -472,7 +594,7 @@ PMU::setCounterValue(CounterId id, uint64_t val) } CounterState &ctr(getCounter(id)); - ctr.value = val; + ctr.setValue(val); } PMU::PMEVTYPER_t @@ -509,7 +631,7 @@ PMU::setCounterTypeRegister(CounterId id, PMEVTYPER_t val) // need to update the probes the counter is using. if (id != PMCCNTR && old_event_id != val.evtCount) { ctr.eventId = val.evtCount; - updateCounter(reg_pmselr.sel, ctr); + updateCounter(ctr); } } @@ -574,6 +696,19 @@ PMU::unserialize(CheckpointIn &cp) cycleCounter.unserializeSection(cp, "cycleCounter"); } +PMU::PMUEvent* +PMU::getEvent(uint64_t eventId) +{ + auto entry = eventMap.find(eventId); + + if (entry == eventMap.end()) { + warn("event %d does not exist\n", eventId); + return nullptr; + } else { + return entry->second; + } +} + void PMU::CounterState::serialize(CheckpointOut &cp) const { @@ -590,16 +725,50 @@ PMU::CounterState::unserialize(CheckpointIn &cp) UNSERIALIZE_SCALAR(overflow64); } -bool +uint64_t PMU::CounterState::add(uint64_t delta) { - const uint64_t msb(1ULL << (overflow64 ? 63 : 31)); - const uint64_t old_value(value); + uint64_t value_until_overflow; + if (overflow64) { + value_until_overflow = UINT64_MAX - value; + } else { + value_until_overflow = UINT32_MAX - (uint32_t)value; + } + + if (isFiltered()) + return value_until_overflow; + + if (resetValue) { + delta = 0; + resetValue = false; + } else { + value += delta; + } - value += delta; + if (delta > value_until_overflow) { - // Overflow if the msb goes from 1 to 0 - return (old_value & msb) && !(value & msb); + // overflow situation detected + // flag the overflow occurence + pmu.reg_pmovsr |= (1 << counterId); + + // Deliver a PMU interrupt if interrupt delivery is enabled + // for this counter. + if (pmu.reg_pminten & (1 << counterId)) { + pmu.raiseInterrupt(); + } + return overflow64 ? UINT64_MAX : UINT32_MAX; + } + return value_until_overflow - delta + 1; +} + +void +PMU::SWIncrementEvent::write(uint64_t val) +{ + for (auto& counter: userCounters) { + if (val & (0x1 << counter->getCounterId())) { + counter->add(1); + } + } } } // namespace ArmISA diff --git a/src/arch/arm/pmu.hh b/src/arch/arm/pmu.hh index aecdfd84e..7090b4a78 100644 --- a/src/arch/arm/pmu.hh +++ b/src/arch/arm/pmu.hh @@ -47,8 +47,13 @@ #include "arch/arm/isa_device.hh" #include "arch/arm/registers.hh" -#include "sim/probe/probe.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; @@ -94,6 +99,9 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { ~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; @@ -101,6 +109,7 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { void drainResume() override; + void regProbeListeners() override; public: // ISA Device interface /** @@ -183,9 +192,6 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { */ 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); @@ -220,7 +226,7 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { * not exist. */ uint64_t getCounterValue(CounterId id) const { - return isValidCounter(id) ? getCounter(id).value : 0; + return isValidCounter(id) ? getCounter(id).getValue() : 0; } /** @@ -261,74 +267,135 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { 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) override - { - pmu.handleEvent(id, val); - } - - protected: - PMU &pmu; - const CounterId id; - }; - typedef std::unique_ptr ProbeListenerUPtr; + struct CounterState; /** - * 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. + * Event definition base class */ - struct EventType { + struct PMUEvent { + + PMUEvent() {} + + virtual ~PMUEvent() {} + /** - * @param _obj Target SimObject - * @param _name Probe name + * attach this event to a given counter + * + * @param a pointer to the counter where to attach this event */ - EventType(SimObject *_obj, const std::string &_name) - : obj(_obj), name(_name) {} + void attachEvent(PMU::CounterState *user); /** - * Create and attach a probe used to drive this event. + * detach this event from a given counter * - * @param pmu PMU owning the probe. - * @param CounterID counter ID within the PMU. - * @return Pointer to a probe listener. + * @param a pointer to the counter where to detach this event from */ - std::unique_ptr create(PMU &pmu, CounterId cid) const - { - std::unique_ptr ptr; - ptr.reset(new ProbeListener(pmu, cid, - obj->getProbeManager(), name)); - return ptr; - } + 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; - /** SimObject being measured by this probe */ - SimObject *const obj; - /** Probe name within obj */ - const std::string name; + /** + * Method called immediately before a counter access in order for + * the associated event to update its state (if required) + */ + virtual void updateAttachedCounters() {} + + protected: - private: - // Disable the default constructor - EventType(); + /** set of counters using this event **/ + std::set userCounters; }; - /** State of a counter within the PMU. */ - struct CounterState : public Serializable { - CounterState() - : eventId(0), filter(0), value(0), enabled(false), - overflow64(false) { + struct RegularEvent : public PMUEvent { + typedef std::pair EventTypeEntry; + + void addMicroarchitectureProbe(SimObject* object, + std::string name) { + + panic_if(!object,"malformed probe-point" + " definition with name %s\n", name); - listeners.reserve(4); + microArchitectureEventSet.emplace(object, name); } + protected: + struct RegularProbe: public ProbeListenerArgBase + { + 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 microArchitectureEventSet; + + /** Set of probe listeners tapping onto each of the input micro-arch + * events which compose this pmu event + */ + std::vector> 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; @@ -336,9 +403,46 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { * 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. + * @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 */ - bool add(uint64_t delta); + 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 */ @@ -347,32 +451,37 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { /** Filtering settings (evtCount is unused) */ PMEVTYPER_t filter; - /** 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; - }; + protected: /* Configuration */ + /** PmuEvent currently in use (if any) **/ + PMUEvent *sourceEvent; - /** - * 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); + /** 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 + 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? @@ -398,7 +507,6 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { return id == PMCCNTR ? cycleCounter : counters[id]; } - /** * Return the state of a counter. * @@ -422,7 +530,7 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { * @param id ID of counter within the PMU. * @param ctr Reference to the counter's state */ - void updateCounter(CounterId id, CounterState &ctr); + void updateCounter(CounterState &ctr); /** * Check if a counter's settings allow it to be counted. @@ -468,14 +576,25 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { /** 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 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; @@ -485,22 +604,9 @@ class PMU : public SimObject, public ArmISA::BaseISADevice { 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. - *
+ * List of event types supported by this PMU. */ - std::multimap pmuEventTypes; + std::map eventMap; }; } // namespace ArmISA -- cgit v1.2.3