¿Podría una instrucción de bifurcación ARM (ARM7TDMI) tomar 6 ciclos?

Descubrí que una instrucción ARM Branch parece tardar 6 ciclos en ejecutarse en un procesador ARM7TDMI. Parece que no debería estar sucediendo porque en todas las referencias que he encontrado, una instrucción de bifurcación ARM7TDMI debería tomar solo 3 ciclos. Pero:

La función C:

start_time = TC;
for (int i=0; i<120; i++) {
  __asm("NOP");
}
end_time = TC;

El desmontaje muestra el bucle como: (Actualización: direcciones de instrucciones agregadas):

0x120             MOV R1, 0
0x124             B LOC0
            start:    
0x128             NOP
0x12C             ADD R1, R1, 1
            LOC0:     
0x130             CMP R1, 120
0x134             BLT start

Ahora, el resultado muestra que el bucle tarda 1080 ciclos (convertidos de un contador de tiempo puesto en TC), es decir, 9 ciclos por núcleo de bucle. Dado que NOP, ADD, CMPson todas instrucciones de un solo ciclo, BLTtiene que ser de 6 ciclos.

Una vez sospeché si mi método de sincronización tiene fallas. Pero si agrego 1 NOPen el kernel de bucle, el aumento de tiempo sería exactamente de 1 ciclo.

¿Qué pasa aquí?

(Actualización: corrección: el código de desmontaje original se escribió mal ADD R1, R1, 1como ADD R1, R1)

Actualización: Respuesta aceptada: el bloqueo de acceso a Flash provoca los 3 ciclos adicionales

Gracias a todos por las útiles respuestas y comentarios, especialmente @supercat, @Dzarda, @DaveTweed, @IgorSkochinsky, @WoutervanOoijen. Estoy ejecutando código desde flash. La CPU es una LPC23xx. De acuerdo con el Manual del usuario, incluye un Módulo de aceleración de memoria (MAM) para el acceso flash almacenado en búfer. Y los ciclos de recuperación flash sugeridos bajo la velocidad de mi CPU son exactamente 3 ciclos.

El startkernel de bucle penalizado anterior se está alineando con un límite de 8 bytes. Si cambio la alineación de startun límite de 16 bytes, entonces desaparece la penalización de 3 ciclos adicionales. Esto puede explicarse por el tamaño del búfer de precarga flash de 128 bits (16 bytes) de mi CPU.

(@WoutervanOoijen) Tenga en cuenta que el tiempo de recuperación de flash MAM de 3 ciclos no lo realiza la CPU ARM, sino el módulo MAM que precarga los datos flash en paralelo con la CPU. Entonces, en mi código con startalineación en el límite de 8 bytes, CMPes la primera instrucción en el búfer de captación previa MAM de 128 bits (4 instrucciones). Cuando la CPU ARM se ejecuta BLT, se necesita el primer ciclo para "comprender" la instrucción. Luego intenta recuperar NOPla instrucción que no está en el búfer de captación previa de MAM. Ese debería ser el momento en que ocurren los 3 ciclos adicionales cuando el MAM accede al flash. Cuando la NOPinstrucción está en el búfer (junto con otras 3 instrucciones en la línea flash de 32 bytes), la CPU ARM puede volver a llenar la canalización recuperando NOP(quinto ciclo) y decodificandoNOP(6to ciclo). De ahí provienen los 6 ciclos totales.

Entonces, la respuesta a mi pregunta es Sí, es posible una instrucción de bifurcación de 6 ciclos si hay un bloqueo de acceso flash.

Pregunta final no resuelta

Como señala @WoutervanOoijen, el razonamiento anterior tiene un defecto. El módulo de aceleración de memoria de LPC23xx tiene un búfer de seguimiento de rama adicional que se supone que evita este tipo de bifurcaciones de bucle de recuperación repetidas. El Manual del usuario de LPC23XX establece:

El búfer de Branch Trail captura la línea en la que se produce dicha ruptura no secuencial. Si se vuelve a tomar la misma bifurcación, la siguiente instrucción se toma del búfer de Branch Trail

Esta declaración no parece ser muy clara acerca de qué se está colocando exactamente en el búfer de Branch Trail. Podría ser la última línea flash precargada o la última línea flash de destino de bifurcación. En cualquier caso, la penalización de acceso flash no debería haber ocurrido porque la línea flash (0x120 ~ 0x12F) que incluye la instrucción de destino de bifurcación ( ) ya NOPdebería estar en el búfer de Branch Trail cuando BLTse está ejecutando (al menos desde la segunda vez en adelante) .

(Por cierto, verifiqué que el MAM está en modo totalmente habilitado, es decir, MAM_mode_control es 2).

Actualizaré esta pregunta después de encontrar más información sobre esto. Y te agradeceré si tienes algún comentario sobre lo que podría estar pasando aquí, o qué prueba se puede hacer para buscar pistas.

Mi apuesta sería que tiene algo que ver con errores de caché o algo en ese estadio de béisbol. De todos modos, ARM es una arquitectura bastante compleja y no debe esperar una ejecución precisa del ciclo. Hay muchas cosas que los ingenieros regulares no saben (no tienen que saber) sobre la cosa.
En primer lugar, ¿estás seguro de que el desmontaje es correcto? Parece que debería duplicar R1 (no incrementarlo) en cada iteración. En segundo lugar, puede haber un bloqueo de la tubería inducido por la dependencia de datos entre addy las cmpinstrucciones. Finalmente, 3 ciclos para bltpodría ser solo un valor mínimo; Es posible que se requieran relojes adicionales si es necesario vaciar partes de la canalización de decodificación cuando se toma la bifurcación.
Es muy difícil contar los pulsos del reloj para los ciclos de instrucción en un núcleo canalizado moderno. No estoy seguro de que sea determinista
¿Qué chip exacto es? (¿NXP?) ¿Tiene un acelerador flash/caché? ¿Tiene en cuenta el tiempo de acceso flash? ¿Obtienes un resultado diferente si ejecutas desde RAM?
Para un ARM7 que no tiene que esperar (para obtener operandos, por ejemplo), ese ciclo debería ser de 1+1+1+3 ciclos. 1) Verifique si realmente es este código el que se ejecuta (el nop parece estar en el lugar equivocado y, como dijo David, el conteo de bucles parece estar mal). 2) Compruebe si la CPU tiene que esperar a que se obtengan las instrucciones (Flash suele tener un ciclo de espera adicional).
@Dzarda Curiosamente, ARM7TDMI no tiene caché según esta página: en.wikipedia.org/wiki/List_of_ARM_microarchitectures
@xiaobai, el ARM7TDMI es una CPU, el fabricante del chip agrega la memoria (RAM, FLash), que puede incluir alguna forma de almacenamiento en caché o almacenamiento en búfer (el LPC2148, por ejemplo, tiene un búfer Flash primitivo). Entonces, ¿por qué no nos dice exactamente qué chip está usando y con qué configuración?
@DaveTweed Lo siento error de código de desmontaje solucionado.
Creo que has sacado la conclusión equivocada. Un acceso Flash de 3 ciclos significa 2 ciclos adicionales (el acceso de 1 ciclo significa que no hay paradas). Parece que tiene una sobrecarga de 6 ciclos, lo que sería consistente con 3 lecturas flash de 64 bits, que sería el caso si la etiqueta de "inicio" está en la segunda mitad de una palabra larga de 64 bits. No es el BLT en sí mismo el que obtiene la sobrecarga de 6 ciclos, es la obtención de las 4 instrucciones.
@WoutervanOoijen En realidad, el tiempo de recuperación de flash de 3 ciclos no lo realiza la CPU ARM, sino el módulo MAM que precarga los datos flash en paralelo con la CPU. Consulte mi actualización para obtener detalles sobre cómo podrían provenir los 6 ciclos totales.
@xiaobai, su razonamiento habría sido correcto para el MAM más simple de un LPC21, pero el LPC22 tiene un "búfer de seguimiento de rama", que en este caso debería proporcionar la línea que contiene el objetivo de la rama (el NOP) sin demora adicional.
@WoutervanOoijen Tienes razón. Todavía no sé por qué Branch Buffer no almacena en búfer el destino del bucle.

Respuestas (2)

¿Estás ejecutando código desde RAM o desde flash? Los procesadores ARM que ejecutan código desde flash a menudo requieren estados de espera en al menos algunas circunstancias; dichos procesadores a menudo incluyen hardware que puede eliminar la mayoría de los estados de espera en el código común, pero dicho hardware puede ser tan simple como un búfer de una sola línea que permite el acceso a la misma línea de flash que el acceso anterior para evitar el estado de espera. Si el objetivo de bifurcación es la última palabra de una línea flash, el flash requerirá dos o tres ciclos para obtener esa palabra y dos o tres ciclos para obtener la siguiente palabra. Si uno de los ciclos se realiza simultáneamente con alguna otra operación de la CPU, eso dejaría una penalización de tres ciclos.

Muchas gracias por la respuesta. Estoy ejecutando código desde flash. La CPU es una LPC23xx. De acuerdo con el Manual del usuario, incluye un Módulo de aceleración de memoria (MAM) para el acceso flash almacenado en búfer. Y los ciclos de recuperación flash sugeridos bajo la velocidad de mi CPU son exactamente 3 ciclos.
@xiaobai: ¿Adiviné correctamente la alineación relativa a la línea de caché, o me equivoqué por un byte? ¿Cambiar el código para que comience en un múltiplo de 8 palabras (32 bytes) haría que se ejecutara más rápido?
Sí, tiene razón en que el kernel de bucle debe alinearse con el tamaño de línea del búfer flash. En mi caso, el búfer de precarga flash es de 128 bits (16 bytes). Verifiqué que los ciclos adicionales no ocurren si startse alinea con el límite de 16 bytes. Anteriormente estaba en el límite de 8 bytes.

Eche un vistazo al centro de información de ARM, figura 2 , teniendo en cuenta que está trabajando con la canalización ARM7 y no con la canalización de 3 etapas M3. El punto sigue siendo válido.

Puede haber ciclos entre la obtención y la ejecución. Es muy difícil contar los pulsos del reloj para los ciclos de instrucción en un núcleo canalizado moderno. No estoy seguro de que sea determinista

Me pregunto si la canalización debe comenzar de nuevo en cada sucursal. Podría considerar apilar un montón de estos NOP en lugar de bifurcarlos para ver si su comportamiento resultante es más determinista como un paso de depuración.

De hecho, me han advertido sobre el uso de NOP para retrasos precisos en plataformas ARM por este motivo.

ingrese la descripción de la imagen aquí

-1: su respuesta se basa en diagramas de tiempo para CPU M3 y M4 (16 bits), pero la pregunta es sobre el ARM7 (32 bits).
@WoutervanOoijen Estaba asumiendo que la tubería A7 de 8 etapas sería una situación aún más difícil que la tubería M de 3 etapas, y no pude encontrar una buena ilustración para el A7, incluso después de mirar un poco. Me complace reemplazar la imagen y el enlace si puede indicarme uno mejor como ilustrativo del punto que estoy tratando de hacer.
@WoutervanOoijen, M3 y M4 son CPU de 32 bits. ¿Te refieres al conjunto de instrucciones?
Perdón por la redacción, lo que quise decir es que los Mx son (en su mayoría) CPU de instrucción de 16 bits, mientras que la pregunta es sobre ARM7TDMI en modo ARM, que es una CPU totalmente diferente, que incluso tiene una instrucción diferente. (Pero el mismo ancho no es suficiente, M0 y M0+, por ejemplo, tienen el mismo conjunto de instrucciones pero diferentes canalizaciones). No estoy seguro de lo que quiere decir con A7, ¡ARM7 no es un Cortex A7! Sin paradas, la canalización ARM7 es Fetch-Decode-Excute, con una penalización de 2 ciclos por cambiar la PC.
@ScottSeidman Gracias por la sugerencia de usar NOPen lugar de bucles. Usar NOPel resultado sería correcto.