Using the Cycle Counter Registers on the Raspberry Pi 3

ARM processors support various performance monitoring registers, the most basic being a cycle count register. This is how to make use of it on the Raspberry Pi 3 with its ARM Cortex-A53 processor. The A53 implements the ARMv8 architecture which can operate in both 64- and 32-bit modes, the Pi 3 uses the 32-bit AArch32 mode, which is more or less backwards compatible with the ARMv7-A architecture, as implemented for example by the Cortex-A7 (used in the early Pi 2’s) and Cortex-A8. I hope I’ve got that right, all these names are confusing

The performance counters are made available through coprocessor registers and the mrc and mcr instructions, the precise registers used depending on the particular architecture.

By default, use of these instructions is only possible in “privileged” mode, ie. from the kernel, so the first thing we need to do is to enable register access from userspace. This can be done through a simple kernel module that can also set up the cycle counter parameters needed (we could do this from userspace after the kernel module has enabled access, but it’s simpler to do everything at once).

To compile a kernel module, you need a set of header files compatible with the kernel you are running. Fortunately, if you have installed a kernel with the raspberrypi-kernel package, the corresponding headers should be in raspberrypi-kernel-headers – if you have used rpi-update, you may need to do something else to get the right headers, and of course if you have built your own kernel, you should use the headers from there. So:

$ sudo apt-get install raspberrypi-kernel
$ sudo apt-get install raspberrypi-kernel-headers

Our Makefile is just:

obj-m += enable_ccr.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

and the kernel module source is:

#include <linux/module.h>
#include <linux/kernel.h>

void enable_ccr(void *info) {
  // Set the User Enable register, bit 0
  asm volatile ("mcr p15, 0, %0, c9, c14, 0" :: "r" (1));
  // Enable all counters in the PNMC control-register
  asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(1));
  // Enable cycle counter specifically
  // bit 31: enable cycle counter
  // bits 0-3: enable performance counters 0-3
  asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x80000000));
}

int init_module(void) {
  // Each cpu has its own set of registers
  on_each_cpu(enable_ccr,NULL,0);
  printk (KERN_INFO "Userspace access to CCR enabled\n");
  return 0;
}

void cleanup_module(void) {
}

To build the module, just use make:

$ make

and if all goes well, the module itself should be built as enable_ccr.ko

Install it:

$ sudo insmod enable_ccr.ko

$ dmesg | tail

should show something like:

...
[ 430.244803] enable_ccr: loading out-of-tree module taints kernel.
[ 430.244820] enable_ccr: module license 'unspecified' taints kernel.
[ 430.244824] Disabling lock debugging due to kernel taint
[ 430.245300] User-level access to CCR has been turned on
...

It should go without saying that making your own kernel modules & allowing normally forbidden access from userspace may result in all sorts of potential vulnerabilities that you should be wary of).

Now we can use the cycle counters in user code:

#include <stdio.h>
#include <stdint.h>

static inline uint32_t ccnt_read (void)
{
  uint32_t cc = 0;
  __asm__ volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (cc));
  return cc;
}

int main() {
  uint32_t t0 = ccnt_read();
  uint32_t t1 = ccnt_read();       
  printf("%u\n", t1-t0);
  volatile uint64_t n = 100000000;
  while(n > 0) n--;
  t1 = ccnt_read();
  printf("%u\n", t1-t0);
}

We use a volatile loop counter so the loop isn’t optimized away completely.

Using taskset to keep the process on one CPU:

$ gcc -Wall -O3 cycles.c -o cycles
pi@pi3:~/raspbian-ccr$ time taskset 0x1 ./cycles
1
805314304

real 0m0.712s
user 0m0.700s
sys 0m0.010s

Looks like we can count a single cycle and since the Pi 3 has a 1.2GHz clock the loop time looks about right (the clock seems to be scaled if the processor is idle so we don’t necessarily get a full 1.2 billion cycles per second – for example, if we replace the loop above with a sleep).

References:

ARMv8 coprocessor registers:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/Bgbjjhaj.html

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0344k/Bgbdeggf.html

A useful forum discussion (which includes some details on accessing other performance counters):

https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=181490

Cycle counters on the Pi 1:

High-Resolution Timing on the Raspberry Pi


47 Comments on “Using the Cycle Counter Registers on the Raspberry Pi 3”

  1. Mr. K says:

    There is a small typo. In your Makefile you address enable-ccr.o (hyphen) while you suggest to name the C file enable_ccr.c (underscore).

  2. David says:

    Help! I copied the above snippets to text editor and saved the files as makefile and enable_ccr.c respectively. I learned that in the makefile, you HAVE to use TAB instead of spacing on the line below all: and clean: respectively from a google search, but now I’m stuck with this after using the make command:

    pi@raspberrypi:~ $ make
    make -C /lib/modules/4.14.79-v7+/build M=/home/pi modules
    make[1]: Entering directory ‘/usr/src/linux-headers-4.14.79-v7+’
    scripts/Makefile.build:45: /home/pi/Makefile: No such file or directory
    make[2]: *** No rule to make target ‘/home/pi/Makefile’. Stop.
    Makefile:1527: recipe for target ‘_module_/home/pi’ failed
    make[1]: *** [_module_/home/pi] Error 2
    make[1]: Leaving directory ‘/usr/src/linux-headers-4.14.79-v7+’
    makefile:4: recipe for target ‘all’ failed
    make: *** [all] Error 2

    • David says:

      Ok, I solved my own problem:)

      first, I needed to save makefile as Makefile (I assumed capitalization wouldn’t matter :\

      then, I found on line 1 of the Makefile, it says …enable-ccr.o… which needs to be changed to …enable_ccr.o

      Once I made those changes, it ran correctly.

  3. matthew says:

    Thanks very much for your comments. Yes, make is fussy about tabs – the original had them, but WordPress seems to have replaced them with spaces, I’ve added a note.

    And thanks for pointing out the typo with enable_ccr, sorry about that. Fixed now.

  4. badger says:

    Hi. It looks like the include files are missing in your code.
    There is definitely something weird going on with your wordpress.

  5. hammaster says:

    I see that you’ve mentioned rpi-update and new headers. What is the kernel version that you’ve been using?

    • matthew says:

      Currently my pi3 is on 4.14.98 (the latest for stretch) – I’m not using rpi-update, I tried it a while back and had some problems getting the right version of kernel headers, but the situation could well be different now, I haven’t been keeping up.

  6. pi@raspberrypi:~ $ make
    make -C /lib/modules/5.4.50-v7l+/build M=/home/pi modules
    make[1]: Entering directory ‘/lib/modules/5.4.50-v7l+/build’
    make[1]: *** No rule to make target ‘modules’. Stop.
    make[1]: Leaving directory ‘/lib/modules/5.4.50-v7l+/build’
    make: *** [Makefile:4: all] Error 2

    • matthew says:

      Don’t know what your problem there might be – did the raspberrypi-kernel and raspberrypi-kernel-headers packages install correctly?

      The Makefile and enable_ccr.c files should all be in the same directory, eg:

      pi@pi3:~/code$ pwd
      /home/pi/code
      pi@pi3:~/code$ ls
      enable_ccr.c  Makefile
      pi@pi3:~/code$ make
      make -C /lib/modules/5.4.51-v7+/build M=/home/pi/code modules
      make[1]: Entering directory '/usr/src/linux-headers-5.4.51-v7+'
        CC [M]  /home/pi/code/enable_ccr.o
        Building modules, stage 2.
        MODPOST 1 modules
      WARNING: modpost: missing MODULE_LICENSE() in /home/pi/code/enable_ccr.o
      see include/linux/module.h for more information
        CC [M]  /home/pi/code/enable_ccr.mod.o
        LD [M]  /home/pi/code/enable_ccr.ko
      make[1]: Leaving directory '/usr/src/linux-headers-5.4.51-v7+'
      pi@pi3:~/code$ sudo insmod enable_ccr.ko 
      pi@pi3:~/code$ dmesg | tail
      [  102.113640] enable_ccr: loading out-of-tree module taints kernel.
      [  102.113662] enable_ccr: module license 'unspecified' taints kernel.
      [  102.113669] Disabling lock debugging due to kernel taint
      [  102.114094] Userspace access to CCR enabled
      [  604.062922] Userspace access to CCR enabled
      pi@pi3:~/code$ 
      
  7. George Troulis says:

    For whatever reason, my gcc (Raspbian 8.3.0-6+rpi1) 8.3.0) did not generate inline assembly code for `ccnt_read(),` but created it as a normal function that it called via `bl`.

    As a workaround, I defined `ccnt_read()` as a macro, and used it as follows:
    “`
    #define CCNT_READ(N) __asm__ volatile (“mrc p15, 0, %0, c9, c13, 0″:”=r” (N));

    uint32_t t0, t1;
    CCNT_READ(t0);
    CCNT_READ(t1);
    “`

    It compiles and works normally, and checking the assembly reveals that it indeed generates a single inline `mrc` instruction

  8. Thank you Matthew for the tutorial. Suppose I want to enable the Cycle Count Registers on a Raspberry Pi 3 but with aarch64 instead of 32-bit. How different would the kernel module and cycle program be?

    • matthew says:

      It looks like the way of accessing the timer control registers has changed in aarch64 – the ‘mcr’ & ‘mrc’ instruction has been replaced by ‘msr’ and ‘mrs’ (which do seem to take more sensible arguments, at least).

    • Danijel Domazet says:

      To see the difference between 64 and 32 bit assembly, look into Arm Cortex-53 Processor Technical Reference Manual, PMU unit:
      https://developer.arm.com/documentation/ddi0500/j/Performance-Monitor-Unit/AArch64-PMU-register-descriptions/Performance-Monitors-Control-Register?lang=en

      • matthew says:

        Thanks Danijel – very useful link. Looks like there is a straightforward mapping between 32- and 64-bit registers. I had a play with the new registers on my Pi3 in 64-bit mode:

        void enable_ccr(void *info) {
          // Set the User Enable register, bit 0
          asm volatile ("msr pmuserenr_el0, %0\t\n" :: "r" (1));
        }
        

        should work for the kernel module and:

        static inline void ccnt_init (void) {
          // 0: Enable all counters in the PMNC control-register
          // 2: Reset CCNT to zero
          // 3: if 1, divide by 64
          uint32_t cr = 0;
          asm volatile ("mrs %0, pmcr_el0\t\n" : "=r"(cr));
          cr |= (1U<<0); // set bit 0 - enable ccnt
          cr |= (1U<<2); // set bit 2 - clear ccnt
          cr &= ~(1U<<3); // clear bit 3
          asm volatile ("msr pmcr_el0, %0\t\n" :: "r"(cr));
          // Enable cycle counter specifically
          // bit 31: enable cycle counter
          // bits 0-3: enable performance counters 0-3
          asm volatile ("mrs %0, pmcntenset_el0\t\n" : "=r"(cr));
          cr |= 0x80000000;
          asm volatile ("msr pmcntenset_el0, %0\t\n" :: "r"(cr));
        }
        
        static inline uint32_t ccnt_read (void)
        {
          uint32_t cc = 0;
          __asm__ volatile ("mrs %0, pmccntr_el0\t\n":"=r"(cc));
          return cc;
        }
        

        is an improved version of the user code – I’ve moved the setup into there rather than doing it in the kernel module (other things like perf can mess with the timer setup, which can be confusing).

    • Danijel Domazet says:

      Make sure you scroll all the way down to the bottom of that Manual page.

  9. Danijel Domazet says:

    Hi Matthew,
    This works great on RPi3 (thanks!) which is ARM Cortex-A5, ARMv7 arhictecture.

    Will this code work on other ARM Cortex? Which ARM info is important when trying to find out if this code will work?

    • matthew says:

      Thanks Danijel! I think this should work in for ARMv7 and ARMv8 32-bit architectures, but can’t say I fully understand the documentation on all this. aarch64 uses different instructions to access the system registers, but apart from that, things seem to work the same.

  10. AL says:

    Hi,

    I ran into a couple problems.

    After the command ” sudo apt-get install raspberrypi-kernel-headers”, I get the headers installed, per the below, but I don’t know where the enable_ccr module is so that I can build and install it.

    Basically, I get an error on linking in ccnt_read(), as documented below.


    $ uname -a
    Linux raspberrypi 5.15.32-v8+ #1538 SMP PREEMPT Thu Mar 31 19:40:39 BST 2022 aarch64 GNU/Linux


    $ sudo apt-get install raspberrypi-kernel-headers
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    The following package was automatically installed and is no longer required:
    libfuse2
    Use 'sudo apt autoremove' to remove it.
    The following NEW packages will be installed:
    raspberrypi-kernel-headers
    0 upgraded, 1 newly installed, 0 to remove and 28 not upgraded.
    Need to get 9,675 kB of archives.
    After this operation, 60.6 MB of additional disk space will be used.
    Get:1 http://archive.raspberrypi.org/debian bullseye/main arm64 raspberrypi-kernel-headers arm64 1:1.20220331-1 [9,675 kB]
    Fetched 9,675 kB in 3s (3,682 kB/s)
    Selecting previously unselected package raspberrypi-kernel-headers.
    (Reading database ... 98949 files and directories currently installed.)
    Preparing to unpack .../raspberrypi-kernel-headers_1%3a1.20220331-1_arm64.deb ...
    Unpacking raspberrypi-kernel-headers (1:1.20220331-1) ...
    Setting up raspberrypi-kernel-headers (1:1.20220331-1) ...

    I include the below to my main.c, and it compiles fine.


    static inline uint32_t ccnt_read (void)
    {
    uint32_t cc = 0;
    __asm__ volatile ("mrc p15, 0, %0, c9, c13, 0":"=r" (cc));
    return cc;
    }

    But when I try to call ccnt_read(), I get the below errors.

    /tmp/ccOyPemS.s: Assembler messages:
    /tmp/ccOyPemS.s:153: Error: unknown mnemonic `mrc’ — `mrc p15,0,x19,c9,c13,0′
    /tmp/ccOyPemS.s:236: Error: unknown mnemonic `mrc’ — `mrc p15,0,x20,c9,c13,0′

    • matthew says:

      Looks like you are using a 64-bit OS – see comments above for getting things to work there (the instructions for accessing the timer registers have changed).

      • AL says:

        Thanks! After using the below, per your suggestion, the assembler does not complain about the unknown `mrc` instruction and is able to link the program. HOWEVER, when running the program, I get ‘illegal instruction’ printed out. I take that I need to build the kernel module and install it, so that the enable_ccr(void *info) could be called. My original post says after the command “sudo apt-get install raspberrypi-kernel-headers”, things get installed but I don’t see any source file, e.g., Makefile, etc., in order to build and install the enable_ccr module.

        void enable_ccr(void *info) {
        // Set the User Enable register, bit 0
        asm volatile (“msr pmuserenr_el0, %0\t\n” :: “r” (1));
        }

        should work for the kernel module and:

        static inline void ccnt_init (void) {
        // 0: Enable all counters in the PMNC control-register
        // 2: Reset CCNT to zero
        // 3: if 1, divide by 64
        uint32_t cr = 0;
        asm volatile (“mrs %0, pmcr_el0\t\n” : “=r”(cr));
        cr |= (1U<<0); // set bit 0 – enable ccnt
        cr |= (1U<<2); // set bit 2 – clear ccnt
        cr &= ~(1U<<3); // clear bit 3
        asm volatile ("msr pmcr_el0, %0\t\n" :: "r"(cr));
        // Enable cycle counter specifically
        // bit 31: enable cycle counter
        // bits 0-3: enable performance counters 0-3
        asm volatile ("mrs %0, pmcntenset_el0\t\n" : "=r"(cr));
        cr |= 0x80000000;
        asm volatile ("msr pmcntenset_el0, %0\t\n" :: "r"(cr));
        }

        static inline uint32_t ccnt_read (void)
        {
        uint32_t cc = 0;
        __asm__ volatile ("mrs %0, pmccntr_el0\t\n":"=r"(cc));
        return cc;
        }

      • matthew says:

        You need to make a Makefile with the text described in the original post (and an enable_ccr.c file with the 64 bit version of the function).

      • AL says:

        Thanks again, Matthew.

        I made the Makefile and attempted the build. A enable_ccr.o from the enable_ccr.c is produced NOT enable_ccr.ko file. I think the problem is that I don’t have the entire source code after doing the command “sudo apt-get install raspberrypi-kernel-headers”. I only have the enable_ccr.c that I created for the 64-bit (AARCH64) of the OS.

        Could you please post the zip file of the entire source, and I can try again? Thanks!

        $ sudo insmod enable_ccr.o
        insmod: ERROR: could not insert module enable_ccr.o: Invalid module format

        The `make` command produces the below output.

        $ make
        make -C /lib/modules/5.15.32-v8+/build M=/home/test/cycles modules
        make[1]: Entering directory ‘/usr/src/linux-headers-5.15.32-v8+’
        CC [M] /home/test/cycles/enable_ccr.o
        MODPOST /home/test/cycles/Module.symvers
        ERROR: modpost: missing MODULE_LICENSE() in /home/test/cycles/enable_ccr.o
        make[2]: *** [scripts/Makefile.modpost:134: /home/test/cycles/Module.symvers] Error 1
        make[2]: *** Deleting file ‘/home/test/cycles/Module.symvers’
        make[1]: *** [Makefile:1783: modules] Error 2
        make[1]: Leaving directory ‘/usr/src/linux-headers-5.15.32-v8+’
        make: *** [Makefile:4: all] Error 2

        Here is my Makefile.

        obj-m += enable_ccr.o

        all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

        clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

        Checking the produced *.o file.

        $ file enable_ccr.o
        enable_ccr.o: ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), not stripped

      • matthew says:

        The problem presumably is this error, which I don’t think I’ve seen before & don’t know about.

        ERROR: modpost: missing MODULE_LICENSE() in /home/test/cycles/enable_ccr.o

      • Danijel Domazet says:

        Matthew, you can resolve that problem simply with adding this to the top of your .c file:

        MODULE_LICENSE(“GPL”);
        MODULE_AUTHOR(“You”);
        MODULE_DESCRIPTION(“Enables access to PMU unit on Arm devices.”);

      • matthew says:

        That looks like the right thing to do, thanks.

      • Danijel Domazet says:

        I had some problems with the Makefile where I had to replace all $(PWD) with $(CURDIR). Maybe that info helps someone.

      • matthew says:

        Aha, thanks – looks like CURDIR is the correct Gnu Make way to do things, the PWD variable comes from the shell, and might be missing or have the wrong definition, depending on the shell.

  11. AL says:

    Danijel Domazat wrote:

    “you can resolve that problem simply with adding this to the top of your .c file:

    MODULE_LICENSE(“GPL”);
    MODULE_AUTHOR(“You”);
    MODULE_DESCRIPTION(“Enables access to PMU unit on Arm devices.”);”

    That was a great hint! Thanks! To be precise, place the above macro calls AFTER the below include statements.

    #include
    #include

    I am able to compile the enable_ccr.c now for the AARCH64, and the enable_ccr.ko and its .o files are produced.

    To summarize:

    1) Your the AARCH64 enable_ccr.c should have the below content.

    #include
    #include

    MODULE_LICENSE(“GPL”);
    MODULE_AUTHOR(“You”);
    MODULE_DESCRIPTION(“Enables access to PMU unit on Arm devices.”);”

    void enable_ccr(void *info) {
    // Set the User Enable register, bit 0
    asm volatile (“msr pmuserenr_el0, %0\t\n” :: “r” (1));
    }

    int init_module(void) {
    // Each cpu has its own set of registers
    on_each_cpu(enable_ccr,NULL,0);
    printk (KERN_INFO “Userspace access to CCR enabled\n”);
    return 0;
    }

    void cleanup_module(void) {
    }

    2) Your OWN module/user code should have the below.

    static inline void ccnt_init (void) {
    // 0: Enable all counters in the PMNC control-register
    // 2: Reset CCNT to zero
    // 3: if 1, divide by 64
    uint32_t cr = 0;
    asm volatile (“mrs %0, pmcr_el0\t\n” : “=r”(cr));
    cr |= (1U<<0); // set bit 0 – enable ccnt
    cr |= (1U<<2); // set bit 2 – clear ccnt
    cr &= ~(1U<<3); // clear bit 3
    asm volatile ("msr pmcr_el0, %0\t\n" :: "r"(cr));
    // Enable cycle counter specifically
    // bit 31: enable cycle counter
    // bits 0-3: enable performance counters 0-3
    asm volatile ("mrs %0, pmcntenset_el0\t\n" : "=r"(cr));
    cr |= 0x80000000;
    asm volatile ("msr pmcntenset_el0, %0\t\n" :: "r"(cr));
    }

    static inline uint32_t ccnt_read (void)
    {
    uint32_t cc = 0;
    __asm__ volatile ("mrs %0, pmccntr_el0\t\n":"=r"(cc));
    return cc;
    }

    3) The Makefile should have the below content.

    obj-m += enable_ccr.o

    all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

    clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

  12. AL says:

    the two includes are not listed in my previous post due to mark-up language, but they are from the original code of this article.

    linux/module.h
    linux/kernel.h

  13. AL says:

    Damn! I still get “Illegal instruction” when running my program after I have done all of the above, generating the .ko file and patch it in via ‘sudo insmod’. I declared victory prematurely!!!

    • AL says:

      Ha! I take that back! If you reboot your RaspberryPI, the “Userspace access to CCR” is reset! So you need to “sudo insmod enable_ccr.ko” AGAIN! I found out by doing “dmesg | tail” and not seeing the enabling of the CCR count after rebooting my RaspberryPI!

      I guess the kernel is smart enough not to make the patching permanently, at least, on the the 64-bit RaspberryPI 3b OS.

      • matthew says:

        That’s right – there are invocations to load the module on startup – check out /etc/modules.

        Thanks very much for the summary & thanks for persevering!

      • AL says:

        Matthew wrote:

        “That’s right – there are invocations to load the module on startup – check out /etc/modules.

        Thanks very much for the summary & thanks for persevering!”

        No problem. My pleasure and thanks for doing this to start with. Very interesting.

        One thing interesting of counter difference with the below loop. I always get 1 for the t1-t0 difference. Surely the floating point add for 8 elements should really take more than one clock cycle count.

        So, that’s the puzzle of the day.

        float outBuf[8] = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};
        float inBuf[8] = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1};

        uint32_t t0 = ccnt_read();
        for (uint8_t i = 0; i < 8; i++)
        {
        outBuf[i] += inBuf[i];
        }

        uint32_t t1 = ccnt_read();

        printf("%u\n", t1-t0);

      • Danijel Domazet says:

        *** To install the kernel module permanently:

        ** Make a directory in the /lib/modules/5.10.63-v7+/kernel/drivers/ (use uname -r command to find out the kernel version; in this case it gives the above “5.10.63-v7+” directory name):

        `sudo mkdir /lib/modules/5.10.63-v7+/kernel/drivers/enable_ccr`

        ** Copy the `enable_ccr.ko` to created directory:

        `sudo cp enable_ccr.ko /lib/modules/5.10.63-v7+/kernel/drivers/enable_ccr`

        ** Add the module name to file `/etc/modules` (without .ko extension!)

        `sudo nano /etc/modules`

        ** Restart the machine

        `sudo reboot`

        ** After restart, list all the kernel modules:

        `lsmod`

        ** If the kernel has been updated, the installation process needs to be repeated for the new kernel version. Make sure to run `sudo depmod` to re-generate the module dependencies for the new kernel version.

  14. AL says:

    One more question:

    uint32_t t0 = ccnt_read();

    What is the time granularity returned for each count in ‘t0’, that is, each tick happens? This obviously depends on the CPU clock. So, is it basically the 1/CPU_CLOCK_FREQ?

    Thanks

    • AL says:

      Eureka! Make sure the loop counter of whatever you do is declared with `volatile` keyword, as mentioned in the doc, so that the counter reads are correct, since the loop itself won’t optimized away by the compiler!

      • matthew says:

        You’ve got it – and you can always use the -S option for gcc or clang to see the exact assembler being generated.

    • matthew says:

      It’s the instruction counter more or less, though there is a divde-by-64 option (bit 3 of the control register – see above), and funny things can happen with low power mode or if your process hops between cores (which have separate counters). Caveat emptor and all that.

      • AL says:

        I believe you’re right. Although it seems to be working, the result, run after run, can be different even with the below simple test. I am not using low power mode, but it looks like I need to peg the below test into an allocated core, of the four cores the Cortex-A53 has. I am wondering if the ‘volatile’ on the loop counter cause the variation in results, since ‘i’ must be fetched from its memory location on every iteration rather than cached and increased by the compiler’s code generation. I’ll check the difference between non-volatile and volatile in code generation with the -S option.

        If anyone has a chancce to try the below, with either with ARMv7 or ARMv8 stuff that I posted above, please let me know if you see variability in the timing results. Thanks!

        float outBuf[8] = { /* fill in random float numbers */ };
        float inBuf[8] = { /* fill in random float numbers */};

        uint32_t t0 = ccnt_read();
        volatile uint32_t i = 0;
        for (i = 0; i < 8; i++)
        {
        outBuf[i] += inBuf[i];
        }

        uint32_t t1 = ccnt_read();

        printf("%u\n", t1-t0);

      • AL says:

        Meh! Updated my Raspberry Pi 3 to the below Linux version, and the ticker count routine no longer works. It always has zero for the count!

        $ uname -a
        Linux raspberrypi 5.15.74-v8+ #1595 SMP PREEMPT Wed Oct 26 11:07:24 BST 2022 aarch64 GNU/Linux

      • matthew says:

        That’s not good – is there anything in syslog? Perhaps the kernel module is failing for some reason. I don’t have an RPi system running at the moment, so can’t investigate here right now.

      • Anonymous says:

        Ok. Did another update and rebooted and looks ok now. Maybe a new SD card needed 🙂

      • matthew says:

        Excellent, I like bugs that fix themselves!


Leave a Comment