[[ext:zicntr]]
=== ext:zicntr[] Extension for Base Counters and Timers

[#norm:zihpm_op_sz_mode_acc_count]#RISC-V ISAs provide a set of up to thirty-two 64-bit performance
counters and timers# that are [#norm:zihpm_op_sz_mode_acc_priv]#accessible via unprivileged XLEN-bit
read-only CSR registers `0xC00`–`0xC1F`# ([#norm:zihpm_op_sz_mode_acc_xlen32]#when XLEN=32, the upper 32 bits
are accessed via CSR registers `0xC80`–`0xC9F`#). [#norm:zihpm_op_sz_mode_acc_partition]#These counters are
divided between the ext:zicntr[] and extlink:zihpm[] extensions.#

The ext:zicntr[] standard extension comprises the first three of these
counters (csr:cycle[], csr:time[], and csr:instret[]), which have dedicated functions
(cycle count, real-time clock, and instructions retired, respectively).
The ext:zicntr[] extension depends on the ext:zicsr[] extension.

[NOTE]
====
We recommend provision of these basic counters in implementations as
they are essential for basic performance analysis, adaptive and dynamic
optimization, and to allow an application to work with real-time
streams. Additional counters in the separate ext:zihpm[] extension can help
diagnose performance problems and these should be made accessible from
user-level application code with low overhead.

Some execution environments might prohibit access to counters, for
example, to impede timing side-channel attacks.
====

include::images/wavedrom/counters-diag.edn[]

For base ISAs with XLEN{ge}64, CSR instructions can access
the full 64-bit CSRs directly. In particular, the insn:rdcycle[], insn:rdtime[], and
insn:rdinstret[] pseudoinstructions read the full 64 bits of the csr:cycle[],
csr:time[], and csr:instret[] counters.

[NOTE]
====
The counter pseudoinstructions are mapped to the read-only
insn:csrrs[rd,counter,x0] canonical form, but the other read-only CSR
instruction forms (based on insn:csrrc[]/insn:csrrsi[]/insn:csrrci[]) are also legal ways to
read these CSRs.
====
For base ISAs with XLEN=32, the ext:zicntr[] extension enables the three
64-bit read-only counters to be accessed in 32-bit pieces. The insn:rdcycle[],
insn:rdtime[], and insn:rdinstret[] pseudoinstructions provide the lower 32 bits, and
the insn:rdcycleh[], insn:rdtimeh[], and insn:rdinstreth[] pseudoinstructions provide the
upper 32 bits of the respective counters.
[NOTE]
====
We required the counters be 64 bits wide, even when XLEN=32, as
otherwise it is very difficult for software to determine if values have
overflowed.
The sample code <<zicntr-rv32-sample>> shows how the full 64-bit width value can
be safely read using the individual 32-bit width pseudoinstructions.
====

[#norm:zicntr_rdcycle_op]#The insn:rdcycle[] pseudoinstruction reads the low
XLEN bits of the csr:cycle[] CSR which holds a count of the number of clock
cycles executed by the processor core on which the hart is running from an
arbitrary start time in the past.#
[#norm:zicntr_rdcycleh_op]#insn:rdcycleh[] is only present when XLEN=32 and reads bits 63-32
of the same cycle counter.# The underlying 64-bit counter should never
overflow in practice. The rate at which the cycle counter advances will
depend on the implementation and operating environment. The execution
environment should provide a means to determine the current rate
(cycles/second) at which the cycle counter is incrementing.
[NOTE]
====
insn:rdcycle[] is intended to return the number of cycles executed by the
processor core, not the hart. Precisely defining what is a "`core`" is
difficult given some implementation choices (e.g., AMD Bulldozer).
Precisely defining what is a "`clock cycle`" is also difficult given the
range of implementations (including software emulations), but the intent
is that insn:rdcycle[] is used for performance monitoring along with the other
performance counters. In particular, where there is one hart/core, one
would expect cycle-count/instructions-retired to measure cycles-per-instruction
(CPI) for a hart.

Cores don't have to be exposed to software at all, and an implementer
might choose to pretend multiple harts on one physical core are running
on separate cores with one hart/core, and provide separate cycle
counters for each hart. This might make sense in a simple barrel
processor (e.g., CDC 6600 peripheral processors) where inter-hart timing
interactions are non-existent or minimal.

Where there is more than one hart/core and dynamic multithreading, it is
not generally possible to separate out cycles per hart (especially with
SMT). It might be possible to define a separate performance counter that
tried to capture the number of cycles a particular hart was running, but
this definition would have to be very fuzzy to cover all the possible
threading implementations. For example, should we only count cycles for
which any instruction was issued to execution for this hart, and/or
cycles any instruction retired, or include cycles this hart was
occupying machine resources but couldn't execute due to stalls while
other harts went into execution? Likely, "`all of the above`" would be
needed to have understandable performance stats. This complexity of
defining a per-hart cycle count, and also the need in any case for a
total per-core cycle count when tuning multithreaded code led to just
standardizing the per-core cycle counter, which also happens to work
well for the common single hart/core case.

Standardizing what happens during "`sleep`" is not practical given that
what "`sleep`" means is not standardized across execution environments,
but if the entire core is paused (entirely clock-gated or powered-down
in deep sleep), then it is not executing clock cycles, and the cycle
count shouldn't be increasing per the spec. There are many details,
e.g., whether clock cycles required to reset a processor after waking up
from a power-down event should be counted, and these are considered
execution-environment-specific details.

Even though there is no precise definition that works for all platforms,
this is still a useful facility for most platforms, and an imprecise,
common, "`usually correct`" standard here is better than no standard.
The intent of insn:rdcycle[] was primarily performance monitoring/tuning, and
the specification was written with that goal in mind.
====
[#norm:zicntr_rdtime_op]#The insn:rdtime[] pseudoinstruction reads the low XLEN bits of the csr:time[] CSR,
which counts wall-clock real time that has passed from an arbitrary
start time in the past.# [#norm:zicntr_rdtimeh_op]#RDTIMEH is only present when XLEN=32 and reads
bits 63-32 of the same real-time counter.# The underlying 64-bit counter
increments by one with each tick of the real-time clock, and, for
realistic real-time clock frequencies, should never overflow in
practice. The execution environment should provide a means of
determining the period of a counter tick (seconds/tick). The period
should be constant within a small error bound. The environment should
provide a means to determine the accuracy of the clock (i.e., the
maximum relative error between the nominal and actual real-time clock
periods).
[NOTE]
====
On some simple platforms, cycle count might represent a valid
implementation of insn:rdtime[], in which case insn:rdtime[] and insn:rdcycle[] may return
the same result.

It is difficult to provide a strict mandate on clock period given the
wide variety of possible implementation platforms. The maximum error
bound should be set based on the requirements of the platform.
====

[#norm:zicntr_time_hart_sync]#The real-time clocks of all harts must be synchronized to within one
tick of the real-time clock.#
[NOTE]
====
As with other architectural mandates, it suffices to appear "`as if`"
harts are synchronized to within one tick of the real-time clock, i.e.,
software is unable to observe that there is a greater delta between the
real-time clock values observed on two harts.

If, for example, the real-time clock increments at a frequency of 1 GHz, then
all harts must appear to be synchronized to within 1 ns.
But it is also acceptable for this example implementation to only update the
real-time clock at, say, a frequency of 100 MHz with increments of 10 ticks.
As long as software cannot observe this seeming violation of the above
synchronization requirement, and software always observes time across harts to
be monotonically nondecreasing, then this implementation is compliant.

A platform spec may then, for example, specify an apparent real-time clock
tick frequency (e.g. 1 GHz) and also a minimum update frequency (e.g. 100 MHz)
at which updated time values are guaranteed to be observable by software.
Software may read time more frequently, but it should only observe
monotonically nondecreasing values and it should observe a new value at least
once every 10 ns (corresponding to the 100 MHz update frequency in this
example).
====
[#norm:zicntr_rdinstret_op]#The insn:rdinstret[] pseudoinstruction reads the low XLEN bits of the
csr:instret[] CSR, which counts the number of instructions retired by this
hart from some arbitrary start point in the past.# [#norm:zicntr_rdinstreth_op]#insn:rdinstreth[] is only
present when XLEN=32 and reads bits 63-32 of the same instruction
counter.# The underlying 64-bit counter should never overflow in
practice.
[NOTE]
====
Instructions that cause synchronous exceptions, including insn:ecall[] and
insn:ebreak[], are not considered to retire and hence do not increment the
csr:instret[] CSR.
====
The following code sequence will read a valid 64-bit cycle counter value
into `x3:x2`, even if the counter overflows its lower half between
reading its upper and lower halves.

[[zicntr-rv32-sample]]
[source,asm.]
.Sample code for reading the 64-bit cycle counter when XLEN=32.
    again:
        rdcycleh     x3
        rdcycle      x2
        rdcycleh     x4
        bne          x3, x4, again

//
