| /* | |
| * Copyright (c) 2013, 2015, 2017-2018 ARM Limited | |
| * All rights reserved. | |
| * | |
| * The license below extends only to copyright in the software and shall | |
| * not be construed as granting a license to any other intellectual | |
| * property including but not limited to intellectual property relating | |
| * to a hardware implementation of the functionality of the software | |
| * licensed hereunder. You may use the software subject to the license | |
| * terms below provided that you ensure that this notice is replicated | |
| * unmodified and in its entirety in all distributions of the software, | |
| * modified or unmodified, in source code or in binary form. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are | |
| * met: redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer; | |
| * redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution; | |
| * neither the name of the copyright holders nor the names of its | |
| * contributors may be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * 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 "arch/arm/system.hh" | |
| #include "base/bitunion.hh" | |
| #include "dev/arm/base_gic.hh" | |
| #include "sim/core.hh" | |
| #include "sim/sim_object.hh" | |
| /// @file | |
| /// This module implements the global system counter and the local per-CPU | |
| /// architected timers as specified by the ARM Generic Timer extension (ARM | |
| /// ARM, Issue C, Chapter 17). | |
| class Checkpoint; | |
| class GenericTimerParams; | |
| class GenericTimerMemParams; | |
| /// Global system counter. It is shared by the architected timers. | |
| /// @todo: implement memory-mapped controls | |
| class SystemCounter : public Serializable | |
| { | |
| 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; | |
| /// Kernel event stream control register | |
| uint32_t _regCntkctl; | |
| /// Hypervisor event stream control register | |
| uint32_t _regCnthctl; | |
| 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 setHypControl(uint32_t val) { _regCnthctl = val; } | |
| uint32_t getHypControl() { return _regCnthctl; } | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| private: | |
| // Disable copying | |
| SystemCounter(const SystemCounter &c); | |
| }; | |
| /// Per-CPU architected timer. | |
| class ArchTimer : public Serializable, public Drainable | |
| { | |
| protected: | |
| /// Control register. | |
| BitUnion32(ArchTimerCtrl) | |
| Bitfield<0> enable; | |
| Bitfield<1> imask; | |
| Bitfield<2> istatus; | |
| EndBitUnion(ArchTimerCtrl) | |
| /// Name of this timer. | |
| const std::string _name; | |
| /// Pointer to parent class. | |
| SimObject &_parent; | |
| SystemCounter &_systemCounter; | |
| ArmInterruptPin * const _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; | |
| /// Offset relative to the physical timer (CNTVOFF) | |
| uint64_t _offset; | |
| /** | |
| * 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(); | |
| EventFunctionWrapper _counterLimitReachedEvent; | |
| virtual bool scheduleEvents() { return true; } | |
| public: | |
| ArchTimer(const std::string &name, | |
| SimObject &parent, | |
| SystemCounter &sysctr, | |
| ArmInterruptPin *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); | |
| uint64_t offset() const { return _offset; } | |
| void setOffset(uint64_t val); | |
| /// Returns the value of the counter which this timer relies on. | |
| uint64_t value() const; | |
| // Serializable | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| // Drainable | |
| DrainState drain() override; | |
| void drainResume() override; | |
| private: | |
| // Disable copying | |
| ArchTimer(const ArchTimer &t); | |
| }; | |
| class ArchTimerKvm : public ArchTimer | |
| { | |
| private: | |
| ArmSystem &system; | |
| public: | |
| ArchTimerKvm(const std::string &name, | |
| ArmSystem &system, | |
| SimObject &parent, | |
| SystemCounter &sysctr, | |
| ArmInterruptPin *interrupt) | |
| : ArchTimer(name, parent, sysctr, interrupt), system(system) {} | |
| protected: | |
| // For ArchTimer's in a GenericTimerISA with Kvm execution about | |
| // to begin, skip rescheduling the event. | |
| // Otherwise, we should reschedule the event (if necessary). | |
| bool scheduleEvents() override { | |
| return !system.validKvmEnvironment(); | |
| } | |
| }; | |
| class GenericTimer : public ClockedObject | |
| { | |
| public: | |
| const GenericTimerParams * params() const; | |
| GenericTimer(GenericTimerParams *p); | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| public: | |
| void setMiscReg(int misc_reg, unsigned cpu, RegVal val); | |
| RegVal readMiscReg(int misc_reg, unsigned cpu); | |
| protected: | |
| struct CoreTimers { | |
| CoreTimers(GenericTimer &parent, ArmSystem &system, unsigned cpu, | |
| ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS, | |
| ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp) | |
| : irqPhysS(_irqPhysS), | |
| irqPhysNS(_irqPhysNS), | |
| irqVirt(_irqVirt), | |
| irqHyp(_irqHyp), | |
| physS(csprintf("%s.phys_s_timer%d", parent.name(), cpu), | |
| system, parent, parent.systemCounter, | |
| _irqPhysS), | |
| // This should really be phys_timerN, but we are stuck with | |
| // arch_timer for backwards compatibility. | |
| physNS(csprintf("%s.arch_timer%d", parent.name(), cpu), | |
| system, parent, parent.systemCounter, | |
| _irqPhysNS), | |
| virt(csprintf("%s.virt_timer%d", parent.name(), cpu), | |
| system, parent, parent.systemCounter, | |
| _irqVirt), | |
| hyp(csprintf("%s.hyp_timer%d", parent.name(), cpu), | |
| system, parent, parent.systemCounter, | |
| _irqHyp) | |
| {} | |
| ArmInterruptPin const *irqPhysS; | |
| ArmInterruptPin const *irqPhysNS; | |
| ArmInterruptPin const *irqVirt; | |
| ArmInterruptPin const *irqHyp; | |
| ArchTimerKvm physS; | |
| ArchTimerKvm physNS; | |
| ArchTimerKvm virt; | |
| ArchTimerKvm hyp; | |
| 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 | |
| /// ARM system containing this timer | |
| ArmSystem &system; | |
| }; | |
| class GenericTimerISA : public ArmISA::BaseISADevice | |
| { | |
| public: | |
| GenericTimerISA(GenericTimer &_parent, unsigned _cpu) | |
| : parent(_parent), cpu(_cpu) {} | |
| void setMiscReg(int misc_reg, RegVal val) override; | |
| RegVal readMiscReg(int misc_reg) override; | |
| protected: | |
| GenericTimer &parent; | |
| unsigned cpu; | |
| }; | |
| class GenericTimerMem : public PioDevice | |
| { | |
| public: | |
| GenericTimerMem(GenericTimerMemParams *p); | |
| void serialize(CheckpointOut &cp) const override; | |
| void unserialize(CheckpointIn &cp) override; | |
| public: // PioDevice | |
| AddrRangeList getAddrRanges() const override { return addrRanges; } | |
| Tick read(PacketPtr pkt) override; | |
| Tick write(PacketPtr pkt) override; | |
| protected: | |
| uint64_t ctrlRead(Addr addr, size_t size) const; | |
| void ctrlWrite(Addr addr, size_t size, uint64_t value); | |
| uint64_t timerRead(Addr addr, size_t size) const; | |
| void timerWrite(Addr addr, size_t size, uint64_t value); | |
| protected: // Registers | |
| static const Addr CTRL_CNTFRQ = 0x000; | |
| static const Addr CTRL_CNTNSAR = 0x004; | |
| static const Addr CTRL_CNTTIDR = 0x008; | |
| static const Addr CTRL_CNTACR_BASE = 0x040; | |
| static const Addr CTRL_CNTVOFF_LO_BASE = 0x080; | |
| static const Addr CTRL_CNTVOFF_HI_BASE = 0x084; | |
| static const Addr TIMER_CNTPCT_LO = 0x000; | |
| static const Addr TIMER_CNTPCT_HI = 0x004; | |
| static const Addr TIMER_CNTVCT_LO = 0x008; | |
| static const Addr TIMER_CNTVCT_HI = 0x00C; | |
| static const Addr TIMER_CNTFRQ = 0x010; | |
| static const Addr TIMER_CNTEL0ACR = 0x014; | |
| static const Addr TIMER_CNTVOFF_LO = 0x018; | |
| static const Addr TIMER_CNTVOFF_HI = 0x01C; | |
| static const Addr TIMER_CNTP_CVAL_LO = 0x020; | |
| static const Addr TIMER_CNTP_CVAL_HI = 0x024; | |
| static const Addr TIMER_CNTP_TVAL = 0x028; | |
| static const Addr TIMER_CNTP_CTL = 0x02C; | |
| static const Addr TIMER_CNTV_CVAL_LO = 0x030; | |
| static const Addr TIMER_CNTV_CVAL_HI = 0x034; | |
| static const Addr TIMER_CNTV_TVAL = 0x038; | |
| static const Addr TIMER_CNTV_CTL = 0x03C; | |
| protected: // Params | |
| const AddrRange ctrlRange; | |
| const AddrRange timerRange; | |
| const AddrRangeList addrRanges; | |
| protected: | |
| /// System counter. | |
| SystemCounter systemCounter; | |
| ArchTimer physTimer; | |
| ArchTimer virtTimer; | |
| }; | |
| #endif // __DEV_ARM_GENERIC_TIMER_HH__ |