From 65f3f097d3c270d2f28fc7d55651afaefb56ceed Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 23 May 2015 13:46:52 +0100 Subject: 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/dev/arm/generic_timer.cc | 366 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 308 insertions(+), 58 deletions(-) (limited to 'src/dev/arm/generic_timer.cc') 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() { -- cgit v1.2.3