ARM Cortex-a8 BeagleBone Black上的计算时钟周期计数

时间:2015-12-04 04:58:57

标签: c linux-kernel arm beagleboneblack

我想计算我的c代码中特定函数的时钟周期计数,该函数将在BeagleBone Black上编译和运行。我不知道怎么做到这一点。我在网上搜索,发现了这条指令:

Arndale板上的时钟读取方法:

步骤1:插入内核模块以启用对PMU计数器的用户空间访问。 解压附件文件“arndale_clockread.tar.bz2”,它有Makefile和enableccnt.c。在Makefile中,用您的内核源目录更改“KERNELDIR”,例如/ usr/src/linux-kernel-version然后运行命令。

linaro@linaro-server:~/enableccnt$ make

上面的命令应该输出为enableccnt.ko,这是内核模块,用于允许用户空间访问PMU计数器。然后运行命令。

linaro@linaro-server:~/enableccnt$ sudo insmod enableccnt.ko

以下命令应显示正在运行的内核中插入了enableccnt模块。

linaro@linaro-server:~/enableccnt$ lsmod

步骤2:从用户空间应用程序中读取计数器。 一旦设置了内核模块。以下功能可用于读取计数器

static void readticks(unsigned int *result)
{   
  struct timeval t;
  unsigned int cc;
  if (!enabled) {
   // program the performance-counter control-register:
    asm volatile("mcr p15, 0, %0, c9, c12, 0" :: "r"(17));
   //enable all counters.
    asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f));
   //clear overflow of coutners
    asm volatile("mcr p15, 0, %0, c9, c12, 3" :: "r"(0x8000000f));
    enabled = 1;
  }
  //read the counter value.
  asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(cc));
  gettimeofday(&t,(struct timezone *) 0);
  result[0] = cc;
  result[1] = t.tv_usec;
  result[2] = t.tv_sec;
}

我相信这条指令适用于任何ARMv7平台。所以,我按照指令更改了内核源目录。这就是Makefile的样子:

KERNELDIR := /usr/src/linux-headers-3.8.13-bone70

obj-m := enableccnt.o
CROSS=arm-linux-gnueabihf-

all:
        CC=arm-cortex_a15-linux-gnueabihf-gcc $(MAKE) ARCH=arm -C $(KERNELDIR) M=`pwd`  CROSS_COMPILE=$(CROSS) -I/lib/arm-linux-gnueabihf/lib

现在,当我运行make时,我发现了这个抱怨arm-linux-gnueabihf-ar的错误:

CC=arm-cortex_a08-linux-gnueabihf-gcc make ARCH=arm -C /usr/src/linux-headers-3.8.13-bone70 M=`pwd`  CROSS_COMPILE=arm-linux-gnueabihf- -I/lib/arm-linux-gnueabihf/
make[1]: Entering directory `/usr/src/linux-headers-3.8.13-bone70'
  LD      /root/crypto_project/Arndale_enableccnt/built-in.o
/bin/sh: 1: arm-linux-gnueabihf-ar: not found
make[2]: *** [/root/crypto_project/Arndale_enableccnt/built-in.o] Error 127
make[1]: *** [_module_/root/crypto_project/Arndale_enableccnt] Error 2
make[1]: Leaving directory `/usr/src/linux-headers-3.8.13-bone70'
make: *** [all] Error 2

我尝试安装arm-linux-gnueabihf-ar,但它不起作用。所以,我不知道我现在应该做什么!

EDIT1 - 正如评论中提到的,我使用以下方法将我的工具链路径添加到我的环境变量中:

export PATH=/path/to/mytoolchain/bin:$PATH

现在我没有得到以前的错误。但是,我遇到了这种语法错误,我认为它与内核头文件有关:

CC=arm-cortex_a15-linux-gnueabihf-gcc make ARCH=arm -C /usr/src/linux-headers-3.8.13-bone70 M=`pwd`  CROSS_COMPILE=arm-linux-gnueabihf- -I/lib/arm-linux-gnueabihf/bin
/root/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/arm-linux-gnueabihf-gcc: 1: /root/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/arm-linux-gnueabihf-gcc: Syntax error: "(" unexpected
make[1]: Entering directory `/usr/src/linux-headers-3.8.13-bone70'
  LD      /root/crypto_project/Arndale_enableccnt/built-in.o
/root/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/arm-linux-gnueabihf-ar: 1: /root/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/arm-linux-gnueabihf-ar: Syntax error: "(" unexpected
make[2]: *** [/root/crypto_project/Arndale_enableccnt/built-in.o] Error 2
make[1]: *** [_module_/root/crypto_project/Arndale_enableccnt] Error 2
make[1]: Leaving directory `/usr/src/linux-headers-3.8.13-bone70'
make: *** [all] Error 2

我想到的唯一合理的解决方案是下载带有头文件的内核源代码并尝试重新制作。有没有人有任何想法解决这个问题?

1 个答案:

答案 0 :(得分:2)

由于可能存在许多障碍,下面是如何构建内核模块和用户空间应用程序的完整指南。

工具链

首先,您需要下载并安装2个工具链:

  1. 用于构建内核(和内核模块)的工具链:bare-metal (EABI) toolchain
  2. 用于构建用户空间应用程序的工具链:GNU/Linux toolchain
  3. 我建议您使用Linaro ARM工具链,因为它们是free,可靠且针对ARM进行了优化。 Here您可以选​​择所需的工具链(" Linaro Toolchain"部分)。在BeagleBone Black上,默认情况下你会使用little-endian架构(就像大多数ARMv7处理器一样),所以下载下两个档案:

    1. linaro-toolchain-binaries (little-endian) Bare Metal
    2. linaro-toolchain-binaries (little-endian) Linux
    3. 下载完成后,将这些档案解压缩到/opt目录。

      内核源

      首先,您需要找出哪些完全内核源代码用于构建闪存到您板上的内核。您可以尝试从here计算出来(通过您的电路板修订版)。或者您可以构建自己的内核,将其刷新到您的电路板,现在您确切知道正在使用的内核版本。

      无论如何,您需要下载正确的内核源代码(与您的主板上的内核相对应)。这些源将进一步用于构建内核模块。如果内核版本不正确,你将会遇到魔法不匹配的问题。模块加载时出现错误或类似错误。

      我将使用来自stablekernel.org内核源代码作为参考(至少应该足以构建模块)。

      构建内核

      在终端中运行下一个命令,为内核构建配置shell环境(裸机工具链):

      $ export PATH=/opt/gcc-linaro-5.1-2015.08-x86_64_arm-eabi/bin:$PATH
      $ export CROSS_COMPILE=arm-eabi-
      $ export ARCH=arm
      

      使用defconfig为您的主板配置内核(来自arch/arm/configs/)。我将使用omap2plus_defconfig例如:

      $ make omap2plus_defconfig
      

      现在要么构建整个内核:

      $ make -j4
      

      或准备所需的内核文件以构建外部模块:

      $ make prepare
      $ make modules_prepare
      

      在第二种情况下,模块没有依赖列表,可能你需要使用" force"加载时的选项。所以首选的选择是构建整个内核。

      内核模块

      注意:我将进一步使用的代码来自this answer

      首先,您需要为用户空间访问启用ARM性能计数器(详细信息为here)。它只能在内核空间中完成。以下是您可以使用的模块代码和Makefile

      <强> perfcnt_enable.c

      #include <linux/module.h>
      
      static int __init perfcnt_enable_init(void)
      {
      
          /* Enable user-mode access to the performance counter */
          asm ("mcr p15, 0, %0, C9, C14, 0\n\t" :: "r"(1));
      
          /* Disable counter overflow interrupts (just in case) */
          asm ("mcr p15, 0, %0, C9, C14, 2\n\t" :: "r"(0x8000000f));
      
          pr_debug("### perfcnt_enable module is loaded\n");
          return 0;
      }
      
      static void __exit perfcnt_enable_exit(void)
      {
      }
      
      module_init(perfcnt_enable_init);
      module_exit(perfcnt_enable_exit);
      
      MODULE_AUTHOR("Sam Protsenko");
      MODULE_DESCRIPTION("Module for enabling performance counter on ARMv7");
      MODULE_LICENSE("GPL");
      

      <强>生成文件

      ifneq ($(KERNELRELEASE),)
      
      # kbuild part of makefile
      
      CFLAGS_perfcnt_enable.o := -DDEBUG
      obj-m := perfcnt_enable.o
      
      else
      
      # normal makefile
      
      KDIR ?= /lib/modules/$(shell uname -r)/build
      
      module:
          $(MAKE) -C $(KDIR) M=$(PWD) modules
      
      clean:
          $(MAKE) -C $(KDIR) M=$(PWD) clean
      
      .PHONY: module clean
      
      endif
      

      构建内核模块

      使用上一步中配置的shell环境,让我们再导出一个环境变量:

      $ export KDIR=/path/to/your/kernel/sources/dir
      

      现在运行:

      $ make
      

      构建模块(perfcnt_enable.ko文件)。

      用户空间应用

      在内核空间(内核模块)中启用ARM性能计数器后,您可以在用户空间应用程序中读取其值。以下是此类应用程序的示例。

      <强> perfcnt_test.c

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      
      static unsigned int get_cyclecount(void)
      {
          unsigned int value;
      
          /* Read CCNT Register */
          asm volatile ("mrc p15, 0, %0, c9, c13, 0\t\n": "=r"(value));
      
          return value;
      }
      
      static void init_perfcounters(int32_t do_reset, int32_t enable_divider)
      {
          /* In general enable all counters (including cycle counter) */
          int32_t value = 1;
      
          /* Peform reset */
          if (do_reset) {
              value |= 2; /* reset all counters to zero */
              value |= 4; /* reset cycle counter to zero */
          }
      
          if (enable_divider)
              value |= 8; /* enable "by 64" divider for CCNT */
      
          value |= 16;
      
          /* Program the performance-counter control-register */
          asm volatile ("mcr p15, 0, %0, c9, c12, 0\t\n" :: "r"(value));
      
          /* Enable all counters */
          asm volatile ("mcr p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000000f));
      
          /* Clear overflows */
          asm volatile ("mcr p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f));
      }
      
      int main(void)
      {
          unsigned int overhead;
          unsigned int t;
      
          /* Init counters */
          init_perfcounters(1, 0);
      
          /* Measure the counting overhead */
          overhead = get_cyclecount();
          overhead = get_cyclecount() - overhead;
      
          /* Measure ticks for some operation */
          t = get_cyclecount();
          sleep(1);
          t = get_cyclecount() - t;
      
          printf("function took exactly %d cycles (including function call)\n",
                  t - overhead);
      
          return EXIT_SUCCESS;
      }
      

      <强>生成文件

      CC = gcc
      APP = perfcnt_test
      SOURCES = perfcnt_test.c
      CFLAGS = -Wall -O2 -static
      
      default:
          $(CROSS_COMPILE)$(CC) $(CFLAGS) $(SOURCES) -o $(APP)
      
      clean:
          -rm -f $(APP)
      
      .PHONY: default clean
      

      请注意,我添加了-static选项以防您使用Android等。如果您的发行版具有常规libc,您可以删除该标志以减小结果二进制文件的大小。

      构建用户空间应用程序

      准备shell环境(Linux工具链):

      $ export PATH=/opt/gcc-linaro-5.1-2015.08-x86_64_arm-linux-gnueabihf/bin:$PATH
      $ export CROSS_COMPILE=arm-linux-gnueabihf-
      

      构建应用程序:

      $ make
      

      输出二进制文件为perfcnt_test

      测试

      1. 将内核模块和用户空间应用程序上传到您的主板。
      2. 加载模块:

        # insmod perfcnt_enable.ko
        
      3. 运行应用程序:

        # ./perfcnt_test