Las rutinas de retardo se escriben en ensamblador

Regularmente se requieren rutinas de retardo en las aplicaciones, y lo más rápido es hacerlas con loops tipo for o while. Aunque ello funciona la mayor parte de las veces, no se tiene el control de una temporización precisa ya que se desconoce exactamente cúanto transcurre en un ciclo del retardo (este valor puede medirse con un osciloscopio o un frecuencímetro), por lo que hay que jugar con los valores para llegar al tiempo deseado.


Una forma un poco más elaborada pero más elegante de obtener retardos exactos es utilizar ensamblador en línea dentro del cuerpo de la función de retardo (o como un procedimiento completo en ensamblador si se encuentra en otro archivo y se enlaza junto con los archivos en C). Gracias al manual o al data-sheet de la CPU se conoce la cantidad ciclos de reloj que toma cada instrucción, por lo que de esta manera se puede calcular a-priori con casi certeza absoluta la cantidad de tiempo que tomará cada vuelta dentro del retardo.

Un ejemplo en pseudocódigo sería:


void delay(unsigned int tm){
asm{
lda tm ; a=tm (2 ciclos)
label: ; etiqueta de salto
dec a ; a-- (2 ciclos)
bne label ; mientras a!=0 ir a label: (5 ciclos)
}
}


En este ejemplo hipotético se ve que cada vuelta toma 7 ciclos (dec + bne), por lo que la cantidad de ciclos totales para un tm=100 sería:

ciclos=7*tm+2+ (los ciclos que tome entrar y salir de la función)
ciclos=7*100+2+40=742 ciclos

y suponiendo que cada ciclo de instrucción es de 1 us (un microsegundo) entonces un tm=100 tomaría un total de 742 us.

Además, por el hecho de estar escrito en ensamblador no queda sujeto a las diferencias en tamaño de código de los diferentes niveles de optimización del compilador.

Otra forma de lograr retardos exactos, para no tener que escribir en ensamblador si alguien no se siente a gusto o no tiene el conocimiento suficiente o el compilador no soporta ensambaldor en línea, entonces basta con observar el código en ensamblador generado por el compilador para la función de retardo y calcular los ciclos de intrucción como en el ejemplo anterior.

Dic 09

Como mencioné en una entrada de este blog, cuando se establece un grado de optimización, p. ej. -Os, el compilador elimina algunas cosas "inútiles", tales como las variables del retardo. Para evitar esta situación hay que definir las variables como 'volatile'; esto obliga al compilador a dejar las variables como están. Sin embargo, no es recomendable abusar de esta cualidad.

Comentarios

Para el ARM LPC2129, dentro de crossworks 1.5, la rutina de retardo queda de la siguiente manera

void retardo(unsigned t){
asm volatile("subs r0, r0, #1");
asm volatile("bne -1");
}

y aunque en teoría el retardo tendría que ser el mismo cambiando los diferentes niveles de optimización, también varía ligeramente el retardo, sin embargo, aún así son más predecibles

Entradas populares de este blog

Compilar un kernel > 2.6 en Debian