¿Cómo maneja MIPS I la bifurcación en la instrucción ALU anterior sin detenerse?
addiu $6,$6,5
bltz $6,$L5
nop
...
$L5:
¿Cómo es esto seguro sin detenerse, algo que el MIPS clásico ni siquiera podía hacer, excepto en caso de fallo de caché? (MIPS originalmente significaba Microprocesador sin etapas de tubería interbloqueadas y tenía una ranura de retardo de carga en lugar de interbloqueo).
MIPS I original es un diseño RISC clásico de 5 etapas IF ID EX MEM WB
que oculta toda su latencia de rama con una sola ranura de retardo de rama al verificar las condiciones de la rama temprano, en la etapa de identificación (corrección: este fue el error, lea esta respuesta; no No se deje engañar por el resto de los detalles de la pregunta basada en esta premisa falsa). Es por eso que se limita a verificaciones de bits de signo o iguales/no iguales, como lt o ge zero, no lt entre dos registros que necesitarían propagación de acarreo a través de un sumador.
¿No significa esto que las ramas necesitan que su entrada esté lista un ciclo antes que las instrucciones de ALU? El bltz
ingresa a la etapa ID en el mismo ciclo que addiu
ingresa a EX.
MIPS I (también conocido como R2000) utiliza el reenvío de derivación desde la salida EX a la entrada EX, por lo que las instrucciones ALU enteras normales (como una cadena de addu
/ xor
) tienen latencia de un solo ciclo y pueden ejecutarse en ciclos consecutivos.
MIPS significa "Microprocesador sin etapas de tubería entrelazadas ", por lo que no detecta peligros RAW; el código tiene que evitarlos. (De ahí las ranuras de retraso de carga en MIPS de primera generación, con MIPS II agregando interbloqueos para detenerse en ese caso, invalidando el acrónimo: P).
Pero nunca veo ninguna discusión sobre el cálculo de la condición de bifurcación con varias instrucciones por delante para evitar una parada. (El ejemplo addiu/bltz fue emitido por MIPS gcc5.4 -O3 -march=mips1
en Godbolt , que respeta las ranuras de retardo de carga y se llena nop
si es necesario).
¿Utiliza algún tipo de truco como EX que lee entradas en el flanco descendente del reloj y que ID no necesita valores de registro reenviados hasta el flanco ascendente? (Con EX produciendo sus resultados lo suficientemente temprano como para que funcione)
Supongo que eso tendría sentido si la velocidad del reloj tiene un límite lo suficientemente bajo como para que el acceso a la caché sea de un solo ciclo.
El bloqueo o la burbuja en MIPS afirma que lw
+ a beq
en el resultado de la carga necesita 2 ciclos de bloqueo porque no puede avanzar. Eso no es exacto para MIPS I real (a menos que gcc tenga errores). Sin embargo, sí menciona ciclos de medio reloj, lo que permite escribir un valor y luego leerlo del archivo de registro en el mismo ciclo completo.
TL:DR: Classic MIPS I verifica las condiciones de la sucursal en la primera mitad del ciclo de EX, por lo que reenviarlos no es especial.
IF solo necesita la dirección en la segunda mitad de un ciclo para que EX pueda reenviarla.
Estos factores se combinan para dar solo 1 ciclo de latencia de bifurcación (oculto por 1 ranura de retardo), sin problema para las bifurcaciones que dependen de la instrucción ALU anterior.
Definitivamente era seguro ejecutar sltu
MIPS beq
I (R2000) . Eso aparece como la expansión de la bgeu
pseudoinstrucción, por ejemplo, en manuales y libros de MIPS reales sin ninguna advertencia de que no sea seguro en MIPS R2000 o cualquier otro MIPS.
GCC utiliza secuencias como esa en la práctica incluso march=mips1
respetando las ranuras de retardo de carga y otras características del MIPS R2000 real.
El IF de MIPS no necesita una dirección hasta la segunda mitad de un ciclo de reloj, lo que permite a EX producirla lo suficientemente rápido.
De Ver MIPS ejecutado por Dominic Sweetman, (que cubre MIPS I a MIPS IV), Capítulo 1.5.1 Restricciones en las instrucciones
Veremos más adelante que la bifurcación condicional eficiente significa que la decisión sobre si realizar una bifurcación o no tiene que limitarse a sólo la mitad de una etapa del proceso; la arquitectura ayuda a mantener las pruebas de decisión de rama muy simples. Entonces, las ramas condicionales (en MIPS) prueban un solo registro para signo/cero o un par de registros para igualdad.
Su Figura 1.3: Los retrasos de la bifurcación y de la tubería muestran la condición de la bifurcación calculada en la primera mitad de EX y utilizada en la segunda mitad de IF, para una latencia total de la bifurcación de solo 1 ciclo/etapa de tubería (ID)/instrucción. En realidad, IF no comienza hasta la segunda mitad de un ciclo de reloj. (Y continúa con ID. La decodificación/recuperación de registro real de ID solo toma la última fracción de un ciclo de reloj).
Eso tiene el mismo resultado final que sugerí en la pregunta (verifique la condición de bifurcación al final de ID), excepto que solo requiere EX -> EX reenvío para bifurcarse según el resultado de la instrucción ALU anterior.
Quizás estaba recordando mal o malinterpretando algo que había leído anteriormente sobre la decisión de rama de medio ciclo. Esta cosa de medio ciclo bien podría ser exactamente lo que recordaba haber visto.
Para citar más, consulte MIPS Run 1.5.5 Efectos de canalización visibles para el programador
• Sucursales retrasadas: [el primer párrafo explica el intervalo de retraso de sucursales]
Si el hardware no hiciera nada especial, la decisión de bifurcar o no, junto con la dirección de destino de la bifurcación, surgiría al final de la etapa de canalización de la ALU, a tiempo para recuperar la instrucción de destino de la bifurcación en lugar de las siguientes dos instrucciones. Pero las bifurcaciones son lo suficientemente importantes como para justificar un tratamiento especial, y en la Figura 1.3 [descrita arriba] se puede ver que se proporciona una ruta especial a través de la ALU para que la dirección de la bifurcación esté disponible medio ciclo de reloj antes. Junto con el cambio impar de medio ciclo de reloj de la etapa de búsqueda de instrucciones , eso significa que el objetivo de bifurcación se puede buscar a tiempo para convertirse en el penúltimo, por lo que el hardware ejecuta la instrucción de bifurcación, luego la instrucción de ranura de retardo de bifurcación, y luego el objetivo de la sucursal, sin más demoras.
... [no desperdicies tus espacios de demora en sucursales]
... [muchos ensambladores de MIPS reordenarán las instrucciones si es seguro, para ocultar el retraso de la rama]
Ver MIPS Run tiene un prólogo de John L. Hennessy, fundador de MIPS Technologies, etc., etc. Eso no es prueba de que aprobó que todo lo que aparece en el libro sea exacto, pero es una buena evidencia de que la descripción del libro de cómo MIPS manejó este truco es preciso.
Es fácilmente comprensible y 100% plausible; ya sabemos que la caché de datos tiene latencia de recuperación de un solo ciclo (después de la generación de direcciones en la etapa EX).
En realidad estás haciendo dos preguntas:
- ¿Es eso seguro en MIPS I?
- ¿Si es así, cómo?
¿Es eso seguro en MIPS I?
He visto diferentes diagramas de bloques de CPU MIPS. La mayoría de ellos realizan la decisión de rama en la etapa EX
o incluso en la MEM
etapa en lugar de la ID
etapa.
Por supuesto, dichos diseños reaccionarán de manera diferente cuando se ejecute su código de ejemplo.
Sin una declaración oficial del manual de la CPU que realmente está utilizando, su pregunta no puede responderse con certeza.
(La respuesta de Paul Clayton sobre ¿Es eso cierto si siempre podemos llenar el espacio de retraso, no hay necesidad de predicción de rama? Está de acuerdo en que un espacio de retraso oculta completamente la latencia de rama en MIPS R2000, pero no en MIPS R4000. Entonces, esa es una buena evidencia de que el comercio real Las CPU MIPS funcionan de la forma que supone la pregunta, a pesar de la existencia de varias implementaciones que podrían no seguir exactamente MIPS ISA).
¿Si es así, cómo?
¿No significa esto que las ramas necesitan que su entrada esté lista un ciclo antes que las instrucciones de ALU?
No.
La clave es la lógica de reenvío de derivación. Echemos un vistazo al siguiente ejemplo:
add $A, $B, $C ; Currently in MEM stage
or $D, $E, $F ; Currently in EX stage
bltz $G, someLabel ; Currently in ID stage
(Mientras que A
, B
, ... G
son números GPR).
La lógica de reenvío de derivación para la fase EX ( or
instrucción) contiene un multiplexor que funciona de la siguiente manera (pseudocódigo):
if E = A
take ALU input from EX/MEM shift register output
else
take ALU input from ID/EX shift register output
end-if
Es este multiplexor el que permite utilizar el resultado de alguna instrucción ( add
) en la siguiente ( or
).
Por supuesto, se puede hacer lo mismo para la ID
fase usando un multiplexor de 3 vías:
if G = D
take branch decision input from ALU output
else if G = A
take branch decision input from EX/MEM shift register output
else
take branch decision input from register bank output
end-if
Al hacer esto, el tiempo de propagación de la señal aumentará según el tiempo necesario en la EX
fase. Esto significa que esto limitará la frecuencia de reloj del procesador.
Sin embargo, el resultado de alguna instrucción ya se puede utilizar en la ID
etapa de la siguiente instrucción sin necesidad de un ciclo de reloj adicional.