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.
This commit is contained in:
Andreas Sandberg 2015-05-23 13:46:52 +01:00
parent 5435f25ec8
commit 65f3f097d3
7 changed files with 539 additions and 324 deletions

View 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");
}
return cnt;
}
// We only need to create an ISA interface the first time we try
// to access the timer.
if (timer)
return *timer.get();
::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");
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 timer;
timer.reset(new GenericTimerISA(*generic_timer, tc->cpuId()));
return *timer.get();
}
}

View 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:

View 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)
{

View file

@ -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) */

View 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)

View 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
@ -35,16 +35,24 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Giacomo Gabrielli
* Andreas Sandberg
*/
#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 "arch/arm/system.hh"
#include "debug/Timer.hh"
#include "dev/arm/base_gic.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)
{
setCompareValue(counterValue() + sext<32>(val));
_counterLimit = val;
updateCounter();
}
void
GenericTimer::ArchTimer::setControl(uint32_t val)
ArchTimer::setTimerValue(uint32_t val)
{
setCompareValue(value() + sext<32>(val));
}
void
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;
}
void
GenericTimer::ArchTimer::serialize(std::ostream &os)
uint64_t
ArchTimer::value() const
{
SERIALIZE_SCALAR(_cpuNum);
SERIALIZE_SCALAR(_intNum);
uint32_t control_serial = _control;
SERIALIZE_SCALAR(control_serial);
return _systemCounter.value();
}
void
ArchTimer::serialize(std::ostream &os) const
{
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);
}
}
((ArmSystem *) p->system)->setGenericTimer(this);
void
ArchTimer::Interrupt::clear()
{
if (_ppi) {
_gic.clearPPInt(_irq, _cpu);
} else {
_gic.clearInt(_irq);
}
}
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()
{

View 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
@ -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();
/// 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 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:
/// Global system counter. It is shared by the architected timers.
/// @todo: implement memory-mapped controls
class SystemCounter
class Interrupt
{
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;
public:
/// Ctor.
SystemCounter()
: _freq(0), _period(0), _resetTick(0)
{
setFreq(0x01800000);
}
Interrupt(BaseGic &gic, unsigned irq)
: _gic(gic), _ppi(false), _irq(irq), _cpu(0) {}
/// 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;
}
Interrupt(BaseGic &gic, unsigned irq, unsigned cpu)
: _gic(gic), _ppi(true), _irq(irq), _cpu(cpu) {}
/// Returns the counter frequency.
uint64_t freq() const { return _freq; }
/// Sets the counter frequency.
/// @param freq frequency in Hz.
void setFreq(uint32_t freq);
void send();
void clear();
/// Returns the counter period.
Tick period() const { return _period; }
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string &section);
};
/// 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(); }
public:
/// Ctor.
ArchTimer()
: _control(0), _counterLimit(0), _counterLimitReachedEvent(this)
{}
/// 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 - counterValue(); }
/// 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);
virtual void serialize(std::ostream &os);
virtual void unserialize(Checkpoint *cp, const std::string &section);
friend class GenericTimer;
private:
BaseGic &_gic;
const bool _ppi;
const unsigned _irq;
const unsigned _cpu;
};
protected:
/// Control register.
BitUnion32(ArchTimerCtrl)
Bitfield<0> enable;
Bitfield<1> imask;
Bitfield<2> istatus;
EndBitUnion(ArchTimerCtrl)
static const int CPU_MAX = 8;
/// Name of this timer.
const std::string _name;
/// 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];
/// Pointer to parent class.
SimObject &_parent;
SystemCounter &_systemCounter;
Interrupt _interrupt;
/// Value of the control register ({CNTP/CNTHP/CNTV}_CTL).
ArchTimerCtrl _control;
/// Programmed limit value for the upcounter ({CNTP/CNTHP/CNTV}_CVAL).
uint64_t _counterLimit;
/**
* Timer settings or the offset has changed, re-evaluate
* trigger condition and raise interrupt if necessary.
*/
void updateCounter();
/// Called when the upcounter reaches the programmed value.
void counterLimitReached();
EventWrapper<ArchTimer, &ArchTimer::counterLimitReached>
_counterLimitReachedEvent;
public:
typedef GenericTimerParams Params;
const Params *
params() const
{
return dynamic_cast<const Params *>(_params);
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)
{}
ArchTimer::Interrupt irqPhys;
ArchTimer phys;
private:
// Disable copying
CoreTimers(const CoreTimers &c);
};
CoreTimers &getTimers(int cpu_id);
void createTimers(unsigned cpus);
/// System counter.
SystemCounter systemCounter;
/// Per-CPU physical architected timers.
std::vector<std::unique_ptr<CoreTimers>> timers;
protected: // Configuration
/// Pointer to the GIC, needed to trigger timer interrupts.
BaseGic *const gic;
/// Physical timer interrupt
const unsigned irqPhys;
};
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);
}
/// Ctor.
GenericTimer(Params *p);
/// Returns a pointer to the system counter.
SystemCounter *getSystemCounter() { return &_systemCounter; }
/// Returns a pointer to the architected timer for cpu_id.
ArchTimer *getArchTimer(int cpu_id) { return &_archTimers[cpu_id]; }
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__