Вычислить количество тактовых циклов на 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. Чтение счетчика из приложений пользовательского пространства. После установки модуля ядра. Следующая функция может быть использована для чтения счетчика

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

Единственное разумное решение, которое приходит мне в голову, это загрузить исходный код ядра с его заголовочными файлами и попытаться сделать это снова. У кого-нибудь есть идея решить эту проблему?


person A23149577    schedule 04.12.2015    source источник
comment
Вы хотите измерить FLOPS вашего процессора. Посмотрите, как ядро ​​Linux вычисляет его во время загрузки.   -  person Claudio    schedule 04.12.2015
comment
После установки toolchain вам необходимо настроить среду. Прежде всего, сделайте export PATH=/path/to/your/toolchain/bin:$PATH. Затем export CROSS_COMPILE=arm-linux-gnueabihf-. Теперь вы можете попробовать собрать свой модуль с помощью ARCH=arm (как и вы).   -  person Sam Protsenko    schedule 04.12.2015
comment
@Claudio Я не знаю, правильно ли я понял ваш комментарий или нет, но все, что я хочу вычислить, это количество тактов для определенной функции внутри моего кода C, который будет скомпилирован с помощью gcc на микропроцессоре ARM Cortex-A8.   -  person A23149577    schedule 04.12.2015
comment
@SamProtsenko Спасибо за ваш комментарий. Я попробую и дам вам знать, как это происходит.   -  person A23149577    schedule 04.12.2015
comment
@AJeneral Посмотрите этот ответ, особенно раздел Toolchain. В основном вам нужно сделать те же шаги, только для набора инструментов ARM (а не для MIPS).   -  person Sam Protsenko    schedule 04.12.2015
comment
@SamProtsenko Я обновил свой вопрос. Я был бы признателен, если бы вы взглянули на него и дали мне знать, знакомы ли вы с новым выпуском.   -  person A23149577    schedule 04.12.2015
comment
@AJeneral Ваш Makefile кажется неправильным. Попробуйте изменить правило all: на $(MAKE) ARCH=arm -C $(KERNELDIR) M=`pwd`.   -  person Sam Protsenko    schedule 05.12.2015
comment
@SamProtsenko Я изменил Makefile, но все равно получаю ту же синтаксическую ошибку.   -  person A23149577    schedule 07.12.2015
comment
@AJeneral Смотрите мой ответ с полными инструкциями. В том, что вы делаете, может быть много подводных камней, поэтому мне было легко просто описать весь процесс (который я протестировал, и он работает).   -  person Sam Protsenko    schedule 07.12.2015


Ответы (1)


Поскольку на этом пути может быть много препятствий, ниже приведено полное руководство по сборке этого модуля ядра и приложения в пользовательском пространстве.

Цепочка инструментов

В первую очередь вам необходимо скачать и установить 2 тулчейна:

  1. Набор инструментов для сборки ядра (и модулей ядра): набор инструментов bare-metal (EABI)
  2. Набор инструментов для создания приложения в пространстве пользователя: набор инструментов GNU/Linux

Я рекомендую вам использовать цепочки инструментов Linaro для ARM, так как они бесплатны, надежны и хорошо оптимизированы для ARM. . Здесь вы можете выбрать нужные цепочки инструментов (в разделе "Linaro Toolchain"). На BeagleBone Black по умолчанию используется архитектура с прямым порядком байтов (как и на большинстве процессоров ARMv7), поэтому загрузите следующие два архива:

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

После загрузки извлеките эти архивы в каталог /opt.

Исходники ядра

Прежде всего, вам необходимо выяснить, какие точно исходные коды ядра использовались для сборки ядра, прошитого на вашу плату. Вы можете попытаться выяснить это (по версии вашей платы) из здесь. Или вы можете собрать собственное ядро, прошить его на плату, и теперь вы точно знаете, какая версия ядра используется.

В любом случае, вам нужно загрузить правильные исходники ядра (которые соответствуют ядру на вашей плате). Эти исходники будут использоваться в дальнейшем для сборки модуля ядра. Если версия ядра неверна, у вас будет ошибка "волшебного несоответствия" или что-то в этом роде при загрузке модуля.

Я буду использовать стабильные исходные коды ядра из kernel.org просто для справки (его должно быть достаточно, по крайней мере, для сборки модуля).

Собрать ядро

Запустите следующие команды в своем терминале, чтобы настроить среду оболочки (базовый набор инструментов) для сборки ядра:

$ 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» при его загрузке. Поэтому предпочтительным вариантом является сборка всего ядра.

Модуль ядра

ПРИМЕЧАНИЕ: код, который я буду использовать дальше, взят из этого ответа.

Сначала вам нужно включить счетчик производительности ARM для доступа к пользовательскому пространству (подробности здесь). Это можно сделать только в пространстве ядра. Вот код модуля и 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

Собрать модуль ядра

Используя настроенную среду оболочки из предыдущего шага, давайте экспортируем еще одну переменную среды:

$ 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, вы, вероятно, можете удалить этот флаг, чтобы уменьшить размер результирующего двоичного файла.

Создание пользовательского приложения

Подготовьте среду оболочки (инструментарий 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
    
person Sam Protsenko    schedule 07.12.2015
comment
Большое спасибо за ваш краткий ответ - person A23149577; 08.12.2015