计算 ARM Cortex-a8 BeagleBone Black 上的时钟周期数 [英] Compute clock cycle count on ARM Cortex-a8 BeagleBone Black
问题描述
我想计算我的 C 代码中特定函数的时钟周期数,该函数将在 BeagleBone Black 上编译和运行.我不知道我怎么能做到这一点.我在网上搜索,我找到了这个说明:
I want to compute the clock cycle count for a specific function inside my c code which is going to be compiled and run on BeagleBone Black. I have no idea how I can do this. I searched over the web and I found this instruction:
Arndale 板上的时钟读取方法:
步骤 1: 插入内核模块以启用用户空间对 PMU 计数器的访问.解压包含 Makefile 和 enableccnt.c 的附件arndale_clockread.tar.bz2".在 Makefile 中,将KERNELDIR"更改为您的内核源目录,例如/usr/src/linux-kernel-version
然后运行命令.
Step-1: Inserting kernel module to enable user space access to PMU counters.
Untar the attached file "arndale_clockread.tar.bz2" which is having Makefile and enableccnt.c. In Makefile change the "KERNELDIR" with your kernel source directory e.g. /usr/src/linux-kernel-version
then run the command.
linaro@linaro-server:~/enableccnt$ make
上面的命令应该给出输出为 enableccnt.ko
,这是内核模块,用于启用用户空间访问 PMU 计数器.然后运行命令.
The above command should give output as enableccnt.ko
, which is kernel module to enable userspace access to PMU counters. Then run the command.
linaro@linaro-server:~/enableccnt$ sudo insmod enableccnt.ko
以下命令应显示 enableccnt 模块已插入正在运行的内核中.
The following command should show enableccnt module is being inserted in the running kernel.
linaro@linaro-server:~/enableccnt$ lsmod
第 2 步: 从用户空间应用程序读取计数器.一旦内核模块被设置.以下函数可用于读取计数器
Step-2: Reading the counter from user space applications. Once the kernel module is being setup. Following function can be used to read the counter
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 的样子:
I believe this instruction should be working for any ARMv7
platforms. So, I followed the instruction and change the kernel source directory. This is how the Makefile looks like:
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
:
Now, when I run make
, I've got this error which is complaining about 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
但它不起作用.所以,我现在不知道该怎么办!
I tried to install arm-linux-gnueabihf-ar
but it doesn't work. So, I have no clue what should I do now!
EDIT1- 正如评论中提到的,我使用以下方法将我的工具链路径添加到我的环境变量中:
EDIT1- As it is mentioned in comments, I add my toolchain path into my environment variable by using:
export PATH=/path/to/mytoolchain/bin:$PATH
现在我没有收到以前的错误.但是,我有这个语法错误,我认为它与内核头文件有关:
And now I don't get previous error. However, I've got this syntax error which I think it relates to the kernel header files:
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
我想到的唯一合理的解决方案是下载内核源代码及其头文件,然后再次尝试制作.有没有人有任何想法来解决这个问题?
The only reasonable solution that comes to my mind is to download kernel source code with its header files and try to make again. Does anyone have any idea to resolve this issue?
推荐答案
由于在此过程中可能会遇到很多障碍,以下是如何构建内核模块和用户空间应用程序的完整指南.
As there can be many obstacles along the way, below is the complete guide how to build that kernel module and user-space application.
首先,您需要下载并安装 2 个工具链:
First of all, you need to download and install 2 toolchains:
- Toolchain for building kernel (and kernel modules): bare-metal (EABI) toolchain
- Toolchain for building user-space application: GNU/Linux toolchain
我建议您使用 Linaro ARM 工具链,因为它们免费、可靠且性能良好为ARM优化.此处您可以选择所需的工具链(在Linaro 工具链"部分).在 BeagleBone Black 上,您默认使用小端架构(就像在大多数 ARMv7 处理器上一样),因此请下载接下来的两个档案:
I recommend you to use Linaro ARM toolchains, as they are free, reliable and well optimized for ARM. Here you can choose desired toolchains (in "Linaro Toolchain" section). On BeagleBone Black you have little-endian architecture by default (like on most ARMv7 processors), so download next two archives:
下载后,将这些档案解压缩到 /opt
目录中.
Once downloaded, extract those archives into /opt
directory.
首先,您需要找出确切哪些内核源代码用于构建闪存到您的主板的内核.您可以尝试从 此处 找出(通过您的董事会修订版).或者,您可以构建自己的内核,将其闪存到您的主板上,现在您就可以确切地知道正在使用哪个内核版本了.
First of all, you need to find out which exactly kernel sources were used to build the kernel which flashed to your board. You can try to figure that out (by your board revision) from here. Or you can build your own kernel, flash it to your board, and now you know exactly which kernel version is in use.
无论如何,您需要下载正确的内核源代码(对应于您主板上的内核).这些源代码将进一步用于构建内核模块.如果内核版本不正确,您将在模块加载时出现magic mismatch"错误或类似错误.
Anyway, you need to download correct kernel sources (which correspond to kernel on your board). Those sources will be used further to build kernel module. If kernel version is incorrect, you will have "magic mismatch" error or something like that on module loading.
我将使用 stable来自 kernel.org 的内核源代码仅供参考(至少应该足以构建模块).
I will use stable kernel sources from kernel.org just for references (it should be sufficient at least to build the module).
在终端中运行下一个命令,为内核构建配置 shell 环境(裸机工具链):
Run next commands in your terminal to configure shell environment (bare-metal toolchain) for kernel building:
$ 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
例如:
Configure kernel using defconfig for your board (from arch/arm/configs/
). I will use omap2plus_defconfig
for example:
$ make omap2plus_defconfig
现在要么构建整个内核:
Now either build the whole kernel:
$ make -j4
或准备构建外部模块所需的内核文件:
or prepare needed kernel files for building external module:
$ make prepare
$ make modules_prepare
在第二种情况下,模块将没有依赖项列表,您可能需要在加载时使用强制"选项.所以首选方案是构建整个内核.
In second case the module will not have dependency list and probably you will need to use "force" option when loading it. So the preferred option is building the whole kernel.
注意:我将进一步使用的代码来自这个答案.
NOTE: the code I'm gonna use further is from this answer.
首先你需要为用户空间访问启用ARM性能计数器(详情在此处).它只能在内核空间中完成.这是您可以用来执行此操作的模块代码和 Makefile
:
First you need to enable ARM performance counter for user-space access (details are here). It can be done only in kernel-space. Here is the module code and Makefile
you can use to do so:
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");
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环境,让我们再导出一个环境变量:
Build kernel module
Using configured shell environment from previous step, let's export one more environment variable:
$ export KDIR=/path/to/your/kernel/sources/dir
现在只需运行:
$ make
模块构建完成(perfcnt_enable.ko
文件).
The module is built (perfcnt_enable.ko
file).
在内核空间(通过内核模块)启用 ARM 性能计数器后,您可以在用户空间应用程序中读取其值.这是此类应用程序的示例.
Once ARM performance counter is enabled in kernel-space (by kernel module), you can read its value in user-space application. Here is the example of such application.
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;
}
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,您可能可以删除该标志以减少结果二进制文件的大小.
Notice that I added -static
option just in case if you are using Android etc. If your distro has regular libc, you can probably remove that flag to reduce size of result binary.
准备shell环境(Linux工具链):
Prepare shell environment (Linux toolchain):
$ 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
.
- 将内核模块和用户空间应用程序上传到您的开发板.
加载模块:
- Upload both kernel module and user-space application to your board.
Load the module:
# insmod perfcnt_enable.ko
运行应用程序:
Run the application:
# ./perfcnt_test
这篇关于计算 ARM Cortex-a8 BeagleBone Black 上的时钟周期数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!