11 votos

¿Monitorear los ciclos de reloj para el código en arduino/AVR?

¿Es posible monitorizar un bloque de código y determinar el número de ciclos de reloj del procesador que el código tomó en un procesador de atmosfera Arduino y/o AVR? o, ¿debería monitorizar los microsegundos pasados antes y después de la ejecución del código? Nota: No me preocupa el tiempo real (como en, cuántos segundos reales pasaron) tanto como en "cuántos ciclos de reloj requiere este código de la CPU"

La solución actual que se me ocurre es de time.c:

#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )

wiring.c añade:

#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )

Por esta cuenta podría calcular los cilindros de relojes pasados monitoreando los microsegundos pasados y luego pasarlos a los microsegundos a los ciclos de relojes. Mi pregunta es, ¿hay una mejor manera?

sidenote: ¿hay buenos recursos para la supervisión del rendimiento del AVR. lmgtfy.com y las búsquedas en varios foros no proporcionan ningún resultado obvio, aparte de la exploración de los temporizadores

gracias

6voto

Rufo Sanchez Puntos 390

El método más sencillo es hacer que tu código suba algún pin antes de que ejecute el código que quieres cronometrar, y lo baje después de que haya terminado de hacer lo que sea. Entonces haz un bucle de código (o utiliza un osciloscopio digital con memoria en modo de disparo único) y simplemente mira el pin. La longitud del pulso te dice cuánto tiempo ha tardado en ejecutar el trozo de código más un ciclo de reloj desde el cambio de estado del pin (creo que tarda un ciclo, no estoy 100% seguro).

0 votos

Gracias. Sí, veo que esta es probablemente la solución más precisa. Todavía estoy buscando un código que me dé al menos un análisis general del uso del ciclo dentro del código. Voy a utilizar esto para construir algunas herramientas de prueba y sería bueno para establecer mis límites superiores para los parámetros tales como el tiempo de ejecución máximo permitido basado en la eficiencia del código + todo lo relacionado con ella se ejecuta en la CPU Atmel actual en uso

4voto

reconbot Puntos 1670

¿Qué quiere decir con "monitor"?

No debería ser difícil contar los ciclos de reloj de AVR para pequeñas piezas de código ensamblador.

También puedes configurar un puerto antes de que se ejecute el código y reiniciarlo después, y monitorizarlo con un analizador lógico o un oszilloscopio para obtener la sincronización.

Y también podrías leer la hora de un temporizador de marcha rápida, como dices.

0 votos

Por monitorizar me refiero a determinar el número de ciclos utilizados por el código. algo como (nota, el formato del código probablemente será aplanado por el motor de comentarios): clocks = startCountingAtmegaClocks(); for ... { para ... { digitalRead ... }} Serial.print("número de ciclos utilizados :"); Serial.print(currentCountingAtmegaClocks() - clocks, DEC);

0 votos

Pero sí, tu respuesta es lo que he asumido que son mis opciones. Supongo que, si puedo calcular los ciclos de reloj que el ensamblador tomaría a mano que alguien tal vez ya ha escrito un buen código para hacer esto programáticamente

3voto

BBlake Puntos 310

Este es un ejemplo para Arduino usando la función clockCyclesPerMicrosecond() para calcular los relojes que han pasado. Este código esperará 4 segundos y luego imprimirá el tiempo transcurrido desde el inicio del programa. Los 3 valores de la izquierda son el tiempo total (microsegundos, milisegundos, ciclos de reloj totales) y los 3 de la derecha son los tiempos transcurridos:

La salida:

clocks for 1us:16
runtime us, ms, ck :: elapsed tme us, ms ck
4003236 4002    64051776    ::  4003236 4002    64051760
8006668 8006    128106688   ::  4003432 4004    64054912
12010508    12010   192168128   ::  4003840 4004    64061440
16014348    16014   256229568   ::  4003840 4004    64061440
20018188    20018   320291008   ::  4003840 4004    64061440
24022028    24022   384352448   ::  4003840 4004    64061440
28026892    28026   448430272   ::  4004864 4004    64077824
32030732    32030   512491712   ::  4003840 4004    64061440
36034572    36034   576553152   ::  4003840 4004    64061440
40038412    40038   640614592   ::  4003840 4004    64061440
44042252    44042   704676032   ::  4003840 4004    64061440
48046092    48046   768737472   ::  4003840 4004    64061440
52050956    52050   832815296   ::  4004864 4004    64077824

Estoy seguro de que hay una explicación razonable de por qué los primeros bucles tienen ciclos de reloj más cortos que la mayoría y por qué todos los demás bucles alternan entre dos longitudes de ciclos de reloj.

Código:

unsigned long us, ms, ck;
unsigned long _us, _ms, _ck;
unsigned long __us, __ms, __ck;
void setup() {
        Serial.begin(9600);
}
boolean firstloop=1;
void loop() { 
        delay(4000);

        if (firstloop) {
                Serial.print("clocks for 1us:");
                ck=microsecondsToClockCycles(1);
                Serial.println(ck,DEC);
                firstloop--;
                Serial.println("runtime us, ms, ck :: elapsed tme us, ms ck");
        }

        _us=us;
        _ms=ms;
        _ck=ck;

        us=micros(); // us since program start
        ms=millis();
        //ms=us/1000;
        ck=microsecondsToClockCycles(us);
        Serial.print(us,DEC);
        Serial.print("\t");
        Serial.print(ms,DEC);
        Serial.print("\t");
        Serial.print(ck,DEC);     
        Serial.print("\t::\t");

        __us = us - _us;
        __ms = ms - _ms;
        __ck = ck - _ck;
        Serial.print(__us,DEC);
        Serial.print("\t");
        Serial.print(__ms,DEC);
        Serial.print("\t");
        Serial.println(__ck,DEC);     

}

Nota al margen: si quitas el retraso de 4 segundos empezarás a ver los efectos de Serial.print() mucho más claramente. Nota: aquí se comparan 2 ejecuciones. Sólo he incluido 4 muestras cercanas de sus respectivos registros.

Corre 1:

5000604 5000    80009664    ::  2516    2   40256
6001424 6001    96022784    ::  2520    3   40320
7002184 7002    112034944   ::  2600    3   41600
8001292 8001    128020672   ::  2600    3   41600

Corre 2:

5002460 5002    80039360    ::  2524    3   40384
6000728 6000    96011648    ::  2520    2   40320
7001452 7001    112023232   ::  2600    3   41600
8000552 8000    128008832   ::  2604    3   41664

El tiempo transcurrido aumenta sobre el tiempo total de ejecución. Después de transcurrir un segundo, los relojes aumentan en promedio de 40k a 44k. Esto ocurre de forma consistente unos pocos milisegundos después de 1 segundo y los relojes transcurridos permanecen alrededor de 44k durante al menos los siguientes 10 segundos (no lo he probado más). Por eso la monitorización es útil o necesaria. ¿Tal vez la disminución de la eficiencia tiene que ver con la configuración o con los errores en la serie? O tal vez el código no está utilizando la memoria correctamente y tiene una fuga que afecta al rendimiento, etc.

0 votos

Muchos años después, todavía me gustaría tener algo que muestre los relojes de forma más precisa con código (en lugar de un osciloscopio). Estoy tratando de determinar el número de ciclos de reloj necesarios para un digitalWrite() tanto en 16MHZ como en 8MHZ. En 16MHZ obtengo 8us/64clk. Pero en 8MHZ obtengo 0us/0clk.

1voto

chews Puntos 1507

Puedes utilizar uno de los temporizadores incorporados. Configure todo para prescaller=1 y TCNT=0 antes del bloque. Luego habilite el temporizador en la línea anterior al bloque y desactívelo en la línea posterior al bloque. El TCNT contendrá ahora el número de ciclos que ha tardado el bloque, menos los ciclos fijos para el código de activación y desactivación.

Tenga en cuenta que el TNCT se desbordará después de 65535 ciclos de reloj en un temporizador de 16 bits. Puedes utilizar la bandera de desbordamiento para duplicar el tiempo de ejecución. Si todavía necesitas más tiempo, puedes usar un preescalador, pero obtendrás menos resolución.

1voto

becko Puntos 114

Ya que cada línea de código añadida a su fuente tendrá un impacto en el rendimiento y podría cambiar las optimizaciones aplicadas. Los cambios deben ser los mínimos necesarios para realizar la tarea.

Acabo de encontrar un plugin de Atmel Studio llamado "Annotated Assembly File Debugger". http://www.atmel.com/webdoc/aafdebugger/pr01.html Parece que el paso a través del lenguaje ensamblador generado, aunque probablemente sea tedioso, le mostrará exactamente lo que está sucediendo. Es posible que todavía tenga que decodificar cuántos ciclos se necesita para cada instrucción, pero sería mucho más cerca que algunas de las otras opciones publicadas.

Para aquellos que no lo saben, en la carpeta de salida de su proyecto hay un archivo con una extensión LSS. Este archivo contiene todo su código fuente original como comentarios y debajo de cada línea está el lenguaje ensamblador que se generó basado en esa línea de código. La generación del archivo LSS puede ser desactivada, así que compruebe la siguiente configuración.

Propiedades del proyecto | Toolchain | AVR/GNU Common | OutputFiles

Casilla ".lss (Generar archivo lss)

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X