summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas.sandberg@arm.com>2015-05-23 13:46:52 +0100
committerAndreas Sandberg <andreas.sandberg@arm.com>2015-05-23 13:46:52 +0100
commit65f3f097d3c270d2f28fc7d55651afaefb56ceed (patch)
tree099c9667e246b9e8197f292c1880d9f7a3af9159 /src
parent5435f25ec80ff691c4e42e06888c60a01848a31d (diff)
downloadgem5-65f3f097d3c270d2f28fc7d55651afaefb56ceed.tar.xz
dev, arm: Refactor and clean up the generic timer model
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.
Diffstat (limited to 'src')
-rw-r--r--src/arch/arm/isa.cc132
-rw-r--r--src/arch/arm/isa.hh10
-rw-r--r--src/arch/arm/system.cc18
-rw-r--r--src/arch/arm/system.hh9
-rw-r--r--src/dev/arm/RealView.py4
-rw-r--r--src/dev/arm/generic_timer.cc366
-rw-r--r--src/dev/arm/generic_timer.hh300
7 files changed, 527 insertions, 312 deletions
diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc
index 4358c8b2e..2120c56db 100644
--- a/src/arch/arm/isa.cc
+++ b/src/arch/arm/isa.cc
@@ -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();
}
}
diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh
index fd9801ae2..11f25de6d 100644
--- a/src/arch/arm/isa.hh
+++ b/src/arch/arm/isa.hh
@@ -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:
diff --git a/src/arch/arm/system.cc b/src/arch/arm/system.cc
index eebb220d8..a7db9ca1b 100644
--- a/src/arch/arm/system.cc
+++ b/src/arch/arm/system.cc
@@ -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)
{
diff --git a/src/arch/arm/system.hh b/src/arch/arm/system.hh
index 0937f6376..3add01e61 100644
--- a/src/arch/arm/system.hh
+++ b/src/arch/arm/system.hh
@@ -46,13 +46,13 @@
#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) */
diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py
index 9ff642cb1..2e58c8fa6 100644
--- a/src/dev/arm/RealView.py
+++ b/src/dev/arm/RealView.py
@@ -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)
diff --git a/src/dev/arm/generic_timer.cc b/src/dev/arm/generic_timer.cc
index 555c1050f..642205704 100644
--- a/src/dev/arm/generic_timer.cc
+++ b/src/dev/arm/generic_timer.cc
@@ -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
@@ -35,16 +35,24 @@
* 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()
{
diff --git a/src/dev/arm/generic_timer.hh b/src/dev/arm/generic_timer.hh
index bc43f8b3b..8dc921275 100644
--- a/src/dev/arm/generic_timer.hh
+++ b/src/dev/arm/generic_timer.hh
@@ -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
@@ -35,13 +35,15 @@
* 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"
@@ -51,149 +53,209 @@
/// 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__