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:
committed by
Giacomo Travaglini
parent
1fef1491e2
commit
81fc073768
191
src/dev/arm/GenericTimer.py
Normal file
191
src/dev/arm/GenericTimer.py
Normal 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
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
@@ -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__
|
||||
|
||||
76
src/dev/arm/generic_timer_miscregs_types.hh
Normal file
76
src/dev/arm/generic_timer_miscregs_types.hh
Normal 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__
|
||||
Reference in New Issue
Block a user