dev-arm: Refactor GenericTimer

The GenericTimer specification includes a global component for
a universal view of time: the System Counter.

If both per-PE architected and memory-mapped timers are instantiated
in a system, they must both share the same counter. SystemCounter is
promoted to be an independent SimObject, which is now shared by
implementations.

The SystemCounter may be controlled/accessed through the memory-mapped
counter module in the system level implementation. This provides
control (CNTControlBase) and status (CNTReadBase) register frames. The
counter module is now implemented as part of GenericTimerMem.

Frequency changes occur through writes to an active CNTFID or to
CNTCR.EN as per the architecture. Low-high and high-low transitions are
delayed until suitable thresholds, where the counter value is a divisor
of the increment given the new frequency.
Due to changes in frequency, timers need to be notifies to be
rescheduled their counter limit events based on CompareValue/TimerValue.
A new SystemCounterListener interface is provided to achieve
correctness.

CNTFRQ is no longer able to modify the global frequency. PEs may
use this to modify their register view of the former, but they should
not affect the global value. These two should be consistent.

With frequency changes, counter value needs to be stored to track
contributions from different frequency epochs. This is now handled
on epoch change, counter disable and register access.

References to all GenericTimer model components are now provided as
part of the documentation.

VExpress_GEM5_Base is updated with the new model configuration.

Change-Id: I9a991836cacd84a5bc09e5d5275191fcae9ed84b
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/25306
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Adrian Herrera
2019-10-30 10:03:52 +00:00
committed by Giacomo Travaglini
parent 1fef1491e2
commit 81fc073768
6 changed files with 1629 additions and 471 deletions

191
src/dev/arm/GenericTimer.py Normal file
View File

@@ -0,0 +1,191 @@
# Copyright (c) 2009-2020 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.
from m5.SimObject import SimObject
from m5.objects.Device import PioDevice
from m5.params import Param, MaxAddr, NULL, VectorParam
from m5.proxy import Parent
from m5.util import fatal
from m5.util.fdthelper import FdtNode, FdtProperty, FdtPropertyWords, FdtState
class SystemCounter(SimObject):
"""
Shared by both PE-implementations and memory-mapped timers. It provides a
uniform view of system time through its counter value.
Reference:
Arm ARM (ARM DDI 0487E.a)
D11.1.2 - The system counter
"""
type = 'SystemCounter'
cxx_header = "dev/arm/generic_timer.hh"
# Maximum of 1004 frequency entries, including end marker
freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the "
"system counter (in Hz). First element is the base frequency, "
"following are alternative lower ones which must be exact divisors")
def generateDtb(self):
if not self.freqs:
fatal("No counter frequency to expose in DTB")
return FdtPropertyWords("clock-frequency", [self.freqs[0]])
class GenericTimer(SimObject):
"""
Architected timers per PE in the system. Each of them provides a physical
counter, a virtual counter and several timers accessible from different
exception levels and security states.
Reference:
Arm ARM (ARM DDI 0487E.a)
D11.2 - The AArch64 view of the Generic Timer
G6.2 - The AArch32 view of the Generic Timer
"""
type = 'GenericTimer'
cxx_header = "dev/arm/generic_timer.hh"
_freq_in_dtb = False
system = Param.ArmSystem(Parent.any, "system")
counter = Param.SystemCounter(Parent.any, "Global system counter")
int_phys_s = Param.ArmPPI("Physical (S) timer interrupt")
int_phys_ns = Param.ArmPPI("Physical (NS) timer interrupt")
int_virt = Param.ArmPPI("Virtual timer interrupt")
int_hyp = Param.ArmPPI("Hypervisor timer interrupt")
def generateDeviceTree(self, state):
node = FdtNode("timer")
node.appendCompatible(["arm,cortex-a15-timer",
"arm,armv7-timer",
"arm,armv8-timer"])
node.append(FdtPropertyWords("interrupts", [
1, int(self.int_phys_s.num) - 16, 0xf08,
1, int(self.int_phys_ns.num) - 16, 0xf08,
1, int(self.int_virt.num) - 16, 0xf08,
1, int(self.int_hyp.num) - 16, 0xf08,
]))
if self._freq_in_dtb:
node.append(self.counter.unproxy(self).generateDtb())
yield node
class GenericTimerFrame(PioDevice):
"""
Memory-mapped timer frame implementation. Controlled from GenericTimerMem,
may be used by peripherals without a system register interface.
Reference:
Arm ARM (ARM DDI 0487E.a)
I2.3.2 - The CNTBaseN and CNTEL0BaseN frames
"""
type = 'GenericTimerFrame'
cxx_header = "dev/arm/generic_timer.hh"
_frame_num = 0
counter = Param.SystemCounter(Parent.any, "Global system counter")
cnt_base = Param.Addr("CNTBase register frame base")
cnt_el0_base = Param.Addr(MaxAddr, "CNTEL0Base register frame base")
int_phys = Param.ArmSPI("Physical Interrupt")
int_virt = Param.ArmSPI("Virtual Interrupt")
def generateDeviceTree(self, state):
node = FdtNode("frame@{:08x}".format(self.cnt_base.value))
node.append(FdtPropertyWords("frame-number", self._frame_num))
ints = [0, int(self.int_phys.num) - 32, 4]
if self.int_virt != NULL:
ints.extend([0, int(self.int_virt.num) - 32, 4])
node.append(FdtPropertyWords("interrupts", ints))
reg = state.addrCells(self.cnt_base) + state.sizeCells(0x1000)
if self.cnt_el0_base.value != MaxAddr:
reg.extend(state.addrCells(self.cnt_el0_base)
+ state.sizeCells(0x1000))
node.append(FdtPropertyWords("reg", reg))
return node
class GenericTimerMem(PioDevice):
"""
System level implementation. It provides three main components:
- Memory-mapped counter module: controls the system timer through the
CNTControlBase frame, and provides its value through the CNTReadBase frame
- Memory-mapped timer control module: controls the memory-mapped timers
- Memory-mapped timers: implementations of the GenericTimer for system
peripherals
Reference:
Arm ARM (ARM DDI 0487E.a)
I2 - System Level Implementation of the Generic Timer
"""
type = 'GenericTimerMem'
cxx_header = "dev/arm/generic_timer.hh"
_freq_in_dtb = False
counter = Param.SystemCounter(Parent.any, "Global system counter")
cnt_control_base = Param.Addr("CNTControlBase register frame base")
cnt_read_base = Param.Addr("CNTReadBase register frame base")
cnt_ctl_base = Param.Addr("CNTCTLBase register frame base")
# Maximum of 8 timer frames
frames = VectorParam.GenericTimerFrame([], "Memory-mapped timer frames")
def generateDeviceTree(self, state):
node = self.generateBasicPioDeviceNode(state, "timer",
self.cnt_ctl_base, 0x1000)
node.appendCompatible(["arm,armv7-timer-mem"])
node.append(state.addrCellsProperty())
node.append(state.sizeCellsProperty())
node.append(FdtProperty("ranges"))
if self._freq_in_dtb:
node.append(self.counter.unproxy(self).generateDtb())
for i, frame in enumerate(self.frames):
frame._frame_num = i
node.append(frame.generateDeviceTree(state))
yield node

View File

@@ -51,6 +51,7 @@ from m5.objects.Platform import Platform
from m5.objects.Terminal import Terminal
from m5.objects.Uart import Uart
from m5.objects.SimpleMemory import SimpleMemory
from m5.objects.GenericTimer import *
from m5.objects.Gic import *
from m5.objects.EnergyCtrl import EnergyCtrl
from m5.objects.ClockedObject import ClockedObject
@@ -437,49 +438,6 @@ class CpuLocalTimer(BasicPioDevice):
int_timer = Param.ArmPPI("Interrrupt used per-cpu to GIC")
int_watchdog = Param.ArmPPI("Interrupt for per-cpu watchdog to GIC")
class GenericTimer(ClockedObject):
type = 'GenericTimer'
cxx_header = "dev/arm/generic_timer.hh"
system = Param.ArmSystem(Parent.any, "system")
int_phys_s = Param.ArmPPI("Physical (S) timer interrupt")
int_phys_ns = Param.ArmPPI("Physical (NS) timer interrupt")
int_virt = Param.ArmPPI("Virtual timer interrupt")
int_hyp = Param.ArmPPI("Hypervisor timer interrupt")
freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the "
"system counter (in Hz). First element is the base frequency, "
"following are alternative lower ones which must be exact divisors")
def generateDeviceTree(self, state):
node = FdtNode("timer")
node.appendCompatible(["arm,cortex-a15-timer",
"arm,armv7-timer",
"arm,armv8-timer"])
node.append(FdtPropertyWords("interrupts", [
1, int(self.int_phys_s.num) - 16, 0xf08,
1, int(self.int_phys_ns.num) - 16, 0xf08,
1, int(self.int_virt.num) - 16, 0xf08,
1, int(self.int_hyp.num) - 16, 0xf08,
]))
clock = state.phandle(self.clk_domain.unproxy(self))
node.append(FdtPropertyWords("clocks", clock))
yield node
class GenericTimerMem(PioDevice):
type = 'GenericTimerMem'
cxx_header = "dev/arm/generic_timer.hh"
base = Param.Addr(0, "Base address")
int_phys = Param.ArmSPI("Physical Interrupt")
int_virt = Param.ArmSPI("Virtual Interrupt")
freqs = VectorParam.UInt32([0x01800000], "Frequencies available for the "
"system counter (in Hz). First element is the base frequency, "
"following are alternative lower ones which must be exact divisors")
class PL031(AmbaIntDevice):
type = 'PL031'
cxx_header = "dev/arm/rtc_pl031.hh"
@@ -732,6 +690,7 @@ class VExpress_EMM(RealView):
conf_base=0x30000000, conf_size='256MB', conf_device_bits=16,
pci_pio_base=0)
sys_counter = SystemCounter()
generic_timer = GenericTimer(int_phys_s=ArmPPI(num=29),
int_phys_ns=ArmPPI(num=30),
int_virt=ArmPPI(num=27),
@@ -901,7 +860,14 @@ Memory map:
0x1c170000-0x1c17ffff: RTC
0x20000000-0x3fffffff: On-chip peripherals:
0x2a430000-0x2a43ffff: System Counter (control)
0x2a490000-0x2a49ffff: Trusted Watchdog (SP805)
0x2a800000-0x2a800fff: System Counter (read)
0x2a810000-0x2a810fff: System Timer (control)
0x2a820000-0x2a820fff: System Timer (frame 0)
0x2a830000-0x2a830fff: System Timer (frame 1)
0x2b000000-0x2b00ffff: HDLCD
0x2b060000-0x2b060fff: System Watchdog (SP805)
@@ -946,6 +912,8 @@ Interrupts:
47 : Reserved (Ethernet)
48 : Reserved (USB)
56 : Trusted Watchdog (SP805)
57 : System timer0 (phys)
58 : System timer1 (phys)
95-255: On-chip interrupt sources (we use these for
gem5-specific devices, SPIs)
74 : VirtIO (gem5/FM extension)
@@ -991,19 +959,29 @@ Interrupts:
# Trusted Watchdog, SP805
trusted_watchdog = Sp805(pio_addr=0x2a490000, int_num=56)
sys_counter = SystemCounter()
generic_timer = GenericTimer(int_phys_s=ArmPPI(num=29),
int_phys_ns=ArmPPI(num=30),
int_virt=ArmPPI(num=27),
int_hyp=ArmPPI(num=26))
generic_timer_mem = GenericTimerMem(cnt_control_base=0x2a430000,
cnt_read_base=0x2a800000,
cnt_ctl_base=0x2a810000,
frames=[
GenericTimerFrame(cnt_base=0x2a820000,
int_phys=ArmSPI(num=57), int_virt=ArmSPI(num=133)),
GenericTimerFrame(cnt_base=0x2a830000,
int_phys=ArmSPI(num=58), int_virt=ArmSPI(num=134))
])
system_watchdog = Sp805(pio_addr=0x2b060000, int_num=130)
def _on_chip_devices(self):
return [
self.generic_timer,
self.generic_timer_mem,
self.trusted_watchdog,
self.system_watchdog
]
] + self.generic_timer_mem.frames
def _on_chip_memory(self):
memories = [

View File

@@ -41,6 +41,7 @@ if env['TARGET_ISA'] == 'arm':
SimObject('AbstractNVM.py')
SimObject('Display.py')
SimObject('FlashDevice.py')
SimObject('GenericTimer.py')
SimObject('Gic.py')
SimObject('RealView.py')
SimObject('SMMUv3.py')

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, 2017-2018, 2019 ARM Limited
* Copyright (c) 2013, 2015, 2017-2018,2020 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
@@ -40,67 +40,106 @@
#include "arch/arm/isa_device.hh"
#include "arch/arm/system.hh"
#include "base/bitunion.hh"
#include "dev/arm/base_gic.hh"
#include "dev/arm/generic_timer_miscregs_types.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).
/// architected timers as specified by the ARM Generic Timer extension:
/// Arm ARM (ARM DDI 0487E.a)
/// D11.1.2 - The system counter
/// D11.2 - The AArch64 view of the Generic Timer
/// G6.2 - The AArch32 view of the Generic Timer
/// I2 - System Level Implementation of the Generic Timer
class Checkpoint;
class SystemCounterParams;
class GenericTimerParams;
class GenericTimerFrameParams;
class GenericTimerMemParams;
/// Global system counter. It is shared by the architected timers.
/// @todo: implement memory-mapped controls
class SystemCounter : public Serializable
/// Abstract class for elements whose events depend on the counting speed
/// of the System Counter
class SystemCounterListener : public Serializable
{
public:
/// Called from the SystemCounter when a change in counting speed occurred
/// Events should be rescheduled properly inside this member function
virtual void notify(void) = 0;
};
/// Global system counter. It is shared by the architected and memory-mapped
/// timers.
class SystemCounter : public SimObject
{
protected:
/// Indicates if the counter is enabled
bool _enabled;
/// Counter frequency (as specified by CNTFRQ).
uint32_t _freq;
/// Counter value (as specified in CNTCV).
uint64_t _value;
/// Value increment in each counter cycle
uint64_t _increment;
/// Frequency modes table with all possible frequencies for the counter
std::vector<uint32_t> _freqTable;
/// Currently selected entry in the table, its contents should match _freq
size_t _activeFreqEntry;
/// Cached copy of the counter period (inverse of the frequency).
Tick _period;
/// Tick when the counter was reset.
Tick _resetTick;
/// Counter cycle start Tick when the counter status affecting
/// its value has been updated
Tick _updateTick;
/// Kernel event stream control register
uint32_t _regCntkctl;
/// Hypervisor event stream control register
uint32_t _regCnthctl;
/// Listeners to changes in counting speed
std::vector<SystemCounterListener *> _listeners;
/// Maximum architectural number of frequency table entries
static constexpr size_t MAX_FREQ_ENTRIES = 1004;
public:
SystemCounter(std::vector<uint32_t> &freqs);
SystemCounter(SystemCounterParams *const p);
/// 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;
}
/// Validates a System Counter reference
/// @param sys_cnt System counter reference to validate
static void validateCounterRef(SystemCounter *sys_cnt);
/// Indicates if the counter is enabled.
bool enabled() const { return _enabled; }
/// Returns the counter frequency.
uint32_t freq() const { return _freq; }
/// Sets the counter frequency.
/// @param freq frequency in Hz.
void setFreq(uint32_t freq);
/// Updates and returns the counter value.
uint64_t value();
/// Returns the value increment
uint64_t increment() const { return _increment; }
/// Returns a reference to the frequency modes table.
std::vector<uint32_t>& freqTable() { return _freqTable; }
/// Returns the currently active frequency table entry.
size_t activeFreqEntry() const { return _activeFreqEntry; }
/// Returns the counter period.
Tick period() const { return _period; }
void setKernelControl(uint32_t val) { _regCntkctl = val; }
uint32_t getKernelControl() { return _regCntkctl; }
/// Enables the counter after a CNTCR.EN == 1
void enable();
/// Disables the counter after a CNTCR.EN == 0
void disable();
void setHypControl(uint32_t val) { _regCnthctl = val; }
uint32_t getHypControl() { return _regCnthctl; }
/// Schedules a counter frequency update after a CNTCR.FCREQ == 1
/// This complies with frequency transitions as per the architecture
/// @param new_freq_entry Index in CNTFID of the new freq
void freqUpdateSchedule(size_t new_freq_entry);
/// Sets the value explicitly from writes to CNTCR.CNTCV
void setValue(uint64_t new_value);
/// Called from System Counter Listeners to register
void registerListener(SystemCounterListener *listener);
/// Returns the tick at which a certain counter value is reached
Tick whenValue(uint64_t target_val);
Tick whenValue(uint64_t cur_val, uint64_t target_val) const;
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
@@ -108,10 +147,25 @@ class SystemCounter : public Serializable
private:
// Disable copying
SystemCounter(const SystemCounter &c);
/// Frequency update event handling
EventFunctionWrapper _freqUpdateEvent;
size_t _nextFreqEntry;
/// Callback for the frequency update
void freqUpdateCallback();
/// Updates the counter value.
void updateValue(void);
/// Updates the update tick, normalizes to the lower cycle start tick
void updateTick(void);
/// Notifies counting speed changes to listeners
void notifyListeners(void) const;
};
/// Per-CPU architected timer.
class ArchTimer : public Serializable, public Drainable
class ArchTimer : public SystemCounterListener, public Drainable
{
protected:
/// Control register.
@@ -178,6 +232,11 @@ class ArchTimer : public Serializable, public Drainable
/// Returns the value of the counter which this timer relies on.
uint64_t value() const;
Tick whenValue(uint64_t target_val) {
return _systemCounter.whenValue(value(), target_val);
}
void notify(void) override;
// Serializable
void serialize(CheckpointOut &cp) const override;
@@ -214,12 +273,12 @@ class ArchTimerKvm : public ArchTimer
}
};
class GenericTimer : public ClockedObject
class GenericTimer : public SimObject
{
public:
const GenericTimerParams * params() const;
GenericTimer(GenericTimerParams *p);
GenericTimer(GenericTimerParams *const p);
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
@@ -229,11 +288,15 @@ class GenericTimer : public ClockedObject
RegVal readMiscReg(int misc_reg, unsigned cpu);
protected:
struct CoreTimers {
CoreTimers(GenericTimer &parent, ArmSystem &system, unsigned cpu,
class CoreTimers : public SystemCounterListener
{
public:
CoreTimers(GenericTimer &_parent, ArmSystem &system, unsigned cpu,
ArmInterruptPin *_irqPhysS, ArmInterruptPin *_irqPhysNS,
ArmInterruptPin *_irqVirt, ArmInterruptPin *_irqHyp)
: irqPhysS(_irqPhysS),
: parent(_parent),
threadContext(system.getThreadContext(cpu)),
irqPhysS(_irqPhysS),
irqPhysNS(_irqPhysNS),
irqVirt(_irqVirt),
irqHyp(_irqHyp),
@@ -250,8 +313,33 @@ class GenericTimer : public ClockedObject
_irqVirt),
hyp(csprintf("%s.hyp_timer%d", parent.name(), cpu),
system, parent, parent.systemCounter,
_irqHyp)
{}
_irqHyp),
physEvStream{
EventFunctionWrapper([this]{ physEventStreamCallback(); },
csprintf("%s.phys_event_gen%d", parent.name(), cpu)), 0, 0
},
virtEvStream{
EventFunctionWrapper([this]{ virtEventStreamCallback(); },
csprintf("%s.virt_event_gen%d", parent.name(), cpu)), 0, 0
}
{
cntfrq = 0x01800000;
}
/// System counter frequency as visible from this core
uint32_t cntfrq;
/// Kernel control register
CNTKCTL cntkctl;
/// Hypervisor control register
CNTHCTL cnthctl;
/// Generic Timer parent reference
GenericTimer &parent;
/// Thread (HW) context associated to this PE implementation
ThreadContext *threadContext;
ArmInterruptPin const *irqPhysS;
ArmInterruptPin const *irqPhysNS;
@@ -263,6 +351,39 @@ class GenericTimer : public ClockedObject
ArchTimerKvm virt;
ArchTimerKvm hyp;
// Event Stream. Events are generated based on a configurable
// transitionBit over the counter value. transitionTo indicates
// the transition direction (0->1 or 1->0)
struct EventStream
{
EventFunctionWrapper event;
uint8_t transitionTo;
uint8_t transitionBit;
uint64_t
eventTargetValue(uint64_t val) const
{
uint64_t bit_val = bits(val, transitionBit);
uint64_t ret_val = mbits(val, 63, transitionBit);
uint64_t incr_val = 1 << transitionBit;
if (bit_val == transitionTo)
incr_val *= 2;
return ret_val + incr_val;
}
};
EventStream physEvStream;
EventStream virtEvStream;
void physEventStreamCallback();
void virtEventStreamCallback();
void eventStreamCallback() const;
void schedNextEvent(EventStream &ev_stream, ArchTimer &timer);
void notify(void) override;
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
private:
// Disable copying
CoreTimers(const CoreTimers &c);
@@ -271,8 +392,8 @@ class GenericTimer : public ClockedObject
CoreTimers &getTimers(int cpu_id);
void createTimers(unsigned cpus);
/// System counter.
SystemCounter systemCounter;
/// System counter reference.
SystemCounter &systemCounter;
/// Per-CPU physical architected timers.
std::vector<std::unique_ptr<CoreTimers>> timers;
@@ -280,6 +401,9 @@ class GenericTimer : public ClockedObject
protected: // Configuration
/// ARM system containing this timer
ArmSystem &system;
void handleStream(CoreTimers::EventStream *ev_stream,
ArchTimer *timer, RegVal old_cnt_ctl, RegVal cnt_ctl);
};
class GenericTimerISA : public ArmISA::BaseISADevice
@@ -296,61 +420,190 @@ class GenericTimerISA : public ArmISA::BaseISADevice
unsigned cpu;
};
class GenericTimerMem : public PioDevice
class GenericTimerFrame : public PioDevice
{
public:
GenericTimerMem(GenericTimerMemParams *p);
GenericTimerFrame(GenericTimerFrameParams *const p);
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
public: // PioDevice
AddrRangeList getAddrRanges() const override { return addrRanges; }
/// Indicates if this frame implements a virtual timer
bool hasVirtualTimer() const;
/// Returns the virtual offset for this frame if a virtual timer is
/// implemented
uint64_t getVirtOffset() const;
/// Sets the virtual offset for this frame's virtual timer after
/// a write to CNTVOFF
void setVirtOffset(uint64_t new_offset);
/// Indicates if this frame implements a second EL0 view
bool hasEl0View() const;
/// Returns the access bits for this frame
uint8_t getAccessBits() const;
/// Updates the access bits after a write to CNTCTLBase.CNTACR
void setAccessBits(uint8_t data);
/// Indicates if non-secure accesses are allowed to this frame
bool hasNonSecureAccess() const;
/// Allows non-secure accesses after an enabling write to
/// CNTCTLBase.CNTNSAR
void setNonSecureAccess();
/// Indicates if CNTVOFF is readable for this frame
bool hasReadableVoff() const;
protected:
AddrRangeList getAddrRanges() const override;
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;
private:
/// CNTBase/CNTEL0Base (Memory-mapped timer frame)
uint64_t timerRead(Addr addr, size_t size, bool is_sec, bool to_el0) const;
void timerWrite(Addr addr, size_t size, uint64_t data, bool is_sec,
bool to_el0);
const AddrRange timerRange;
const AddrRangeList addrRanges;
AddrRange timerEl0Range;
protected:
/// System counter.
SystemCounter systemCounter;
static const Addr TIMER_CNTPCT_LO = 0x00;
static const Addr TIMER_CNTPCT_HI = 0x04;
static const Addr TIMER_CNTVCT_LO = 0x08;
static const Addr TIMER_CNTVCT_HI = 0x0c;
static const Addr TIMER_CNTFRQ = 0x10;
static const Addr TIMER_CNTEL0ACR = 0x14;
static const Addr TIMER_CNTVOFF_LO = 0x18;
static const Addr TIMER_CNTVOFF_HI = 0x1c;
static const Addr TIMER_CNTP_CVAL_LO = 0x20;
static const Addr TIMER_CNTP_CVAL_HI = 0x24;
static const Addr TIMER_CNTP_TVAL = 0x28;
static const Addr TIMER_CNTP_CTL = 0x2c;
static const Addr TIMER_CNTV_CVAL_LO = 0x30;
static const Addr TIMER_CNTV_CVAL_HI = 0x34;
static const Addr TIMER_CNTV_TVAL = 0x38;
static const Addr TIMER_CNTV_CTL = 0x3c;
/// All MMIO ranges GenericTimerFrame responds to
AddrRangeList addrRanges;
/// System counter reference.
SystemCounter &systemCounter;
/// Physical and virtual timers
ArchTimer physTimer;
ArchTimer virtTimer;
/// Reports access properties of the CNTBase register frame elements
BitUnion8(AccessBits)
Bitfield<5> rwpt;
Bitfield<4> rwvt;
Bitfield<3> rvoff;
Bitfield<2> rfrq;
Bitfield<1> rvct;
Bitfield<0> rpct;
EndBitUnion(AccessBits)
AccessBits accessBits;
// Reports access properties of the CNTEL0Base register frame elements
BitUnion16(AccessBitsEl0)
Bitfield<9> pten;
Bitfield<8> vten;
Bitfield<1> vcten;
Bitfield<0> pcten;
EndBitUnion(AccessBitsEl0)
AccessBitsEl0 accessBitsEl0;
/// Reports whether non-secure accesses are allowed to this frame
bool nonSecureAccess;
ArmSystem &system;
};
class GenericTimerMem : public PioDevice
{
public:
GenericTimerMem(GenericTimerMemParams *const p);
/// Validates a Generic Timer register frame address range
/// @param base_addr Range of the register frame
static void validateFrameRange(const AddrRange &range);
/// Validates an MMIO access permissions
/// @param sys System reference where the acces is being made
/// @param is_sec If the access is to secure memory
static bool validateAccessPerm(ArmSystem &sys, bool is_sec);
protected:
AddrRangeList getAddrRanges() const override;
Tick read(PacketPtr pkt) override;
Tick write(PacketPtr pkt) override;
private:
/// CNTControlBase (System counter control frame)
uint64_t counterCtrlRead(Addr addr, size_t size, bool is_sec) const;
void counterCtrlWrite(Addr addr, size_t size, uint64_t data, bool is_sec);
const AddrRange counterCtrlRange;
BitUnion32(CNTCR)
Bitfield<17,8> fcreq;
Bitfield<2> scen;
Bitfield<1> hdbg;
Bitfield<0> en;
EndBitUnion(CNTCR)
BitUnion32(CNTSR)
Bitfield<31,8> fcack;
EndBitUnion(CNTSR)
static const Addr COUNTER_CTRL_CNTCR = 0x00;
static const Addr COUNTER_CTRL_CNTSR = 0x04;
static const Addr COUNTER_CTRL_CNTCV_LO = 0x08;
static const Addr COUNTER_CTRL_CNTCV_HI = 0x0c;
static const Addr COUNTER_CTRL_CNTSCR = 0x10;
static const Addr COUNTER_CTRL_CNTID = 0x1c;
static const Addr COUNTER_CTRL_CNTFID = 0x20;
/// CNTReadBase (System counter status frame)
uint64_t counterStatusRead(Addr addr, size_t size) const;
void counterStatusWrite(Addr addr, size_t size, uint64_t data);
const AddrRange counterStatusRange;
static const Addr COUNTER_STATUS_CNTCV_LO = 0x00;
static const Addr COUNTER_STATUS_CNTCV_HI = 0x04;
/// CNTCTLBase (Memory-mapped timer global control frame)
uint64_t timerCtrlRead(Addr addr, size_t size, bool is_sec) const;
void timerCtrlWrite(Addr addr, size_t size, uint64_t data, bool is_sec);
const AddrRange timerCtrlRange;
/// ID register for reporting features of implemented timer frames
uint32_t cnttidr;
static const Addr TIMER_CTRL_CNTFRQ = 0x00;
static const Addr TIMER_CTRL_CNTNSAR = 0x04;
static const Addr TIMER_CTRL_CNTTIDR = 0x08;
static const Addr TIMER_CTRL_CNTACR = 0x40;
static const Addr TIMER_CTRL_CNTVOFF_LO = 0x80;
static const Addr TIMER_CTRL_CNTVOFF_HI = 0x84;
/// All MMIO ranges GenericTimerMem responds to
const AddrRangeList addrRanges;
/// System counter reference.
SystemCounter &systemCounter;
/// Maximum architectural number of memory-mapped timer frames
static constexpr size_t MAX_TIMER_FRAMES = 8;
/// Timer frame references
std::vector<GenericTimerFrame *> frames;
ArmSystem &system;
};
#endif // __DEV_ARM_GENERIC_TIMER_HH__

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__
#define __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__
#include "base/bitunion.hh"
namespace ArmISA
{
BitUnion64(CNTKCTL)
Bitfield<9> el0pten;
Bitfield<8> el0vten;
Bitfield<7,4> evnti;
Bitfield<3> evntdir;
Bitfield<2> evnten;
Bitfield<1> el0vcten;
Bitfield<0> el0pcten;
EndBitUnion(CNTKCTL)
BitUnion64(CNTHCTL)
Bitfield<7,4> evnti;
Bitfield<3> evntdir;
Bitfield<2> evnten;
Bitfield<1> el1pcen;
Bitfield<0> el1pcten;
EndBitUnion(CNTHCTL)
// If Armv8.1-VHE && HCR_EL2.E2H == 1
BitUnion64(CNTHCTL_E2H)
Bitfield<11> el1pten;
Bitfield<10> el1pcten;
Bitfield<9> el0pten;
Bitfield<8> el0vten;
Bitfield<7,4> evnti;
Bitfield<3> evntdir;
Bitfield<2> evnten;
Bitfield<1> el0vcten;
Bitfield<0> el0pcten;
EndBitUnion(CNTHCTL_E2H)
}
#endif // __DEV_ARM_GENERIC_TIMER_MISCREGS_TYPES_HH__