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:
@@ -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 §ion);
|
||||
|
||||
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 §ion);
|
||||
};
|
||||
|
||||
/// 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 §ion);
|
||||
|
||||
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 §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)
|
||||
{}
|
||||
|
||||
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 §ion);
|
||||
protected:
|
||||
GenericTimer &parent;
|
||||
unsigned cpu;
|
||||
};
|
||||
|
||||
#endif // __DEV_ARM_GENERIC_TIMER_HH__
|
||||
|
||||
Reference in New Issue
Block a user