summaryrefslogtreecommitdiff
path: root/src/arch/arm/pmu.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/arm/pmu.cc')
-rw-r--r--src/arch/arm/pmu.cc315
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