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