There are two Arm instructions that allow access to system registers. These are MSR to write a system register and MRS to read a system register. These are the only two instructions required for counting.
If you only need to count time/cycles, then the system counter can be used. You can do this from user space. An example of measuring system counter ticks across a function is shown below:
Use a text editor to create a file named syscnt.c
with the code below:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
// The function we are interested in counting through (see main)
void code_to_measure(){
int sum = 0;
for(int i = 0; i < 1000000000; ++i){
sum += 1;
}
}
int main() {
uint64_t syscnt_freq = 0;
uint64_t syscnt_before, syscnt_after;
// Get frequency of the system counter
asm volatile("mrs %0, cntfrq_el0" : "=r" (syscnt_freq));
// Read system counter
asm volatile("mrs %0, cntvct_el0" : "=r" (syscnt_before));
// This is what we are counting through
code_to_measure();
// Read system counter
asm volatile("mrs %0, cntvct_el0" : "=r" (syscnt_after));
// Calculate results and print to stdout
uint64_t syscnt_ticks = syscnt_after - syscnt_before;
printf("System counter ticks: %"PRIu64"\n", syscnt_ticks);
printf("System counter freq (Hz): %"PRIu64"\n", syscnt_freq);
return 0;
}
This method only requires access to two registers,
cntfrq_el0
and
cntvct_el0
. cntfrq_el0
contains the frequency at which the system counter increments in Hz. cntvct_el0
contains the counter value. These registers can be used to measure real time because they are not affected by power management mechanisms like frequency scaling and are always on, even when the cores are put to sleep.
Compile the example using the GNU compiler:
gcc syscnt.c -o syscnt
Run the application:
./syscnt
The output will be similar to:
System counter ticks: 280201338
System counter freq (Hz): 121875000
Your counter values may be different from the output above.