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

我想计算我的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:从用户空间应用程序中读取计数器。 一旦设置了内核模块。 以下function可用于读取计数器

 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 

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

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

工具链

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

  1. 用于构建内核(和内核模块)的工具链: 裸机(EABI)工具链
  2. 用于构建用户空间应用程序的工具链: GNU / Linux工具链

我建议您使用Linaro ARM工具链,因为它们是免费的 ,可靠的并且针对ARM进行了优化。 在这里,您可以选择所需的工具链(在“Linaro工具链”部分中)。 在BeagleBone Black上,默认情况下你会使用little-endian架构(就像在大多数ARMv7处理器上一样),所以下载下两个档案:

  1. linaro-toolchain-binaries(little-endian)Bare Metal
  2. linaro-toolchain-binaries(little-endian)Linux

下载后,将这些存档解压缩到/opt目录中。

内核来源

首先,您需要找出哪些内核源代码用于构建闪存到您板上的内核。 你可以尝试从这里弄清楚(通过你的董事会修订)。 或者您可以构建自己的内核,将其刷新到您的板上,现在您确切地知道正在使用的内核版本。

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

我将使用来自kernel.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 

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

内核模块

注意 :我将进一步使用的代码来自这个答案 。

首先,您需要为用户空间访问启用ARM性能计数器(详情请参见此处 )。 它只能在内核空间中完成。 以下是可用于执行此操作的模块代码和Makefile

perfcnt_enable.c

 #include  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"); 

Makefile

 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  #include  #include  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; } 

Makefile

 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