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 <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>
This commit is contained in:
committed by
Andreas Sandberg
parent
69ac4aced0
commit
4bb053c3d2
@@ -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")
|
||||
|
||||
@@ -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<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) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PMU::isFiltered(const CounterState &ctr) const
|
||||
void
|
||||
PMU::PMUEvent::attachEvent(PMU::CounterState *user)
|
||||
{
|
||||
assert(isa);
|
||||
if (userCounters.empty()) {
|
||||
enable();
|
||||
}
|
||||
userCounters.insert(user);
|
||||
updateAttachedCounters();
|
||||
}
|
||||
|
||||
const PMEVTYPER_t filter(ctr.filter);
|
||||
const SCR scr(isa->readMiscRegNoEffect(MISCREG_SCR));
|
||||
const CPSR cpsr(isa->readMiscRegNoEffect(MISCREG_CPSR));
|
||||
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::CounterState::isFiltered() const
|
||||
{
|
||||
assert(pmu.isa);
|
||||
|
||||
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 (isFiltered(ctr))
|
||||
return;
|
||||
|
||||
// Handle the "count every 64 cycles" mode
|
||||
if (id == PMCCNTR && reg_pmcr.d) {
|
||||
clock_remainder += delta;
|
||||
delta = (clock_remainder >> 6);
|
||||
clock_remainder &= 63;
|
||||
}
|
||||
|
||||
// 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();
|
||||
if (sourceEvent) {
|
||||
sourceEvent->detachEvent(this);
|
||||
sourceEvent = nullptr;
|
||||
} else {
|
||||
debugCounter("detaching event not currently attached"
|
||||
" to any event\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PMU::updateCounter(CounterId id, CounterState &ctr)
|
||||
PMU::CounterState::attach(PMUEvent* event)
|
||||
{
|
||||
value = 0;
|
||||
sourceEvent = event;
|
||||
sourceEvent->attachEvent(this);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(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);
|
||||
ctr.getCounterId(), 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
value += delta;
|
||||
if (isFiltered())
|
||||
return value_until_overflow;
|
||||
|
||||
// Overflow if the msb goes from 1 to 0
|
||||
return (old_value & msb) && !(value & msb);
|
||||
if (resetValue) {
|
||||
delta = 0;
|
||||
resetValue = false;
|
||||
} else {
|
||||
value += delta;
|
||||
}
|
||||
|
||||
if (delta > value_until_overflow) {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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,73 +267,134 @@ class PMU : public SimObject, public ArmISA::BaseISADevice {
|
||||
void setCounterTypeRegister(CounterId id, PMEVTYPER_t type);
|
||||
|
||||
protected: /* Probe handling and counter state */
|
||||
class ProbeListener : public ProbeListenerArgBase<uint64_t>
|
||||
{
|
||||
public:
|
||||
ProbeListener(PMU &_pmu, CounterId _id,
|
||||
ProbeManager *pm, const std::string &name)
|
||||
: ProbeListenerArgBase(pm, name),
|
||||
pmu(_pmu), id(_id) {}
|
||||
struct CounterState;
|
||||
|
||||
void notify(const uint64_t &val) override
|
||||
{
|
||||
pmu.handleEvent(id, val);
|
||||
/**
|
||||
* 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:
|
||||
PMU &pmu;
|
||||
const CounterId id;
|
||||
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);
|
||||
};
|
||||
typedef std::unique_ptr<ProbeListener> ProbeListenerUPtr;
|
||||
|
||||
/**
|
||||
* Event type configuration
|
||||
* Obtain the event of a given id
|
||||
*
|
||||
* 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.
|
||||
* @param the id of the event to obtain
|
||||
* @return a pointer to the event with id eventId
|
||||
*/
|
||||
struct EventType {
|
||||
/**
|
||||
* @param _obj Target SimObject
|
||||
* @param _name Probe name
|
||||
*/
|
||||
EventType(SimObject *_obj, const std::string &_name)
|
||||
: obj(_obj), name(_name) {}
|
||||
PMUEvent* getEvent(uint64_t eventId);
|
||||
|
||||
/**
|
||||
* Create and attach a probe used to drive this event.
|
||||
*
|
||||
* @param pmu PMU owning the probe.
|
||||
* @param CounterID counter ID within the PMU.
|
||||
* @return Pointer to a probe listener.
|
||||
*/
|
||||
std::unique_ptr<ProbeListener> create(PMU &pmu, CounterId cid) const
|
||||
{
|
||||
std::unique_ptr<ProbeListener> ptr;
|
||||
ptr.reset(new ProbeListener(pmu, cid,
|
||||
obj->getProbeManager(), name));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/** SimObject being measured by this probe */
|
||||
SimObject *const obj;
|
||||
/** Probe name within obj */
|
||||
const std::string name;
|
||||
|
||||
private:
|
||||
// Disable the default constructor
|
||||
EventType();
|
||||
};
|
||||
|
||||
/** State of a counter within the PMU. */
|
||||
/** State of a counter within the PMU. **/
|
||||
struct CounterState : public Serializable {
|
||||
CounterState()
|
||||
: eventId(0), filter(0), value(0), enabled(false),
|
||||
overflow64(false) {
|
||||
|
||||
listeners.reserve(4);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
bool add(uint64_t delta);
|
||||
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 */
|
||||
@@ -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<ProbeListenerUPtr> 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 <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?
|
||||
@@ -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<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;
|
||||
|
||||
@@ -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:
|
||||
* <ul>
|
||||
* <li>Some events can are increment by multiple different probe
|
||||
* points (e.g., the CPU memory access counter gets
|
||||
* incremented for both loads and stores).
|
||||
*
|
||||
* <li>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.
|
||||
* </ul>
|
||||
* List of event types supported by this PMU.
|
||||
*/
|
||||
std::multimap<EventTypeId, EventType> pmuEventTypes;
|
||||
std::map<EventTypeId, PMUEvent*> eventMap;
|
||||
};
|
||||
|
||||
} // namespace ArmISA
|
||||
|
||||
Reference in New Issue
Block a user