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

8voto

Rufo Sanchez Puntos 390

El método más simple es hacer que tu código levante algún alfiler antes de que ejecute el código que quieres cronometrar, y bajarlo después de que haya terminado de hacer lo que sea. Entonces haz el bucle de código (o usa un osciloscopio digital con memoria en el modo de disparo único) y simplemente haz el osciloscopio y luego la clavija. La longitud del pulso te dice cuánto tiempo tardó en ejecutar la pieza de código más un ciclo de reloj para cambiar el estado del pin (creo que tarda un ciclo, no estoy 100% seguro).

4voto

reconbot Puntos 1670

¿Qué quiere decir con "monitor"?

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

También puede establecer un puerto antes de que se ejecute el código y reiniciarlo después, y monitorearlo con un analizador lógico o un oscilograma para obtener la sincronización.

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

1voto

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, total de ciclos de reloj) y los 3 de la derecha son los tiempos transcurridos:

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 tenían 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);     

}

Sidenote: si eliminas el retraso de 4 segundos empezarás a ver los efectos del Serial.print() mucho más claramente. Nota, aquí se comparan 2 tiradas. Sólo he incluido 4 muestras cercanas entre sí 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 un segundo, los relojes aumentan en promedio de 40k a 44k. Esto sucede constantemente 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 es que el monitoreo es útil o necesario. ¿Quizás la disminución de la eficiencia tiene que ver con la configuración o los errores en serie? O tal vez el código no está usando la memoria correctamente y tiene una fuga que afecta el rendimiento, etc.

1voto

chews Puntos 1507

Podrías usar uno de los temporizadores incorporados. Prepara todo para el prellamador = 1 y el TCNT = 0 antes del bloque. Luego habilita el temporizador en la línea anterior al bloque y deshabilítalo en la línea posterior al bloque. El TCNT ahora mantendrá el número de ciclos que el bloque tomó, menos los ciclos fijos para el código de habilitación y deshabilitación.

Tengan en cuenta que el TNCT se desbordará después de 65535 ciclos de reloj en un temporizador de 16 bits. Puedes usar el indicador 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 el mínimo requerido para realizar la tarea.

Acabo de encontrar un plugin de Atmel Studio llamado "Depurador de archivos en ensamblador comentado". http://www.atmel.com/webdoc/aafdebugger/pr01.html Parece que pasar a través del lenguaje ensamblador generado, aunque probablemente sea tedioso, te va a mostrar exactamente lo que está sucediendo. Todavía tendrías que decodificar cuántos ciclos toma cada instrucción pero se acercaría mucho más que algunas de las otras opciones publicadas.

Para los que no lo sepan, 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 se encuentra el lenguaje ensamblador que fue generado en base a esa línea de código. La generación del archivo LSS se puede desactivar, así que compruebe la siguiente configuración.

Propiedades del proyecto | Cadena de herramientas | AVR/GNU Común | Archivos de salida

Marque la 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