raspberry - Cálculo del recuento de ciclos del reloj en ARM Cortex-a8 BeagleBone Black
beaglebone black wireless (1)
Quiero calcular el recuento del ciclo del reloj para una función específica dentro de mi código c que se compilará y ejecutará en BeagleBone Black. No tengo idea de cómo puedo hacer esto. Busqué en la web y encontré esta instrucción:
Método Clock Read en la placa de Arndale:
Paso-1: Insertar el módulo kernel para habilitar el acceso del espacio de usuario a los contadores PMU. Descomprima el archivo adjunto "arndale_clockread.tar.bz2" que tiene Makefile y enableccnt.c. En Makefile, cambie el "KERNELDIR" con su directorio fuente de kernel, por ejemplo / usr/src/linux-kernel-version
luego ejecute el comando.
linaro@linaro-server:~/enableccnt$ make
El comando anterior debe dar salida como enableccnt.ko
, que es un módulo kernel para permitir el acceso del espacio de usuario a los contadores PMU. Luego ejecuta el comando.
linaro@linaro-server:~/enableccnt$ sudo insmod enableccnt.ko
El siguiente comando debería mostrar que el módulo enableccnt se está insertando en el kernel en ejecución.
linaro@linaro-server:~/enableccnt$ lsmod
Paso 2: leer el contador de las aplicaciones de espacio de usuario. Una vez que el módulo kernel se está configurando. La siguiente función se puede usar para leer el contador
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;
}
Creo que esta instrucción debería funcionar para cualquier plataforma ARMv7
. Entonces, seguí las instrucciones y cambio el directorio fuente del kernel. Así es como se ve el archivo 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
Ahora, cuando ejecuto make
, tengo este error que se queja de 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
Intenté instalar arm-linux-gnueabihf-ar
pero no funciona. Entonces, no tengo idea de qué debería hacer ahora.
EDIT1- Como se menciona en los comentarios, agrego mi ruta a la cadena de herramientas en mi variable de entorno usando:
export PATH=/path/to/mytoolchain/bin:$PATH
Y ahora no obtengo el error anterior. Sin embargo, tengo este error de sintaxis que creo que se relaciona con los archivos de encabezado del kernel:
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
La única solución razonable que me viene a la mente es descargar el código fuente del núcleo con sus archivos de encabezado e intentar hacer nuevamente. ¿Alguien tiene alguna idea para resolver este problema?
Como puede haber muchos obstáculos en el camino, a continuación se encuentra la guía completa sobre cómo construir ese módulo de kernel y aplicación de espacio de usuario.
Toolchain
En primer lugar, debe descargar e instalar 2 cadenas de herramientas:
- Herramienta para construir kernel (y módulos kernel): bare-metal (EABI) toolchain
- Herramienta para construir aplicaciones de espacio de usuario: cadena de herramientas GNU / Linux
Te recomiendo que uses Linaro ARM toolchains, ya que son gratis , confiables y están bien optimizados para ARM. Aquí puede elegir las cadenas de herramientas deseadas (en la sección "Linaro Toolchain"). En BeagleBone Black tiene arquitectura little-endian por defecto (como en la mayoría de los procesadores ARMv7), así que descargue los siguientes dos archivos:
Una vez descargado, extraiga esos archivos en el directorio /opt
.
Fuentes de kernel
En primer lugar, debe averiguar qué fuentes exactamente del kernel se usaron para construir el kernel que brilló en su tablero. Puede tratar de resolver eso (mediante la revisión de su placa) desde aquí . O puede construir su propio kernel, llevarlo a su tablero y ahora sabe exactamente qué versión del núcleo está en uso.
De todos modos, debe descargar las fuentes correctas del kernel (que corresponden al núcleo en su tablero). Esas fuentes se usarán más para construir el módulo kernel. Si la versión del kernel es incorrecta, tendrá el error "magic mismatch" o algo así en la carga del módulo.
Utilizaré fuentes kernel estables de kernel.org solo para referencias (al menos debería ser suficiente para compilar el módulo).
Construir kernel
Ejecute los siguientes comandos en su terminal para configurar el entorno del shell (cadena de herramientas bare-metal) para la construcción del kernel:
$ export PATH=/opt/gcc-linaro-5.1-2015.08-x86_64_arm-eabi/bin:$PATH
$ export CROSS_COMPILE=arm-eabi-
$ export ARCH=arm
Configure kernel usando defconfig para su placa (desde arch/arm/configs/
). omap2plus_defconfig
por ejemplo:
$ make omap2plus_defconfig
Ahora bien, construye todo el kernel:
$ make -j4
o prepare los archivos kernel necesarios para construir un módulo externo:
$ make prepare
$ make modules_prepare
En el segundo caso, el módulo no tendrá una lista de dependencias y probablemente tendrá que usar la opción "forzar" al cargarlo. Entonces, la opción preferida es construir todo el kernel.
Módulo kernel
NOTA : el código que voy a usar más es de esta respuesta .
Primero necesita habilitar el contador de rendimiento ARM para el acceso de espacio de usuario (los detalles están aquí ). Se puede hacer solo en kernel-space. Aquí está el código del módulo y Makefile
que puede usar para hacerlo:
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
Construir módulo kernel
Usando el entorno de shell configurado en el paso anterior, exportaremos una variable de entorno más:
$ export KDIR=/path/to/your/kernel/sources/dir
Ahora solo ejecuta:
$ make
El módulo está construido (archivo perfcnt_enable.ko
).
Aplicación de espacio de usuario
Una vez que el contador de rendimiento de ARM está habilitado en kernel-space (por módulo kernel), puede leer su valor en la aplicación de espacio de usuario. Aquí está el ejemplo de tal aplicación.
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
Tenga en cuenta que agregué la opción -static
solo en caso de que esté usando Android, etc. Si su distribución tiene libc regular, probablemente pueda eliminar ese indicador para reducir el tamaño del resultado binario.
Crear una aplicación de espacio de usuario
Preparar entorno de shell (Linux toolchain):
$ export PATH=/opt/gcc-linaro-5.1-2015.08-x86_64_arm-linux-gnueabihf/bin:$PATH
$ export CROSS_COMPILE=arm-linux-gnueabihf-
Construye la aplicación:
$ make
El binario de salida es perfcnt_test
.
Pruebas
- Cargue el módulo de núcleo y la aplicación de espacio de usuario en su placa.
Cargue el módulo:
# insmod perfcnt_enable.ko
Ejecuta la aplicación:
# ./perfcnt_test