From 65f3f097d3c270d2f28fc7d55651afaefb56ceed Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 23 May 2015 13:46:52 +0100 Subject: [PATCH] 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. --- src/arch/arm/isa.cc | 132 +++---------- src/arch/arm/isa.hh | 10 +- src/arch/arm/system.cc | 18 -- src/arch/arm/system.hh | 9 +- src/dev/arm/RealView.py | 4 +- src/dev/arm/generic_timer.cc | 366 +++++++++++++++++++++++++++++------ src/dev/arm/generic_timer.hh | 300 ++++++++++++++++------------ 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 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 #include -#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 §ion) +SystemCounter::unserialize(Checkpoint *cp, + const std::string §ion) { + // 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 §ion) +ArchTimer::unserialize(Checkpoint *cp, + const std::string §ion) { - 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(*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 §ion) { - _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 §ion); + + 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 §ion); + 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 - _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 §ion); + /** + * 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 + _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 §ion); + + 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(_params); - } + /// Per-CPU physical architected timers. + std::vector> 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 §ion); + protected: + GenericTimer &parent; + unsigned cpu; }; #endif // __DEV_ARM_GENERIC_TIMER_HH__ -- 2.30.2