The processor timers

This table shows the processor timers:

Timer name When is the timer present?
EL1 physical timer Always
EL1 virtual timer
Always
Non-secure EL2 physical timer
Implements EL2
Non-secure EL2 virtual timer
Implements ARMv8.1-VHE
EL3 physical timer
Implements EL3
Secure EL2 physical timer
Implements ARMv8.4-SecEL2
Secure EL2 virtual timer
Implements ARMv8.4-SecEL2

Count and frequency

The CNTPCT_EL0 system register reports the current system count value.

Reads of CNTPCT_EL0 can be made speculatively. This means that they can be read out of order regarding the program flow. This could be important in some cases, for example comparing timestamps. When the ordering of the counter read is important, an ISB can be used, as the following code shows:

loop:           // Polling for some communication to indicate a requirement to read
                // the timer
  LDR X1, [X2]
  CBZ x1, loop
  ISB           // Without this, the CNTPCT could be read before the memory location in
                // [X2] has had the value 0 written to it
  MRS X1, CNTPCT_EL0

CNTFRQ_EL0 reports the frequency of the system count. However, this register is not populated by hardware. The register is write-able at the highest implemented Exception level and readable at all Exception levels. Firmware, typically running at EL3, populates this register as part of early system initialization. Higher-level software, like an operating system, can then use the register to get the frequency.

Timer registers

Each timer has the following three system registers:

Register Purpose
<timer>_CTL_EL<x>
Control register
<timer>_CVAL_EL<x>
Comparator value
<timer>_TVAL_EL<x>
 Timer value

In the register name, <timer> identifies which timer is being accessed. The following table shows the possible values:

Timer Register prefix EL<x>
EL1 physical timer
CNTP
EL0
EL1 virtual time CNTV
EL0
Non-secure EL2 physical timer CNTHP
EL2
Non-secure EL2 virtual timer CNTHV
EL2
EL3 physical timer CNTPS
EL1
Secure EL2 physical timer CNTHPS
EL2
Secure EL2 virtual timer CNTHVS
EL2

For example, CNTP_CVAL_EL0 is the Comparator register of the EL1 physical timer.

Test yourself: What is the name of the control register for the EL3 physical timer and Non-secure EL2 virtual timer?

Accessing the timers

For some timers, it is possible to configure which Exception levels can access the timer:

  • EL1 Physical and Virtual Timers: EL0 access to these timers is controlled by CNTKCTL_EL1.
  • EL2 Physical and Virtual Timers: When HCR_EL2.{TGE,E2H}=={1,1}, EL0 access to these timers is controlled by CNTKCTL_EL2. These timers were added as part of the support for the Armv8.1-A Virtualization Host Extension, which is beyond the scope of this guide
  • EL3 physical timer: S.EL1 and S.EL2 access to this timer is controlled by SCR_EL3.ST.

Configuring a timer

There are two ways to configure a timer, either using the comparator (CVAL) register, or using the timer (TVAL) register.

The comparator register, CVAL, is a 64-bit register. Software writes a value to this register and the timer triggers when the count reaches, or exceeds, that value, as you can see here:

Timer Condition Met: CVAL <= System Count

The timer register, TVAL, is a 32-bit register. When software writes TVAL, the processor reads the current system count internally, adds the written value, and then populates CVAL:

CVAL = TVAL + System Counter 
Timer Condition Met: CVAL <= System Count

You can see this populating of CVAL in software. If you read the current system count, write 1000 to TVAL, and then read CVAL, you will see that CVAL is approximately 1000 + system count. The count is approximate, because time will move on during the instruction sequence.

Reading TVAL back will show it decrementing down to 0, while the system count increments. TVAL reports a signed value, and will continue to decrement after the timer fires, which allows software to determine how long ago the timer fired. TVAL and CVAL gives software two different models for how to use the timer. If software needs a timer event in X ticks of the clock, software can write X to TVAL. Alternatively, if software wants an event when the system count reaches Y, software can write Y to CVAL.

Remember that TVAL and CVAL are different ways to program the same timer. They are not two different timers.

Interrupts

Timers can be configured to generate an interrupt. The interrupt from a core’s timer can only be delivered to that core. This means that the timer of one core cannot be used to generate an interrupt that targets a different core.

The generation of interrupts is controlled through the CTL register, using these fields:

  • ENABLE - Enables the timer.
  • IMASK - Interrupt mask. Enables or disables interrupt generation.
  • ISTATUS - When ENABLE==1, reports whether the timer is firing(Cval <= System Count).

To generate an interrupt, software must set ENABLE to 1 and clear IMASK to 0. When the timer fires (CVAL <= System Count), an interrupt signal is asserted to the interrupt controller. In Armv8-A systems, the interrupt controller is usually a Generic Interrupt Controller (GIC).

The interrupt ID (INTID) that is used for each timer is defined by the Server Base System Architecture (SBSA), shown here:

Timer SBSA recommended INTID
EL1 Physical Timer
30
EL1 Virtual Timer
27
Non-secure EL2 Physical Timer 26
Non-secure EL2 Virtual Timer 28
EL3 Physical Timer 29
Secure EL2 Physical Timer 20
Secure EL2 Virtual Timer 19

Note: These INTIDs are in the Private Peripheral Interrupt (PPI) range. These INTIDs are private to a specific core. This means that each core sees its EL1 physical timer as INTID 30. This is described in more detail in our Generic Interrupt Controller guide.

The interrupts generated by the timer behave in a level-sensitive manner. This means that, once the timer firing condition is reached, the timer will continue to signal an interrupt until one of the following situations occurs:

  • IMASK is set to one, which masks the interrupt.
  • ENABLE is cleared to 0, which disables the timer.
  • TVAL or CVAL is written, so that firing condition is no longer met.

When writing an interrupt handler for the timers, it is important that software clears the interrupt before deactivating the interrupt in the GIC. Otherwise the GIC will re-signal the same interrupt again.

The operation and configuration of the GIC is beyond the scope of this guide.

Timer virtualization

Earlier, we introduced the different timers that are found in a processor. These timers can be divided into two groups: virtual timers and physical timers.

Physical timers, like the EL3 physical timer, CNTPS, compare against the count value provided by the System Counter. This value is referred to as the physical count and is reported by CNTPCT_EL0.

Virtual timers, like the EL1 Virtual Timer, CNTV, compare against a virtual count. The virtual count is calculated as:

Virtual Count = Physical Count - <offset>

The offset value is specified in the register CNTVOFF_EL2, which is only accessible at EL2 or EL3. This configuration is shown in the following diagram:

System Counter block diagram

Note: If EL2 not implemented, the offset is fixed as 0. This means that the virtual and physical count values are always the same.

The virtual count allows a hypervisor to show virtual time to a Virtual Machine (VM). For example, a hypervisor could use the offset to hide the passage of time when the VM was not scheduled. This means that the virtual count can represent time experienced by the VM, rather than wall clock time.

Event stream

The Generic Timer can also be used to generate an event stream as part of the Wait for Event mechanism. The WFE instruction puts the core into a low power state, with the core woken by an event.

Details about the WFE mechanism are beyond the scope of this guide.

There are several ways to generate an event, including:

  • Executing the SEV (Send Event) instruction on a different core
  • Clearing the Global Exclusive Monitor of the core
  • Using the Event stream from the core’s the Generic Timer

The Generic Timer can be configured to generate a stream of events at a regular interval. One use for this configuration is to generate a timeout. WFE is typically used when waiting for a resource to become available, when the wait is not expected to be long. The event stream from the timers means that the maximum time that the core will stay in the low power state is bounded.

An event stream can be generated from the physical count, CNTPCT_EL0, or from the virtual count, CNTPVT_EL0:

  • CNTKCTL_EL1 - Controls event stream generation from CNTVCT_EL0
  • CNTKCTL_EL2 - Controls event stream generation from CNTPCT_EL0

For each register, the controls are:

  • EVNTEN   Enables or disables the generation of events
  • EVNTI    Controls the rate of events
  • EVNTDIR  Controls when the event is generated

The control EVNTI specifies a bit position in the range 0 to 15. When the bit at the selected position changes, an event is generated. For example, if EVNTI is set to 3 then an event is generated when bit[3] of the count changes.

The control EVNTDIR controls whether the event is generated when the selected bit transitions from 1-to-0 or from 0-to 1.

Summary table

This table summarizes the information about the different timers discussed in this section:

Timer Registers Typically used by Trappable? Using counter INTID
EL1 Physical Timer
CNTP_<>_EL0**
EL0 and EL1
To EL2 CNTPCT_EL0
30
EL2 Non-secure Physical Timer
CNTHP_<>_EL2
NS.EL2 - CNTPCT_EL0
26
EL2 Secure Physical Timer
CNTHPS_<>_EL2
S.EL2
- CNTPCT_EL0
20
EL3 Physical Timer
CNTPS_<>_EL1
S.EL1 and EL3 To EL3 CNTPCT_EL0
29
EL1 Virtual Timer
CNTV_<>_EL0**
EL0 and EL1 - CNTPCT_EL0
27
EL2 Non-secure Virtual Timer
CNTHV_<>_EL2
NS.EL2 - CNTPCT_EL0*
28
EL2 Secure Virtual Timer
CNTHVS_<>_EL2
S.EL2 - CNTPCT_EL0*
19

*For these timers, the virtual offset (CNTVOFFSET_EL2) always behaves as 0. Therefore, although these timers compare against the virtual count value, they are in practice using the physical counter value.

** Subjects to re-direction when HCR_EL2.E2H==1.

Previous Next