diff options
Diffstat (limited to 'src/arch/arm/pmu.cc')
-rw-r--r-- | src/arch/arm/pmu.cc | 315 |
1 files changed, 242 insertions, 73 deletions
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 */ @@ -88,12 +93,56 @@ 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<RegularEvent*>(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) { @@ -115,6 +164,22 @@ PMU::drainResume() } 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) { DPRINTF(PMUVerbose, "setMiscReg(%s, 0x%x)\n", @@ -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 |