在 ARM Cortex-a8 BeagleBone Black 上计算时钟周期数
Compute clock cycle count on ARM Cortex-a8 BeagleBone Black
我想计算将要编译的 c 代码中特定函数的时钟周期数,并在 BeagleBone Black 上 运行。我不知道我该怎么做。我在网上搜索并找到了这条指令:
Arndale板上的时钟读取方法:
步骤 1: 插入内核模块以允许用户 space 访问 PMU 计数器。
解压包含 Makefile 和 enableccnt.c 的附件“arndale_clockread.tar.bz2”。在 Makefile 中,将“KERNELDIR”更改为您的内核源目录,例如/usr/src/linux-kernel-version
然后 运行 命令。
linaro@linaro-server:~/enableccnt$ make
上面的命令应该输出enableccnt.ko
,这是内核模块允许用户space访问PMU计数器。然后运行命令。
linaro@linaro-server:~/enableccnt$ sudo insmod enableccnt.ko
以下命令应该显示 enableccnt 模块正在 运行ning 内核中插入。
linaro@linaro-server:~/enableccnt$ lsmod
步骤 2: 从用户 space 应用程序读取计数器。
一旦设置了内核模块。以下函数可用于读取计数器
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
我想到的唯一合理的解决方案是下载带有头文件的内核源代码,然后再尝试制作。有没有人知道如何解决这个问题?
由于一路上会遇到很多障碍,下面是完整的指南,说明如何构建该内核模块和用户-space 应用程序。
工具链
首先,您需要下载并安装2个工具链:
- 构建内核(和内核模块)的工具链:bare-metal (EABI)工具链
- 用于构建用户的工具链-space 应用程序:GNU/Linux 工具链
我建议您使用 Linaro ARM 工具链,因为它们是 free, reliable and well optimized for ARM. Here 您可以选择所需的工具链(在 "Linaro Toolchain" 部分)。在 BeagleBone Black 上,默认情况下你有小端架构(就像在大多数 ARMv7 处理器上一样),所以下载接下来的两个档案:
- linaro-toolchain-binaries (little-endian) Bare Metal
- linaro-toolchain-binaries (little-endian) Linux
下载后,将这些档案解压缩到 /opt
目录中。
内核源码
首先,您需要找出 确切 内核源代码用于构建闪存到您的主板的内核。您可以尝试从 here 中找出(通过您的董事会修订版)。或者您可以构建自己的内核,将其闪存到您的主板上,现在您可以确切知道正在使用的内核版本。
无论如何,您需要下载正确的内核源代码(与您主板上的内核相对应)。这些资源将进一步用于构建内核模块。如果内核版本不正确,您将在模块加载时出现 "magic mismatch" 错误或类似错误。
我将使用 stable kernel sources from 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
在第二种情况下,模块将没有依赖项列表,您可能需要在加载它时使用 "force" 选项。所以首选是构建整个内核。
内核模块
注意:我将进一步使用的代码来自this answer。
首先您需要为用户space 访问启用ARM 性能计数器(详细信息here)。它只能在 kernel-space 中完成。这是模块代码,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
文件)。
用户-space应用程序
一旦在kernel-space中启用了ARM性能计数器(通过内核模块),您就可以在user-space应用程序中读取它的值。这是此类应用程序的示例。
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,您可以删除该标志以减小结果二进制文件的大小。
构建用户-space应用程序
准备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
.
测试
- 将内核模块和用户-space 应用程序上传到您的主板。
加载模块:
# insmod perfcnt_enable.ko
运行申请者:
# ./perfcnt_test
我想计算将要编译的 c 代码中特定函数的时钟周期数,并在 BeagleBone Black 上 运行。我不知道我该怎么做。我在网上搜索并找到了这条指令:
Arndale板上的时钟读取方法:
步骤 1: 插入内核模块以允许用户 space 访问 PMU 计数器。
解压包含 Makefile 和 enableccnt.c 的附件“arndale_clockread.tar.bz2”。在 Makefile 中,将“KERNELDIR”更改为您的内核源目录,例如/usr/src/linux-kernel-version
然后 运行 命令。
linaro@linaro-server:~/enableccnt$ make
上面的命令应该输出enableccnt.ko
,这是内核模块允许用户space访问PMU计数器。然后运行命令。
linaro@linaro-server:~/enableccnt$ sudo insmod enableccnt.ko
以下命令应该显示 enableccnt 模块正在 运行ning 内核中插入。
linaro@linaro-server:~/enableccnt$ lsmod
步骤 2: 从用户 space 应用程序读取计数器。 一旦设置了内核模块。以下函数可用于读取计数器
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
我想到的唯一合理的解决方案是下载带有头文件的内核源代码,然后再尝试制作。有没有人知道如何解决这个问题?
由于一路上会遇到很多障碍,下面是完整的指南,说明如何构建该内核模块和用户-space 应用程序。
工具链
首先,您需要下载并安装2个工具链:
- 构建内核(和内核模块)的工具链:bare-metal (EABI)工具链
- 用于构建用户的工具链-space 应用程序:GNU/Linux 工具链
我建议您使用 Linaro ARM 工具链,因为它们是 free, reliable and well optimized for ARM. Here 您可以选择所需的工具链(在 "Linaro Toolchain" 部分)。在 BeagleBone Black 上,默认情况下你有小端架构(就像在大多数 ARMv7 处理器上一样),所以下载接下来的两个档案:
- linaro-toolchain-binaries (little-endian) Bare Metal
- linaro-toolchain-binaries (little-endian) Linux
下载后,将这些档案解压缩到 /opt
目录中。
内核源码
首先,您需要找出 确切 内核源代码用于构建闪存到您的主板的内核。您可以尝试从 here 中找出(通过您的董事会修订版)。或者您可以构建自己的内核,将其闪存到您的主板上,现在您可以确切知道正在使用的内核版本。
无论如何,您需要下载正确的内核源代码(与您主板上的内核相对应)。这些资源将进一步用于构建内核模块。如果内核版本不正确,您将在模块加载时出现 "magic mismatch" 错误或类似错误。
我将使用 stable kernel sources from 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
在第二种情况下,模块将没有依赖项列表,您可能需要在加载它时使用 "force" 选项。所以首选是构建整个内核。
内核模块
注意:我将进一步使用的代码来自this answer。
首先您需要为用户space 访问启用ARM 性能计数器(详细信息here)。它只能在 kernel-space 中完成。这是模块代码,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
文件)。
用户-space应用程序
一旦在kernel-space中启用了ARM性能计数器(通过内核模块),您就可以在user-space应用程序中读取它的值。这是此类应用程序的示例。
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,您可以删除该标志以减小结果二进制文件的大小。
构建用户-space应用程序
准备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
.
测试
- 将内核模块和用户-space 应用程序上传到您的主板。
加载模块:
# insmod perfcnt_enable.ko
运行申请者:
# ./perfcnt_test