arm: Add support for filtering in the PMU
authorAndreas Sandberg <andreas.sandberg@arm.com>
Tue, 23 Dec 2014 14:31:17 +0000 (09:31 -0500)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Tue, 23 Dec 2014 14:31:17 +0000 (09:31 -0500)
This patch adds support for filtering events in the PMU. In order to
do so, it updates the ISADevice base class to forward an ISA pointer
to ISA devices. This enables such devices to access the MiscReg file
to determine the current execution level.

src/arch/arm/isa.cc
src/arch/arm/isa_device.cc
src/arch/arm/isa_device.hh
src/arch/arm/pmu.cc
src/arch/arm/pmu.hh

index d97c03db57a0900693fdc4d1b413db623f6580db..6bbd55195e7d215b71a3268e1bff38259e550cd2 100644 (file)
@@ -139,6 +139,9 @@ ISA::ISA(Params *p)
     if (!pmu)
         pmu = &dummyDevice;
 
+    // Give all ISA devices a pointer to this ISA
+    pmu->setISA(this);
+
     system = dynamic_cast<ArmSystem *>(p->system);
     DPRINTFN("ISA system set to: %p %p\n", system, p->system);
 
index 6fb58c6e128f89cf7ddc6e045a5deac2ff821b85..0bb488dac032b8366e17997100234ad59302d9ef 100644 (file)
 namespace ArmISA
 {
 
+BaseISADevice::BaseISADevice()
+    : isa(nullptr)
+{
+}
+
+void
+BaseISADevice::setISA(ISA *_isa)
+{
+    assert(_isa);
+
+    isa = _isa;
+}
+
 void
 DummyISADevice::setMiscReg(int misc_reg, MiscReg val)
 {
index edf43f1cb867d1b4ca727925ad55a99f206d79a1..8b12fa50222fda087ad0d4607e6301155f775a69 100644 (file)
@@ -46,6 +46,8 @@
 namespace ArmISA
 {
 
+class ISA;
+
 /**
  * Base class for devices that use the MiscReg interfaces.
  *
@@ -56,9 +58,11 @@ namespace ArmISA
 class BaseISADevice
 {
   public:
-    BaseISADevice() {}
+    BaseISADevice();
     virtual ~BaseISADevice() {}
 
+    virtual void setISA(ISA *isa);
+
     /**
      * Write to a system register belonging to this device.
      *
@@ -74,6 +78,9 @@ class BaseISADevice
      * @return Register value.
      */
     virtual MiscReg readMiscReg(int misc_reg) = 0;
+
+  protected:
+    ISA *isa;
 };
 
 /**
index 0fc903e9d656ea59db6ec37aa9debc7eec8a4e4d..d20f4e27d68de561aa514a58af055b2778c73391 100644 (file)
@@ -41,6 +41,8 @@
 
 #include "arch/arm/pmu.hh"
 
+#include "arch/arm/isa.hh"
+#include "arch/arm/utility.hh"
 #include "base/trace.hh"
 #include "cpu/base.hh"
 #include "debug/Checkpoint.hh"
@@ -350,12 +352,44 @@ PMU::updateAllCounters()
     }
 }
 
+bool
+PMU::isFiltered(const CounterState &ctr) const
+{
+    assert(isa);
+
+    const PMEVTYPER_t filter(ctr.filter);
+    const SCR scr(isa->readMiscRegNoEffect(MISCREG_SCR));
+    const CPSR cpsr(isa->readMiscRegNoEffect(MISCREG_CPSR));
+    const ExceptionLevel el(opModeToEL((OperatingMode)(uint8_t)cpsr.mode));
+    const bool secure(inSecureState(scr, cpsr));
+
+    switch (el) {
+      case EL0:
+        return secure ? filter.u : (filter.u != filter.nsu);
+
+      case EL1:
+        return secure ? filter.p : (filter.p != filter.nsk);
+
+      case EL2:
+        return !filter.nsh;
+
+      case EL3:
+        return filter.p != filter.m;
+
+      default:
+        panic("Unexpected execution level in PMU::isFiltered.\n");
+    }
+}
+
 void
 PMU::handleEvent(CounterId id, uint64_t delta)
 {
     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;
@@ -434,9 +468,8 @@ PMU::getCounterTypeRegister(CounterId id) const
         return 0;
 
     const CounterState &cs(getCounter(id));
-    PMEVTYPER_t type(0);
+    PMEVTYPER_t type(cs.filter);
 
-    // TODO: Re-create filtering settings from counter state
     type.evtCount = cs.eventId;
 
     return type;
@@ -453,12 +486,14 @@ PMU::setCounterTypeRegister(CounterId id, PMEVTYPER_t val)
     }
 
     CounterState &ctr(getCounter(id));
-    // TODO: Handle filtering (both for general purpose counters and
-    // the cycle counter)
+    const EventTypeId old_event_id(ctr.eventId);
+
+    ctr.filter = val;
 
-    // If PMCCNTR Register, do not change event type. PMCCNTR can count
-    // processor cycles only.
-    if (id != PMCCNTR) {
+    // If PMCCNTR Register, do not change event type. PMCCNTR can
+    // count processor cycles only. If we change the event type, we
+    // 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);
     }
index 989ad76c67880571f822726836bda0e7e46ad29a..94f8c239748b4a91235fe4f441becac99993c96a 100644 (file)
@@ -323,7 +323,7 @@ class PMU : public SimObject, public ArmISA::BaseISADevice {
     /** State of a counter within the PMU. */
     struct CounterState {
         CounterState()
-            : eventId(0), value(0), enabled(false),
+            : eventId(0), filter(0), value(0), enabled(false),
               overflow64(false) {
 
             listeners.reserve(4);
@@ -344,6 +344,9 @@ class PMU : public SimObject, public ArmISA::BaseISADevice {
         /** Counter event ID */
         EventTypeId eventId;
 
+        /** Filtering settings (evtCount is unused) */
+        PMEVTYPER_t filter;
+
         /** Current value of the counter */
         uint64_t value;
 
@@ -421,6 +424,14 @@ class PMU : public SimObject, public ArmISA::BaseISADevice {
      */
     void updateCounter(CounterId id, 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..