dev, arm: Refactor and clean up the generic timer model
authorAndreas Sandberg <andreas.sandberg@arm.com>
Sat, 23 May 2015 12:46:52 +0000 (13:46 +0100)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Sat, 23 May 2015 12:46:52 +0000 (13:46 +0100)
This changeset cleans up the generic timer a bit and moves most of the
register juggling from the ISA code into a separate class in the same
source file as the rest of the generic timer. It also removes the
assumption that there is always 8 or fewer CPUs in the system. Instead
of having a fixed limit, we now instantiate per-core timers as they
are requested. This is all in preparation for other patches that add
support for virtual timers and a memory mapped interface.

src/arch/arm/isa.cc
src/arch/arm/isa.hh
src/arch/arm/system.cc
src/arch/arm/system.hh
src/dev/arm/RealView.py
src/dev/arm/generic_timer.cc
src/dev/arm/generic_timer.hh

index 4358c8b2efd3ae5b07996eb5bc7d1d9f5e7ab96d..2120c56db05c958bbd5bb3dca5bdfc2143e69e3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2014 ARM Limited
+ * Copyright (c) 2010-2015 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -45,6 +45,7 @@
 #include "cpu/base.hh"
 #include "debug/Arm.hh"
 #include "debug/MiscRegs.hh"
+#include "dev/arm/generic_timer.hh"
 #include "params/ArmISA.hh"
 #include "sim/faults.hh"
 #include "sim/stat_control.hh"
@@ -730,52 +731,14 @@ ISA::readMiscReg(int misc_reg, ThreadContext *tc)
                 return readMiscRegNoEffect(MISCREG_SCR_EL3);
             }
         }
+
       // Generic Timer registers
-      case MISCREG_CNTFRQ:
-      case MISCREG_CNTFRQ_EL0:
-        inform_once("Read CNTFREQ_EL0 frequency\n");
-        return getSystemCounter(tc)->freq();
-      case MISCREG_CNTPCT:
-      case MISCREG_CNTPCT_EL0:
-        return getSystemCounter(tc)->value();
-      case MISCREG_CNTVCT:
-        return getSystemCounter(tc)->value();
-      case MISCREG_CNTVCT_EL0:
-        return getSystemCounter(tc)->value();
-      case MISCREG_CNTP_CVAL:
-      case MISCREG_CNTP_CVAL_EL0:
-        return getArchTimer(tc, tc->cpuId())->compareValue();
-      case MISCREG_CNTP_TVAL:
-      case MISCREG_CNTP_TVAL_EL0:
-        return getArchTimer(tc, tc->cpuId())->timerValue();
-      case MISCREG_CNTP_CTL:
-      case MISCREG_CNTP_CTL_EL0:
-        return getArchTimer(tc, tc->cpuId())->control();
-      // PL1 phys. timer, secure
-      //   AArch64
-      // case MISCREG_CNTPS_CVAL_EL1:
-      // case MISCREG_CNTPS_TVAL_EL1:
-      // case MISCREG_CNTPS_CTL_EL1:
-      // PL2 phys. timer, non-secure
-      //   AArch32
-      // case MISCREG_CNTHCTL:
-      // case MISCREG_CNTHP_CVAL:
-      // case MISCREG_CNTHP_TVAL:
-      // case MISCREG_CNTHP_CTL:
-      //   AArch64
-      // case MISCREG_CNTHCTL_EL2:
-      // case MISCREG_CNTHP_CVAL_EL2:
-      // case MISCREG_CNTHP_TVAL_EL2:
-      // case MISCREG_CNTHP_CTL_EL2:
-      // Virtual timer
-      //   AArch32
-      // case MISCREG_CNTV_CVAL:
-      // case MISCREG_CNTV_TVAL:
-      // case MISCREG_CNTV_CTL:
-      //   AArch64
-      // case MISCREG_CNTV_CVAL_EL2:
-      // case MISCREG_CNTV_TVAL_EL2:
-      // case MISCREG_CNTV_CTL_EL2:
+      case MISCREG_CNTFRQ ... MISCREG_CNTHP_CTL:
+      case MISCREG_CNTPCT ... MISCREG_CNTHP_CVAL:
+      case MISCREG_CNTKCTL_EL1 ... MISCREG_CNTV_CVAL_EL0:
+      case MISCREG_CNTVOFF_EL2 ... MISCREG_CNTPS_CVAL_EL1:
+        return getGenericTimer(tc).readMiscReg(misc_reg);
+
       default:
         break;
 
@@ -1853,47 +1816,11 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc)
             break;
 
           // Generic Timer registers
-          case MISCREG_CNTFRQ:
-          case MISCREG_CNTFRQ_EL0:
-            getSystemCounter(tc)->setFreq(val);
-            break;
-          case MISCREG_CNTP_CVAL:
-          case MISCREG_CNTP_CVAL_EL0:
-            getArchTimer(tc, tc->cpuId())->setCompareValue(val);
-            break;
-          case MISCREG_CNTP_TVAL:
-          case MISCREG_CNTP_TVAL_EL0:
-            getArchTimer(tc, tc->cpuId())->setTimerValue(val);
-            break;
-          case MISCREG_CNTP_CTL:
-          case MISCREG_CNTP_CTL_EL0:
-            getArchTimer(tc, tc->cpuId())->setControl(val);
-            break;
-          // PL1 phys. timer, secure
-          //   AArch64
-          case MISCREG_CNTPS_CVAL_EL1:
-          case MISCREG_CNTPS_TVAL_EL1:
-          case MISCREG_CNTPS_CTL_EL1:
-          // PL2 phys. timer, non-secure
-          //   AArch32
-          case MISCREG_CNTHCTL:
-          case MISCREG_CNTHP_CVAL:
-          case MISCREG_CNTHP_TVAL:
-          case MISCREG_CNTHP_CTL:
-          //   AArch64
-          case MISCREG_CNTHCTL_EL2:
-          case MISCREG_CNTHP_CVAL_EL2:
-          case MISCREG_CNTHP_TVAL_EL2:
-          case MISCREG_CNTHP_CTL_EL2:
-          // Virtual timer
-          //   AArch32
-          case MISCREG_CNTV_CVAL:
-          case MISCREG_CNTV_TVAL:
-          case MISCREG_CNTV_CTL:
-          //   AArch64
-          // case MISCREG_CNTV_CVAL_EL2:
-          // case MISCREG_CNTV_TVAL_EL2:
-          // case MISCREG_CNTV_CTL_EL2:
+          case MISCREG_CNTFRQ ... MISCREG_CNTHP_CTL:
+          case MISCREG_CNTPCT ... MISCREG_CNTHP_CVAL:
+          case MISCREG_CNTKCTL_EL1 ... MISCREG_CNTV_CVAL_EL0:
+          case MISCREG_CNTVOFF_EL2 ... MISCREG_CNTPS_CVAL_EL1:
+            getGenericTimer(tc).setMiscReg(misc_reg, newVal);
             break;
         }
     }
@@ -1988,26 +1915,23 @@ ISA::tlbiMVA(ThreadContext *tc, MiscReg newVal, bool secure_lookup, bool hyp,
     }
 }
 
-::GenericTimer::SystemCounter *
-ISA::getSystemCounter(ThreadContext *tc)
+BaseISADevice &
+ISA::getGenericTimer(ThreadContext *tc)
 {
-    ::GenericTimer::SystemCounter *cnt = ((ArmSystem *) tc->getSystemPtr())->
-        getSystemCounter();
-    if (cnt == NULL) {
-        panic("System counter not available\n");
+    // We only need to create an ISA interface the first time we try
+    // to access the timer.
+    if (timer)
+        return *timer.get();
+
+    assert(system);
+    GenericTimer *generic_timer(system->getGenericTimer());
+    if (!generic_timer) {
+        panic("Trying to get a generic timer from a system that hasn't "
+              "been configured to use a generic timer.\n");
     }
-    return cnt;
-}
 
-::GenericTimer::ArchTimer *
-ISA::getArchTimer(ThreadContext *tc, int cpu_id)
-{
-    ::GenericTimer::ArchTimer *timer = ((ArmSystem *) tc->getSystemPtr())->
-        getArchTimer(cpu_id);
-    if (timer == NULL) {
-        panic("Architected timer not available\n");
-    }
-    return timer;
+    timer.reset(new GenericTimerISA(*generic_timer, tc->cpuId()));
+    return *timer.get();
 }
 
 }
index fd9801ae205c7f2654cad4ed58c87a4ca4bb6705..11f25de6d62fa12620a3f2ab81c2c05ba2c619c1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2014 ARM Limited
+ * Copyright (c) 2010, 2012-2015 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -49,7 +49,6 @@
 #include "arch/arm/tlb.hh"
 #include "arch/arm/types.hh"
 #include "debug/Checkpoint.hh"
-#include "dev/arm/generic_timer.hh"
 #include "sim/sim_object.hh"
 
 struct ArmISAParams;
@@ -139,6 +138,9 @@ namespace ArmISA
         // PMU belonging to this ISA
         BaseISADevice *pmu;
 
+        // Generic timer interface belonging to this ISA
+        std::unique_ptr<BaseISADevice> timer;
+
         // Cached copies of system-level properties
         bool haveSecurity;
         bool haveLPAE;
@@ -205,9 +207,7 @@ namespace ArmISA
             }
         }
 
-        ::GenericTimer::SystemCounter * getSystemCounter(ThreadContext *tc);
-        ::GenericTimer::ArchTimer * getArchTimer(ThreadContext *tc,
-                                                 int cpu_id);
+        BaseISADevice &getGenericTimer(ThreadContext *tc);
 
 
       private:
index eebb220d842499ac282611f5c96123dcf6c38f26..a7db9ca1be2e8408e4c5f6c0b7cab0092513f3f0 100644 (file)
@@ -151,24 +151,6 @@ ArmSystem::initState()
     }
 }
 
-GenericTimer::ArchTimer *
-ArmSystem::getArchTimer(int cpu_id) const
-{
-    if (_genericTimer) {
-        return _genericTimer->getArchTimer(cpu_id);
-    }
-    return NULL;
-}
-
-GenericTimer::SystemCounter *
-ArmSystem::getSystemCounter() const
-{
-    if (_genericTimer) {
-        return _genericTimer->getSystemCounter();
-    }
-    return NULL;
-}
-
 bool
 ArmSystem::haveSecurity(ThreadContext *tc)
 {
index 0937f6376151f34c8b2f7169745cfa8c75a30286..3add01e611383e951414bad665ce9dfab144dc83 100644 (file)
 #include <string>
 #include <vector>
 
-#include "dev/arm/generic_timer.hh"
 #include "kern/linux/events.hh"
 #include "params/ArmSystem.hh"
 #include "params/GenericArmSystem.hh"
 #include "sim/sim_object.hh"
 #include "sim/system.hh"
 
+class GenericTimer;
 class ThreadContext;
 
 class ArmSystem : public System
@@ -166,11 +166,8 @@ class ArmSystem : public System
         _genericTimer = generic_timer;
     }
 
-    /** Returns a pointer to the system counter. */
-    GenericTimer::SystemCounter *getSystemCounter() const;
-
-    /** Returns a pointer to the appropriate architected timer. */
-    GenericTimer::ArchTimer *getArchTimer(int cpu_id) const;
+    /** Get a pointer to the system's generic timer model */
+    GenericTimer *getGenericTimer() const { return _genericTimer; }
 
     /** Returns true if the register width of the highest implemented exception
      * level is 64 bits (ARMv8) */
index 9ff642cb1db0448bc21ade0e57ca0d4189bb44ef..2e58c8fa6fbf0dca98d2e7157353245aaf8e50ad 100644 (file)
@@ -136,7 +136,7 @@ class GenericTimer(SimObject):
     cxx_header = "dev/arm/generic_timer.hh"
     system = Param.System(Parent.any, "system")
     gic = Param.BaseGic(Parent.any, "GIC to use for interrupting")
-    int_num = Param.UInt32("Interrupt number used per-cpu to GIC")
+    int_phys = Param.UInt32("Interrupt number used per-cpu to GIC")
     # @todo: for now only one timer per CPU is supported, which is the
     # normal behaviour when Security and Virt. extensions are disabled.
 
@@ -457,7 +457,7 @@ class VExpress_EMM(RealView):
                                idreg=0x02250000, pio_addr=0x1C010000)
     gic = Pl390(dist_addr=0x2C001000, cpu_addr=0x2C002000)
     local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, pio_addr=0x2C080000)
-    generic_timer = GenericTimer(int_num=29)
+    generic_timer = GenericTimer(int_phys=29)
     timer0 = Sp804(int_num0=34, int_num1=34, pio_addr=0x1C110000, clock0='1MHz', clock1='1MHz')
     timer1 = Sp804(int_num0=35, int_num1=35, pio_addr=0x1C120000, clock0='1MHz', clock1='1MHz')
     clcd   = Pl111(pio_addr=0x1c1f0000, int_num=46)
index 555c1050f87af5b5197409b7d309e5afdc8afc5a..64220570437e746bf44bb66af611b7bf8cfa053b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 ARM Limited
+ * Copyright (c) 2013, 2015 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors: Giacomo Gabrielli
+ *          Andreas Sandberg
  */
 
+#include "dev/arm/generic_timer.hh"
+
 #include "arch/arm/system.hh"
-#include "debug/Checkpoint.hh"
 #include "debug/Timer.hh"
 #include "dev/arm/base_gic.hh"
-#include "dev/arm/generic_timer.hh"
+#include "params/GenericTimer.hh"
+
+SystemCounter::SystemCounter()
+    : _freq(0), _period(0), _resetTick(0), _regCntkctl(0)
+{
+    setFreq(0x01800000);
+}
 
 void
-GenericTimer::SystemCounter::setFreq(uint32_t freq)
+SystemCounter::setFreq(uint32_t freq)
 {
     if (_freq != 0) {
         // Altering the frequency after boot shouldn't be done in practice.
@@ -56,147 +64,389 @@ GenericTimer::SystemCounter::setFreq(uint32_t freq)
 }
 
 void
-GenericTimer::SystemCounter::serialize(std::ostream &os)
+SystemCounter::serialize(std::ostream &os) const
 {
+    SERIALIZE_SCALAR(_regCntkctl);
     SERIALIZE_SCALAR(_freq);
     SERIALIZE_SCALAR(_period);
     SERIALIZE_SCALAR(_resetTick);
 }
 
 void
-GenericTimer::SystemCounter::unserialize(Checkpoint *cp,
-                                         const std::string &section)
+SystemCounter::unserialize(Checkpoint *cp,
+                           const std::string &section)
 {
+    // We didn't handle CNTKCTL in this class before, assume it's zero
+    // if it isn't present.
+    if (!UNSERIALIZE_OPT_SCALAR(_regCntkctl))
+        _regCntkctl = 0;
     UNSERIALIZE_SCALAR(_freq);
     UNSERIALIZE_SCALAR(_period);
     UNSERIALIZE_SCALAR(_resetTick);
 }
 
+
+
+ArchTimer::ArchTimer(const std::string &name,
+                     SimObject &parent,
+                     SystemCounter &sysctr,
+                     const Interrupt &interrupt)
+    : _name(name), _parent(parent), _systemCounter(sysctr),
+      _interrupt(interrupt),
+      _control(0), _counterLimit(0),
+      _counterLimitReachedEvent(this)
+{
+}
+
 void
-GenericTimer::ArchTimer::counterLimitReached()
+ArchTimer::counterLimitReached()
 {
     _control.istatus = 1;
 
     if (!_control.enable)
         return;
 
-    // DPRINTF(Timer, "Counter limit reached\n");
-
+    DPRINTF(Timer, "Counter limit reached\n");
     if (!_control.imask) {
-        // DPRINTF(Timer, "Causing interrupt\n");
-        _parent->_gic->sendPPInt(_intNum, _cpuNum);
+        DPRINTF(Timer, "Causing interrupt\n");
+        _interrupt.send();
     }
 }
 
 void
-GenericTimer::ArchTimer::setCompareValue(uint64_t val)
+ArchTimer::updateCounter()
 {
-    _counterLimit = val;
     if (_counterLimitReachedEvent.scheduled())
-        _parent->deschedule(_counterLimitReachedEvent);
-    if (counterValue() >= _counterLimit) {
+        _parent.deschedule(_counterLimitReachedEvent);
+    if (value() >= _counterLimit) {
         counterLimitReached();
     } else {
+        const auto period(_systemCounter.period());
         _control.istatus = 0;
-        _parent->schedule(_counterLimitReachedEvent,
-             curTick() + (_counterLimit - counterValue()) * _counter->period());
+        _parent.schedule(_counterLimitReachedEvent,
+             curTick() + (_counterLimit - value()) * period);
     }
 }
 
 void
-GenericTimer::ArchTimer::setTimerValue(uint32_t val)
+ArchTimer::setCompareValue(uint64_t val)
+{
+    _counterLimit = val;
+    updateCounter();
+}
+
+void
+ArchTimer::setTimerValue(uint32_t val)
 {
-    setCompareValue(counterValue() + sext<32>(val));
+    setCompareValue(value() + sext<32>(val));
 }
 
 void
-GenericTimer::ArchTimer::setControl(uint32_t val)
+ArchTimer::setControl(uint32_t val)
 {
     ArchTimerCtrl new_ctl = val;
     if ((new_ctl.enable && !new_ctl.imask) &&
         !(_control.enable && !_control.imask)) {
         // Re-evalute the timer condition
-        if (_counterLimit >= counterValue()) {
+        if (_counterLimit >= value()) {
             _control.istatus = 1;
 
             DPRINTF(Timer, "Causing interrupt in control\n");
-            //_parent->_gic->sendPPInt(_intNum, _cpuNum);
+            //_interrupt.send();
         }
     }
     _control.enable = new_ctl.enable;
     _control.imask = new_ctl.imask;
 }
 
+uint64_t
+ArchTimer::value() const
+{
+    return _systemCounter.value();
+}
+
 void
-GenericTimer::ArchTimer::serialize(std::ostream &os)
+ArchTimer::serialize(std::ostream &os) const
 {
-    SERIALIZE_SCALAR(_cpuNum);
-    SERIALIZE_SCALAR(_intNum);
-    uint32_t control_serial = _control;
-    SERIALIZE_SCALAR(control_serial);
+    paramOut(os, "control_serial", _control);
     SERIALIZE_SCALAR(_counterLimit);
-    bool event_scheduled = _counterLimitReachedEvent.scheduled();
+
+    const bool event_scheduled(_counterLimitReachedEvent.scheduled());
     SERIALIZE_SCALAR(event_scheduled);
-    Tick event_time;
     if (event_scheduled) {
-        event_time = _counterLimitReachedEvent.when();
+        const Tick event_time(_counterLimitReachedEvent.when());
         SERIALIZE_SCALAR(event_time);
     }
 }
 
 void
-GenericTimer::ArchTimer::unserialize(Checkpoint *cp, const std::string &section)
+ArchTimer::unserialize(Checkpoint *cp,
+                                         const std::string &section)
 {
-    UNSERIALIZE_SCALAR(_cpuNum);
-    UNSERIALIZE_SCALAR(_intNum);
-    uint32_t control_serial;
-    UNSERIALIZE_SCALAR(control_serial);
-    _control = control_serial;
+    paramIn(cp, section, "control_serial", _control);
     bool event_scheduled;
     UNSERIALIZE_SCALAR(event_scheduled);
-    Tick event_time;
     if (event_scheduled) {
+        Tick event_time;
         UNSERIALIZE_SCALAR(event_time);
-        _parent->schedule(_counterLimitReachedEvent, event_time);
+        _parent.schedule(_counterLimitReachedEvent, event_time);
     }
 }
 
-GenericTimer::GenericTimer(Params *p)
-    : SimObject(p), _gic(p->gic)
+void
+ArchTimer::Interrupt::send()
 {
-   for (int i = 0; i < CPU_MAX; ++i) {
-        std::stringstream oss;
-        oss << name() << ".arch_timer" << i;
-        _archTimers[i]._name = oss.str();
-        _archTimers[i]._parent = this;
-        _archTimers[i]._counter = &_systemCounter;
-        _archTimers[i]._cpuNum = i;
-        _archTimers[i]._intNum = p->int_num;
-   }
+    if (_ppi) {
+        _gic.sendPPInt(_irq, _cpu);
+    } else {
+        _gic.sendInt(_irq);
+    }
+}
+
+
+void
+ArchTimer::Interrupt::clear()
+{
+    if (_ppi) {
+        _gic.clearPPInt(_irq, _cpu);
+    } else {
+        _gic.clearInt(_irq);
+    }
+}
+
 
-   ((ArmSystem *) p->system)->setGenericTimer(this);
+GenericTimer::GenericTimer(GenericTimerParams *p)
+    : SimObject(p),
+      gic(p->gic),
+      irqPhys(p->int_phys)
+{
+    dynamic_cast<ArmSystem &>(*p->system).setGenericTimer(this);
 }
 
 void
 GenericTimer::serialize(std::ostream &os)
 {
+    paramOut(os, "cpu_count", timers.size());
+
     nameOut(os, csprintf("%s.sys_counter", name()));
-    _systemCounter.serialize(os);
-    for (int i = 0; i < CPU_MAX; ++i) {
-        nameOut(os, csprintf("%s.arch_timer%d", name(), i));
-        _archTimers[i].serialize(os);
+    systemCounter.serialize(os);
+
+    for (int i = 0; i < timers.size(); ++i) {
+        CoreTimers &core(getTimers(i));
+
+        nameOut(os, core.phys.name());
+        core.phys.serialize(os);
     }
 }
 
 void
 GenericTimer::unserialize(Checkpoint *cp, const std::string &section)
 {
-    _systemCounter.unserialize(cp, csprintf("%s.sys_counter", section));
-    for (int i = 0; i < CPU_MAX; ++i) {
-        _archTimers[i].unserialize(cp, csprintf("%s.arch_timer%d", section, i));
+    systemCounter.unserialize(cp, csprintf("%s.sys_counter", section));
+
+    // Try to unserialize the CPU count. Old versions of the timer
+    // model assumed a 8 CPUs, so we fall back to that if the field
+    // isn't present.
+    static const unsigned OLD_CPU_MAX = 8;
+    unsigned cpu_count;
+    if (!UNSERIALIZE_OPT_SCALAR(cpu_count)) {
+        warn("Checkpoint does not contain CPU count, assuming %i CPUs\n",
+             OLD_CPU_MAX);
+        cpu_count = OLD_CPU_MAX;
+    }
+
+    for (int i = 0; i < cpu_count; ++i) {
+        CoreTimers &core(getTimers(i));
+        // This should really be phys_timerN, but we are stuck with
+        // arch_timer for backwards compatibility.
+        core.phys.unserialize(cp, csprintf("%s.arch_timer%d", section, i));
     }
 }
 
+
+GenericTimer::CoreTimers &
+GenericTimer::getTimers(int cpu_id)
+{
+    if (cpu_id >= timers.size())
+        createTimers(cpu_id + 1);
+
+    return *timers[cpu_id];
+}
+
+void
+GenericTimer::createTimers(unsigned cpus)
+{
+    assert(timers.size() < cpus);
+
+    const unsigned old_cpu_count(timers.size());
+    timers.resize(cpus);
+    for (unsigned i = old_cpu_count; i < cpus; ++i) {
+        timers[i].reset(
+            new CoreTimers(*this, i, irqPhys));
+    }
+}
+
+
+void
+GenericTimer::setMiscReg(int reg, unsigned cpu, MiscReg val)
+{
+    CoreTimers &core(getTimers(cpu));
+
+    switch (reg) {
+      case MISCREG_CNTFRQ:
+      case MISCREG_CNTFRQ_EL0:
+        systemCounter.setFreq(val);
+        return;
+
+      case MISCREG_CNTKCTL:
+      case MISCREG_CNTKCTL_EL1:
+        systemCounter.setKernelControl(val);
+        return;
+
+      // Physical timer
+      case MISCREG_CNTP_CVAL:
+      case MISCREG_CNTP_CVAL_NS:
+      case MISCREG_CNTP_CVAL_EL0:
+        core.phys.setCompareValue(val);
+        return;
+
+      case MISCREG_CNTP_TVAL:
+      case MISCREG_CNTP_TVAL_NS:
+      case MISCREG_CNTP_TVAL_EL0:
+        core.phys.setTimerValue(val);
+        return;
+
+      case MISCREG_CNTP_CTL:
+      case MISCREG_CNTP_CTL_NS:
+      case MISCREG_CNTP_CTL_EL0:
+        core.phys.setControl(val);
+        return;
+
+      // Count registers
+      case MISCREG_CNTPCT:
+      case MISCREG_CNTPCT_EL0:
+      case MISCREG_CNTVCT:
+      case MISCREG_CNTVCT_EL0:
+        warn("Ignoring write to read only count register: %s\n",
+             miscRegName[reg]);
+        return;
+
+      // Virtual timer
+      case MISCREG_CNTVOFF:
+      case MISCREG_CNTVOFF_EL2:
+      case MISCREG_CNTV_CVAL:
+      case MISCREG_CNTV_CVAL_EL0:
+      case MISCREG_CNTV_TVAL:
+      case MISCREG_CNTV_TVAL_EL0:
+      case MISCREG_CNTV_CTL:
+      case MISCREG_CNTV_CTL_EL0:
+        /* FALLTHROUGH */
+
+      // PL1 phys. timer, secure
+      case MISCREG_CNTP_CTL_S:
+      case MISCREG_CNTPS_CVAL_EL1:
+      case MISCREG_CNTPS_TVAL_EL1:
+      case MISCREG_CNTPS_CTL_EL1:
+        /* FALLTHROUGH */
+
+      // PL2 phys. timer, non-secure
+      case MISCREG_CNTHCTL:
+      case MISCREG_CNTHCTL_EL2:
+      case MISCREG_CNTHP_CVAL:
+      case MISCREG_CNTHP_CVAL_EL2:
+      case MISCREG_CNTHP_TVAL:
+      case MISCREG_CNTHP_TVAL_EL2:
+      case MISCREG_CNTHP_CTL:
+      case MISCREG_CNTHP_CTL_EL2:
+        warn("Writing to unimplemented register: %s\n",
+             miscRegName[reg]);
+        return;
+
+      default:
+        warn("Writing to unknown register: %s\n", miscRegName[reg]);
+        return;
+    }
+}
+
+
+MiscReg
+GenericTimer::readMiscReg(int reg, unsigned cpu)
+{
+    CoreTimers &core(getTimers(cpu));
+
+    switch (reg) {
+      case MISCREG_CNTFRQ:
+      case MISCREG_CNTFRQ_EL0:
+        return systemCounter.freq();
+
+      case MISCREG_CNTKCTL:
+      case MISCREG_CNTKCTL_EL1:
+        return systemCounter.getKernelControl();
+
+      // Physical timer
+      case MISCREG_CNTP_CVAL:
+      case MISCREG_CNTP_CVAL_EL0:
+        return core.phys.compareValue();
+
+      case MISCREG_CNTP_TVAL:
+      case MISCREG_CNTP_TVAL_EL0:
+        return core.phys.timerValue();
+
+      case MISCREG_CNTP_CTL:
+      case MISCREG_CNTP_CTL_EL0:
+      case MISCREG_CNTP_CTL_NS:
+        return core.phys.control();
+
+      case MISCREG_CNTPCT:
+      case MISCREG_CNTPCT_EL0:
+        return core.phys.value();
+
+
+      // Virtual timer
+      case MISCREG_CNTVCT:
+      case MISCREG_CNTVCT_EL0:
+        warn_once("Virtual timer not implemented, "
+                  "returning physical timer value\n");
+        return core.phys.value();
+
+      case MISCREG_CNTVOFF:
+      case MISCREG_CNTVOFF_EL2:
+      case MISCREG_CNTV_CVAL:
+      case MISCREG_CNTV_CVAL_EL0:
+      case MISCREG_CNTV_TVAL:
+      case MISCREG_CNTV_TVAL_EL0:
+      case MISCREG_CNTV_CTL:
+      case MISCREG_CNTV_CTL_EL0:
+        /* FALLTHROUGH */
+
+      // PL1 phys. timer, secure
+      case MISCREG_CNTP_CTL_S:
+      case MISCREG_CNTPS_CVAL_EL1:
+      case MISCREG_CNTPS_TVAL_EL1:
+      case MISCREG_CNTPS_CTL_EL1:
+        /* FALLTHROUGH */
+
+      // PL2 phys. timer, non-secure
+      case MISCREG_CNTHCTL:
+      case MISCREG_CNTHCTL_EL2:
+      case MISCREG_CNTHP_CVAL:
+      case MISCREG_CNTHP_CVAL_EL2:
+      case MISCREG_CNTHP_TVAL:
+      case MISCREG_CNTHP_TVAL_EL2:
+      case MISCREG_CNTHP_CTL:
+      case MISCREG_CNTHP_CTL_EL2:
+        warn("Reading from unimplemented register: %s\n",
+             miscRegName[reg]);
+        return 0;
+
+
+      default:
+        warn("Reading from unknown register: %s\n", miscRegName[reg]);
+        return 0;
+    }
+}
+
+
 GenericTimer *
 GenericTimerParams::create()
 {
index bc43f8b3b61d6561f31a14c29610a155e4c5b538..8dc921275eef8596b2bace925d7e6332418a6ad6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 ARM Limited
+ * Copyright (c) 2013, 2015 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors: Giacomo Gabrielli
+ *          Andreas Sandberg
  */
 
 #ifndef __DEV_ARM_GENERIC_TIMER_HH__
 #define __DEV_ARM_GENERIC_TIMER_HH__
 
+#include "arch/arm/isa_device.hh"
 #include "base/bitunion.hh"
-#include "params/GenericTimer.hh"
+#include "dev/arm/base_gic.hh"
 #include "sim/core.hh"
 #include "sim/sim_object.hh"
 
 /// ARM, Issue C, Chapter 17).
 
 class Checkpoint;
-class BaseGic;
+class GenericTimerParams;
 
-/// Wrapper around the actual counters and timers of the Generic Timer
-/// extension.
-class GenericTimer : public SimObject
+/// Global system counter.  It is shared by the architected timers.
+/// @todo: implement memory-mapped controls
+class SystemCounter
 {
+  protected:
+    /// Counter frequency (as specified by CNTFRQ).
+    uint64_t _freq;
+    /// Cached copy of the counter period (inverse of the frequency).
+    Tick _period;
+    /// Tick when the counter was reset.
+    Tick _resetTick;
+
+    uint32_t _regCntkctl;
+
   public:
+    SystemCounter();
 
-    /// Global system counter.  It is shared by the architected timers.
-    /// @todo: implement memory-mapped controls
-    class SystemCounter
+    /// Returns the current value of the physical counter.
+    uint64_t value() const
     {
-      protected:
-        /// Counter frequency (as specified by CNTFRQ).
-        uint64_t _freq;
-        /// Cached copy of the counter period (inverse of the frequency).
-        Tick _period;
-        /// Tick when the counter was reset.
-        Tick _resetTick;
+        if (_freq == 0)
+            return 0;  // Counter is still off.
+        return (curTick() - _resetTick) / _period;
+    }
+
+    /// Returns the counter frequency.
+    uint64_t freq() const { return _freq; }
+    /// Sets the counter frequency.
+    /// @param freq frequency in Hz.
+    void setFreq(uint32_t freq);
+
+    /// Returns the counter period.
+    Tick period() const { return _period; }
 
+    void setKernelControl(uint32_t val) { _regCntkctl = val; }
+    uint32_t getKernelControl() { return _regCntkctl; }
+
+    void serialize(std::ostream &os) const;
+    void unserialize(Checkpoint *cp, const std::string &section);
+
+  private:
+    // Disable copying
+    SystemCounter(const SystemCounter &c);
+};
+
+/// Per-CPU architected timer.
+class ArchTimer
+{
+  public:
+    class Interrupt
+    {
       public:
-        /// Ctor.
-        SystemCounter()
-            : _freq(0), _period(0), _resetTick(0)
-        {
-            setFreq(0x01800000);
-        }
-
-        /// Returns the current value of the physical counter.
-        uint64_t value() const
-        {
-            if (_freq == 0)
-                return 0;  // Counter is still off.
-            return (curTick() - _resetTick) / _period;
-        }
-
-        /// Returns the counter frequency.
-        uint64_t freq() const { return _freq; }
-        /// Sets the counter frequency.
-        /// @param freq frequency in Hz.
-        void setFreq(uint32_t freq);
-
-        /// Returns the counter period.
-        Tick period() const { return _period; }
-
-        void serialize(std::ostream &os);
-        void unserialize(Checkpoint *cp, const std::string &section);
+        Interrupt(BaseGic &gic, unsigned irq)
+            : _gic(gic), _ppi(false), _irq(irq), _cpu(0) {}
+
+        Interrupt(BaseGic &gic, unsigned irq, unsigned cpu)
+            : _gic(gic), _ppi(true), _irq(irq), _cpu(cpu) {}
+
+        void send();
+        void clear();
+
+      private:
+        BaseGic &_gic;
+        const bool _ppi;
+        const unsigned _irq;
+        const unsigned _cpu;
     };
 
-    /// Per-CPU architected timer.
-    class ArchTimer
-    {
-      protected:
-        /// Control register.
-        BitUnion32(ArchTimerCtrl)
-            Bitfield<0> enable;
-            Bitfield<1> imask;
-            Bitfield<2> istatus;
-        EndBitUnion(ArchTimerCtrl)
-
-        /// Name of this timer.
-        std::string _name;
-        /// Pointer to parent class.
-        GenericTimer *_parent;
-        /// Pointer to the global system counter.
-        SystemCounter *_counter;
-        /// ID of the CPU this timer is attached to.
-        int _cpuNum;
-        /// ID of the interrupt to be triggered.
-        int _intNum;
-        /// Cached value of the control register ({CNTP/CNTHP/CNTV}_CTL).
-        ArchTimerCtrl _control;
-        /// Programmed limit value for the upcounter ({CNTP/CNTHP/CNTV}_CVAL).
-        uint64_t _counterLimit;
-
-        /// Called when the upcounter reaches the programmed value.
-        void counterLimitReached();
-        EventWrapper<ArchTimer, &ArchTimer::counterLimitReached>
-            _counterLimitReachedEvent;
-
-        /// Returns the value of the counter which this timer relies on.
-        uint64_t counterValue() const { return _counter->value(); }
+  protected:
+    /// Control register.
+    BitUnion32(ArchTimerCtrl)
+    Bitfield<0> enable;
+    Bitfield<1> imask;
+    Bitfield<2> istatus;
+    EndBitUnion(ArchTimerCtrl)
 
-      public:
-        /// Ctor.
-        ArchTimer()
-            : _control(0), _counterLimit(0), _counterLimitReachedEvent(this)
-        {}
+    /// Name of this timer.
+    const std::string _name;
 
-        /// Returns the timer name.
-        std::string name() const { return _name; }
+    /// Pointer to parent class.
+    SimObject &_parent;
 
-        /// Returns the CompareValue view of the timer.
-        uint64_t compareValue() const { return _counterLimit; }
-        /// Sets the CompareValue view of the timer.
-        void setCompareValue(uint64_t val);
+    SystemCounter &_systemCounter;
 
-        /// Returns the TimerValue view of the timer.
-        uint32_t timerValue() const { return _counterLimit - counterValue(); }
-        /// Sets the TimerValue view of the timer.
-        void setTimerValue(uint32_t val);
+    Interrupt _interrupt;
 
-        /// Sets the control register.
-        uint32_t control() const { return _control; }
-        void setControl(uint32_t val);
+    /// Value of the control register ({CNTP/CNTHP/CNTV}_CTL).
+    ArchTimerCtrl _control;
+    /// Programmed limit value for the upcounter ({CNTP/CNTHP/CNTV}_CVAL).
+    uint64_t _counterLimit;
 
-        virtual void serialize(std::ostream &os);
-        virtual void unserialize(Checkpoint *cp, const std::string &section);
+    /**
+     * Timer settings or the offset has changed, re-evaluate
+     * trigger condition and raise interrupt if necessary.
+     */
+    void updateCounter();
 
-        friend class GenericTimer;
-    };
+    /// Called when the upcounter reaches the programmed value.
+    void counterLimitReached();
+    EventWrapper<ArchTimer, &ArchTimer::counterLimitReached>
+    _counterLimitReachedEvent;
+
+  public:
+    ArchTimer(const std::string &name,
+              SimObject &parent,
+              SystemCounter &sysctr,
+              const Interrupt &interrupt);
+
+    /// Returns the timer name.
+    std::string name() const { return _name; }
+
+    /// Returns the CompareValue view of the timer.
+    uint64_t compareValue() const { return _counterLimit; }
+    /// Sets the CompareValue view of the timer.
+    void setCompareValue(uint64_t val);
+
+    /// Returns the TimerValue view of the timer.
+    uint32_t timerValue() const { return _counterLimit - value(); }
+    /// Sets the TimerValue view of the timer.
+    void setTimerValue(uint32_t val);
+
+    /// Sets the control register.
+    uint32_t control() const { return _control; }
+    void setControl(uint32_t val);
+
+    /// Returns the value of the counter which this timer relies on.
+    uint64_t value() const;
+
+    void serialize(std::ostream &os) const;
+    void unserialize(Checkpoint *cp, const std::string &section);
+
+  private:
+    // Disable copying
+    ArchTimer(const ArchTimer &t);
+};
+
+class GenericTimer : public SimObject
+{
+  public:
+    GenericTimer(GenericTimerParams *p);
+
+    void serialize(std::ostream &os) M5_ATTR_OVERRIDE;
+    void unserialize(Checkpoint *cp, const std::string &sec) M5_ATTR_OVERRIDE;
+
+  public:
+    void setMiscReg(int misc_reg, unsigned cpu, ArmISA::MiscReg val);
+    ArmISA::MiscReg readMiscReg(int misc_reg, unsigned cpu);
 
   protected:
+    struct CoreTimers {
+        CoreTimers(GenericTimer &parent, unsigned cpu,
+                   unsigned _irqPhys)
+            : irqPhys(*parent.gic, _irqPhys, cpu),
+              // This should really be phys_timerN, but we are stuck with
+              // arch_timer for backwards compatibility.
+              phys(csprintf("%s.arch_timer%d", parent.name(), cpu),
+                   parent, parent.systemCounter,
+                   irqPhys)
+        {}
 
-    static const int CPU_MAX = 8;
+        ArchTimer::Interrupt irqPhys;
+        ArchTimer phys;
+
+      private:
+        // Disable copying
+        CoreTimers(const CoreTimers &c);
+    };
+
+    CoreTimers &getTimers(int cpu_id);
+    void createTimers(unsigned cpus);
 
-    /// Pointer to the GIC, needed to trigger timer interrupts.
-    BaseGic *_gic;
     /// System counter.
-    SystemCounter _systemCounter;
-    /// Per-CPU architected timers.
-    // @todo: this would become a 2-dim. array with Security and Virt.
-    ArchTimer _archTimers[CPU_MAX];
+    SystemCounter systemCounter;
 
-  public:
-    typedef GenericTimerParams Params;
-    const Params *
-    params() const
-    {
-        return dynamic_cast<const Params *>(_params);
-    }
+    /// Per-CPU physical architected timers.
+    std::vector<std::unique_ptr<CoreTimers>> timers;
 
-    /// Ctor.
-    GenericTimer(Params *p);
+  protected: // Configuration
+    /// Pointer to the GIC, needed to trigger timer interrupts.
+    BaseGic *const gic;
 
-    /// Returns a pointer to the system counter.
-    SystemCounter *getSystemCounter() { return &_systemCounter; }
+    /// Physical timer interrupt
+    const unsigned irqPhys;
+};
 
-    /// Returns a pointer to the architected timer for cpu_id.
-    ArchTimer *getArchTimer(int cpu_id) { return &_archTimers[cpu_id]; }
+class GenericTimerISA : public ArmISA::BaseISADevice
+{
+  public:
+    GenericTimerISA(GenericTimer &_parent, unsigned _cpu)
+        : parent(_parent), cpu(_cpu) {}
+
+    void setMiscReg(int misc_reg, ArmISA::MiscReg val) M5_ATTR_OVERRIDE {
+        parent.setMiscReg(misc_reg, cpu, val);
+    }
+    ArmISA::MiscReg readMiscReg(int misc_reg) M5_ATTR_OVERRIDE {
+        return parent.readMiscReg(misc_reg, cpu);
+    }
 
-    virtual void serialize(std::ostream &os);
-    virtual void unserialize(Checkpoint *cp, const std::string &section);
+  protected:
+    GenericTimer &parent;
+    unsigned cpu;
 };
 
 #endif // __DEV_ARM_GENERIC_TIMER_HH__