System Counter

In What is the Generic Timer, we introduced the System Counter. The System Counter generates the system count value that is distributed to all the cores in the system, as shown in the following diagram:

System counter block diagram

The SoC implementer is responsible for the design of the System Counter. Usually, the System Counter requires some initialization when a system boots up. Arm provides a recommended register interface for the System Counter, but you should check with your SoC implementer for details of a specific implementation.

One physical system count value broadcasts to all cores. This means that all cores share the same view of the passing of time. Consider the following example:

  • Device A reads the current system count and adds it to a message as a timestamp, then sends the message to Device B.
  • When Device B receives the message, it compares the timestamp to the current system count.

In this example, the system count value that is seen by Device B can never be earlier than the timestamp in the message.

The System Counter measures real time. This means that it cannot be affected by power management techniques like Dynamic Voltage and Frequency Scaling (DVFS) or putting cores into a lower power state. The count must continue to increment at its fixed frequency. In practice, this requires the System Counter to be in an always-on power domain.

To save power, the System Counter can vary the rate at which it updates the count. For example, the System Counter could update the count by 10 every 10th tick of the clock. This can be useful when the connected cores are all in low power state. The system count still needs to reflect time advancing, but power can be saved by broadcasting fewer counter updates.

Counter scaling

The option to scale the system count was introduced in Armv8.4-A. Instead of incrementing by one on every tick of the clock, the count can increment by X, where X is configured by software during system initialization. This feature allows the count to effectively increment faster or slower than the frequency of the counter.

To support scaling, the System Counter internally expands the counter value to 88 bits, as you can see in the following diagram:

bit integer part of count image

The count is represented as an 88-bit fixed point number, with 64 bits for the integer part and 24 bits for the fractional part. The integer portion of the count is what is reported by CNTPCT_EL0 on the connected processors. The fractional part is used internally by the System Counter.

The increment amount comes from a 32-bit register called CNTSCR, and its format is shown below:

bit integer part

The increment value is split into an integer part of 8 bits and a fractional part of 24 bits.

When scaling is enabled, on every tick the count is incremented by the value in CNTSCR. For example, if CNTSCR is set to 0x0180_0000, that means that the count increments by 1.5 (integer part 0x01, fraction part 0x80_0000) on every tick. This is illustrated in the following table:


Internal counter value

Integer part / Fractional part

Exported counter value

(Visible via CNTPCT_EL0)

0 0x0000_0000_0000_0000_0000_00
1 0x0000_0000_0000_0001_8000_00
2 0x0000_0000_0000_0003_0000_00
3 0x0000_0000_0000_0004_8000_00
4 0x0000_0000_0000_0006_0000_00
5 0x0000_0000_0000_0007_8000_00
6 0x0000_0000_0000_0009_0000_00

Scaling can only be configured while the System Counter is disabled. Changing whether scaling is enabled, or the scaling factor, while the counter is running can result in unknown count values being returned.

Basic programming

The guidance in this section assumes that the System Counter implements the recommended Arm register interface.

The System Counter provides two register frames: CNTControlBase and CNTReadBase.

The register frame CNTControlBase is used to configure the System Counter and is Secure access only on systems that support TrustZone. The registers in this frame are shown in the following table:

Register Description
Control register, includes:
  • Counter enable
  • Counter scaling enable (Armv8.4-A or later)
  • Update frequency selection
  • Halt-on-debug control. Stops the counter from incrementing when requested by debugger.
CNTSCR Increment value when using scaling (Armv8.4-A or later)
CNTID ID register, reports which features are implemented.
CNTSR Status register. Reports whether the timer is running or stopped.
CNTCV Reports the current count value.
Returns only the integer portion of the count.
CNTFID<n> Reports the available update frequencies.

To enable the System Counter, software must select an update frequency and set the counter enable.

CNTReadBase is a copy of CNTControlBase that only includes the CNTCV register. This means that CNTReadBase only reports the current system count value. However, unlike CNTControlBase, CNTReadBase is accessible to Non-secure accesses. This means that Non-secure software can read the current count, but cannot otherwise configure the System Counter.

Previous Next