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

10

¿Es posible monitorear un bloque de código y determinar la cantidad de ciclos de reloj del procesador que tomó el código en un procesador Atel Arduino y / o AVR? o, ¿debería monitorear microsegundos pasados antes y después de ejecutar el código? Nota: no me preocupa el tiempo real (como en cuántos segundos reales pasados), sino "cuántos ciclos de reloj requiere este código de la CPU"

La solución actual que puedo encontrar es de time.c:

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

wiring.c agrega:

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

Por esta cuenta, pude calcular los ciclos de reloj pasados monitoreando los microsegundos pasados y luego pasarlos a microsecondsToClockCycles (). Mi pregunta es, ¿hay una mejor manera?

nota al margen: existen buenos recursos para el monitoreo del rendimiento del AVR. lmgtfy.com y las búsquedas en diversos foros no proporcionan ningún resultado obvio, excepto la exploración de temporizadores

gracias

    
pregunta cyphunk

5 respuestas

3

Este es un ejemplo para Arduino que utiliza la función clockCyclesPerMicrosecond () para calcular los relojes que han pasado. Este código esperará 4 segundos, luego imprimirá el tiempo transcurrido desde el inicio del programa. Los valores de la izquierda 3 son el tiempo total (microsegundos, milisegundos, ciclos de reloj totales) y los 3 más a 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 también tuvieron ciclos de reloj más cortos que la mayoría y por qué todos los otros bucles alternan entre dos 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 elimina la demora de 4 segundos, comenzará a ver los efectos de Serial.print () mucho más claramente. Tenga en cuenta, aquí se comparan 2 carreras. Solo he incluido 4 muestras una al lado de la otra en sus respectivos registros.

Ejecutar 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

Ejecutar 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 con 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 en torno a 44k durante al menos los siguientes 10 segundos (no lo he probado más). Es por esto que el monitoreo es útil o necesario. ¿Tal vez la menor eficiencia tenga que ver con la configuración o los errores en serie? O quizás el código no esté usando la memoria correctamente y tenga una fuga que afecte el rendimiento, etc.

    
respondido por el cyphunk
6

El método más simple es hacer que su código tire un poco hacia arriba antes de que ejecute el código que desea cronometrar, y tire de él hacia abajo una vez que haya terminado de hacer lo que sea. Luego haga el bucle de código (o use un osciloscopio digital con memoria en modo de disparo único) y solo el alcance y el pin. La duración del pulso le indica cuánto tiempo tardó en ejecutar la pieza de código más un ciclo de reloj desde cambiar el estado del pin (creo que toma un ciclo, no es 100% seguro).

    
respondido por el Dago
4

¿Qué quieres decir con "monitor"?

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

También puede configurar un puerto antes de que se ejecute el código y restablecerlo luego, y monitorearlo con un analizador lógico o un oszilloscopio para obtener la sincronización.

Y también puedes leer la hora de un temporizador de ejecución rápida, como dices.

    
respondido por el starblue
1

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

Acabo de encontrar un complemento de Atmel Studio llamado "Annotated Assembly File Debugger". enlace Parece un paso a través del lenguaje ensamblador generado real, mientras que probablemente tedioso le mostrará exactamente lo que es sucediendo. Es posible que aún tenga que decodificar cuántos ciclos toma para cada instrucción, pero se acercaría mucho más que algunas de las otras opciones publicadas.

Para aquellos que no saben en la carpeta de resultados 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ó en base a esa línea de código. La generación del archivo LSS se puede desactivar, así que verifique la siguiente configuración.

Propiedades del proyecto | Toolchain | AVR / GNU Common | Archivos de salida

Casilla de verificación ".lss (Generar archivo lss)

    
respondido por el James
1

Puedes usar uno de los temporizadores integrados. Haga que todo esté configurado para prescaller = 1 y TCNT = 0 antes del bloqueo. Luego habilite el temporizador en la línea antes del bloque y desactívelo en la línea después del bloque. El TCNT ahora contendrá la cantidad de ciclos que tomó el bloque, menos los ciclos fijos para el código de habilitación y deshabilitación.

Tenga en cuenta que el TNCT se desbordará después de 65535 ciclos de reloj en un temporizador de 16 bits. Puede utilizar el indicador de desbordamiento para duplicar el tiempo de ejecución. Si aún necesita más tiempo, puede usar un prescaler, pero obtendrá menos resolución.

    
respondido por el bigjosh

Lea otras preguntas en las etiquetas

Comentarios Recientes

El software de Heli tiene arduino cpulclusters ejecutándose. La publicación sobre la modificación de arduino / AVR para la configuración sin interrupciones en un enrutador todavía no parece la respuesta, pero tiene ideas alegres. ¿Qué tal una interfaz gráfica de usuario Arduino Metro con 3.3v dos hilos de búfer configurados en demanda de energía con modo 0v, ya que esto también se ejecuta en RouterDDR5. Esto podría usarse en cualquier IO - bit de sincronización entre un módem wifi y una conexión ISP /... Lees verder