En la historia del hardware informático , algunas de las primeras unidades de procesamiento central informático con conjuntos de instrucciones reducidos (CPU RISC) utilizaban una solución arquitectónica muy similar, ahora denominada canalización RISC clásica . Esas CPU fueron: MIPS , SPARC , Motorola 88000 y, más tarde, la CPU teórica DLX inventada para la educación.
Cada uno de estos diseños RISC escalares clásicos busca e intenta ejecutar una instrucción por ciclo . El principal concepto común de cada diseño es una canalización de instrucciones de ejecución de cinco etapas . Durante la operación, cada etapa de la canalización funciona con una instrucción a la vez. Cada una de estas etapas consta de un conjunto de flip-flops para mantener el estado y una lógica combinatoria que opera en las salidas de esos flip-flops.
La tubería RISC clásica de cinco etapas
Búsqueda de instrucciones
Las instrucciones residen en la memoria que tarda un ciclo en leerse. Esta memoria puede ser una SRAM dedicada o una caché de instrucciones . El término "latencia" se utiliza a menudo en informática y significa el tiempo desde que se inicia una operación hasta que se completa. Por lo tanto, la búsqueda de instrucciones tiene una latencia de un ciclo de reloj (si se usa SRAM de ciclo único o si la instrucción estaba en la caché). Por lo tanto, durante la etapa de obtención de instrucciones, se obtiene una instrucción de 32 bits de la memoria de instrucciones.
El Contador de programa , o PC, es un registro que contiene la dirección que se presenta a la memoria de instrucciones. Al comienzo de un ciclo, la dirección se presenta en la memoria de instrucciones. Luego, durante el ciclo, la instrucción se lee de la memoria de instrucciones y, al mismo tiempo, se realiza un cálculo para determinar la próxima PC. El cálculo de la próxima PC se realiza incrementando la PC en 4 y eligiendo si tomarla como la próxima PC o, alternativamente, tomar el resultado de un cálculo de salto / salto como la próxima PC. Tenga en cuenta que en el RISC clásico, todas las instrucciones tienen la misma longitud. (Esto es algo que separa a RISC de CISC [1] ). En los diseños originales de RISC, el tamaño de una instrucción es de 4 bytes, por lo que siempre agregue 4 a la dirección de la instrucción, pero no use PC + 4 para el caso de una rama tomada, un salto o una excepción (consulte las ramas retrasadas a continuación). ). (Tenga en cuenta que algunas máquinas modernas utilizan algoritmos más complicados ( predicción de rama y predicción de destino de rama ) para adivinar la siguiente dirección de instrucción).
Decodificación de instrucciones
Otra cosa que separa a las primeras máquinas RISC de las máquinas CISC anteriores es que RISC no tiene microcódigo . [2] En el caso de las instrucciones microcodificadas CISC, una vez extraídas de la caché de instrucciones, los bits de instrucción se desplazan por la tubería, donde la lógica combinacional simple en cada etapa de la tubería produce señales de control para la ruta de datos directamente desde los bits de instrucción. En esos diseños CISC, se realiza muy poca decodificación en la etapa tradicionalmente llamada etapa de decodificación. Una consecuencia de esta falta de decodificación es que se deben usar más bits de instrucción para especificar lo que hace la instrucción. Eso deja menos bits para cosas como índices de registro.
Todas las instrucciones MIPS, SPARC y DLX tienen como máximo dos entradas de registro. Durante la etapa de decodificación, los índices de estos dos registros se identifican dentro de la instrucción y los índices se presentan a la memoria de registros, como la dirección. Por lo tanto, los dos registros nombrados se leen del archivo de registros . En el diseño MIPS, el archivo de registro tenía 32 entradas.
Al mismo tiempo que se lee el archivo de registro, la lógica de emisión de instrucciones en esta etapa determina si la tubería está lista para ejecutar la instrucción en esta etapa. De lo contrario, la lógica de emisión hace que tanto la etapa de obtención de instrucciones como la etapa de decodificación se detengan. En un ciclo de bloqueo, los flip flops de entrada no aceptan nuevos bits, por lo que no se realizan nuevos cálculos durante ese ciclo.
Si la instrucción decodificada es una bifurcación o un salto, la dirección de destino de la bifurcación o salto se calcula en paralelo con la lectura del archivo de registro. La condición de bifurcación se calcula en el ciclo siguiente (después de leer el archivo de registro), y si se toma la bifurcación o si la instrucción es un salto, a la PC de la primera etapa se le asigna el objetivo de bifurcación, en lugar de la PC incrementada que ha sido calculado. Algunas arquitecturas hicieron uso de la unidad lógica aritmética (ALU) en la etapa de ejecución, a costa de un rendimiento de instrucción ligeramente menor.
La etapa de decodificación terminó con una gran cantidad de hardware: MIPS tiene la posibilidad de ramificarse si dos registros son iguales, por lo que un árbol AND de 32 bits de ancho se ejecuta en serie después de leer el archivo de registro, lo que crea una ruta crítica muy larga a través de este. etapa (lo que significa menos ciclos por segundo). Además, el cálculo del objetivo de bifurcación generalmente requería una adición de 16 bits y un incremento de 14 bits. La resolución de la bifurcación en la etapa de decodificación hizo posible tener una penalización por predicción errónea de una bifurcación de un solo ciclo. Dado que las ramas se tomaban con mucha frecuencia (y por lo tanto se predecían erróneamente), era muy importante mantener esta penalización baja.
Ejecutar
La etapa de ejecución es donde ocurre el cálculo real. Por lo general, esta etapa consta de una ALU y también un cambiador de bits. También puede incluir un multiplicador y divisor de ciclos múltiples.
La ALU es responsable de realizar operaciones booleanas (y, o, no, nand, ni, xor, xnor) y también de realizar sumas y restas enteras. Además del resultado, la ALU generalmente proporciona bits de estado, como si el resultado fue 0 o no, o si ocurrió un desbordamiento.
El cambiador de bits es responsable del cambio y las rotaciones.
Las instrucciones de estas máquinas RISC simples se pueden dividir en tres clases de latencia según el tipo de operación:
- Operación de registro-registro (latencia de ciclo único): operaciones de sumar, restar, comparar y lógicas. Durante la etapa de ejecución, los dos argumentos se enviaron a una ALU simple, que generó el resultado al final de la etapa de ejecución.
- Referencia de memoria (latencia de dos ciclos). Todas las cargas de la memoria. Durante la etapa de ejecución, la ALU agregó los dos argumentos (un registro y un desplazamiento constante) para producir una dirección virtual al final del ciclo.
- Instrucciones de ciclos múltiples (latencia de muchos ciclos). Multiplicar y dividir enteros y todas las operaciones de punto flotante . Durante la etapa de ejecución, los operandos de estas operaciones se alimentaron a la unidad de multiplicación / división de ciclos múltiples. El resto de la canalización estaba libre para continuar la ejecución mientras la unidad de multiplicar / dividir hacía su trabajo. Para evitar complicar la etapa de escritura diferida y la lógica del problema, la instrucción multiciclo escribía sus resultados en un conjunto de registros separado.
Acceso a la memoria
Si es necesario acceder a la memoria de datos, se hace en esta etapa.
Durante esta etapa, las instrucciones de latencia de ciclo único simplemente envían sus resultados a la siguiente etapa. Este reenvío asegura que las instrucciones de uno y dos ciclos siempre escriban sus resultados en la misma etapa de la canalización de modo que solo se pueda usar un puerto de escritura en el archivo de registro, y siempre esté disponible.
Para el almacenamiento en caché de datos directamente mapeados y etiquetados virtualmente, la más simple con diferencia de las numerosas organizaciones de caché de datos , se utilizan dos SRAM , una para almacenar datos y la otra para almacenar etiquetas.
Respóndeme
Durante esta etapa, tanto las instrucciones de ciclo único como las de dos ciclos escriben sus resultados en el archivo de registro. Tenga en cuenta que dos etapas diferentes acceden al archivo de registro al mismo tiempo: la etapa de decodificación está leyendo dos registros de origen, al mismo tiempo que la etapa de escritura diferida está escribiendo el registro de destino de una instrucción anterior. En silicio real, esto puede ser un peligro (consulte más abajo para obtener más información sobre los peligros). Esto se debe a que uno de los registros de origen que se lee en decodificación puede ser el mismo que el registro de destino que se escribe en escritura diferida. Cuando eso sucede, las mismas celdas de memoria en el archivo de registro se leen y escriben al mismo tiempo. En silicio, muchas implementaciones de celdas de memoria no funcionarán correctamente cuando se lean y escriban al mismo tiempo.
Peligros
Hennessy y Patterson acuñaron el término peligro para situaciones en las que las instrucciones en una tubería producirían respuestas incorrectas.
Riesgos estructurales
Los peligros estructurales ocurren cuando dos instrucciones pueden intentar utilizar los mismos recursos al mismo tiempo. Las canalizaciones RISC clásicas evitaron estos peligros al replicar el hardware. En particular, las instrucciones de bifurcación podrían haber utilizado la ALU para calcular la dirección de destino de la bifurcación. Si la ALU se usara en la etapa de decodificación para ese propósito, una instrucción ALU seguida de una bifurcación habría visto a ambas instrucciones intentar usar la ALU simultáneamente. Es sencillo resolver este conflicto diseñando un sumador de destino de rama especializado en la etapa de decodificación.
Riesgos de datos
Los peligros de los datos ocurren cuando una instrucción, programada a ciegas, intenta utilizar los datos antes de que los datos estén disponibles en el archivo de registro.
En la canalización clásica de RISC, los peligros de datos se evitan de una de dos maneras:
Solución A. Pasando por alto
La omisión también se conoce como reenvío de operandos .
Suponga que la CPU está ejecutando el siguiente código:
SUB r3 , r4 -> r10 ; Escribe r3 - r4 en r10 Y r10 , r3 -> r11 ; Escribe r10 y r3 en r11
Las etapas de recuperación y decodificación de instrucciones envían la segunda instrucción un ciclo después del primero. Fluyen por la tubería como se muestra en este diagrama:
En una tubería ingenua , sin consideración de peligro, el peligro de datos progresa de la siguiente manera:
En el ciclo 3, la SUB
instrucción calcula el nuevo valor de r10
. En el mismo ciclo, la AND
operación se decodifica y el valor de r10
se obtiene del archivo de registro. Sin embargo, la SUB
instrucción aún no ha escrito su resultado en r10
. La reescritura de esto ocurre normalmente en el ciclo 5 (recuadro verde). Por lo tanto, el valor leído del archivo de registro y pasado a la ALU (en la etapa Ejecutar de la AND
operación, cuadro rojo) es incorrecto.
En su lugar, debemos pasar los datos que se calcularon de SUB
nuevo a la etapa Ejecutar (es decir, al círculo rojo en el diagrama) de la AND
operación antes de que normalmente se vuelva a escribir. La solución a este problema es un par de multiplexores de derivación. Estos multiplexores se encuentran al final de la etapa de decodificación, y sus salidas flopeadas son las entradas a la ALU. Cada multiplexor selecciona entre:
- Un puerto de lectura de archivo de registro (es decir, la salida de la etapa de decodificación, como en la tubería ingenua): flecha roja
- La tubería de registro actual de la ALU (para omitir en una etapa): flecha azul
- La canalización de registro actual de la etapa de acceso (que es un valor cargado o un resultado ALU reenviado, esto proporciona la omisión de dos etapas): flecha violeta . Tenga en cuenta que esto requiere que los datos pasen un ciclo hacia atrás en el tiempo. Si esto ocurre, se debe insertar una burbuja para detener la
AND
operación hasta que los datos estén listos.
La lógica de la etapa de decodificación compara los registros escritos por instrucciones en las etapas de ejecución y acceso de la tubería con los registros leídos por la instrucción en la etapa de decodificación, y hace que los multiplexores seleccionen los datos más recientes. Estos multiplexores de derivación hacen posible que la tubería ejecute instrucciones simples con solo la latencia de la ALU, el multiplexor y un flip-flop. Sin los multiplexores, la latencia de escribir y luego leer el archivo de registro tendría que incluirse en la latencia de estas instrucciones.
Tenga en cuenta que los datos solo se pueden pasar hacia adelante en el tiempo; los datos no se pueden omitir a una etapa anterior si aún no se han procesado. En el caso anterior, los datos se transmiten (cuando AND
está listo para el registro en la ALU, SUB
ya lo ha calculado).
Solución B. Enclavamiento de tuberías
Sin embargo, tenga en cuenta las siguientes instrucciones:
LD adr -> r10 Y r10 , r3 -> r11
Los datos leídos de la dirección adr
no están presentes en la caché de datos hasta después de la etapa de acceso a la memoria de la LD
instrucción. En este momento, la AND
instrucción ya está a través de la ALU. Para resolver esto, sería necesario que los datos de la memoria se pasen hacia atrás en el tiempo a la entrada de la ALU. Esto no es posible. La solución es retrasar la AND
instrucción en un ciclo. El riesgo de datos se detecta en la etapa de decodificación, y las etapas de recuperación y decodificación se detienen ; se les impide cambiar sus entradas y, por lo tanto, permanecen en el mismo estado durante un ciclo. Las etapas de ejecución, acceso y reescritura en sentido descendente ven una instrucción extra de no operación (NOP) insertada entre las instrucciones LD
y AND
.
Este NOP se denomina burbuja de tubería ya que flota en la tubería, como una burbuja de aire en una tubería de agua, ocupando recursos pero no produciendo resultados útiles. El hardware para detectar un peligro de datos y detener la tubería hasta que se elimine el peligro se denomina enclavamiento de tubería .
Pasando hacia atrás en el tiempo | Problema resuelto usando una burbuja |
Sin embargo, no es necesario utilizar un enclavamiento de canalización con ningún reenvío de datos. El primer ejemplo de SUB
seguido de AND
y el segundo ejemplo de LD
seguido de AND
se pueden resolver deteniendo la primera etapa en tres ciclos hasta que se logre la escritura diferida y los datos en el archivo de registro sean correctos, lo que provocará que se obtenga el valor de registro correcto. por la AND
etapa 's Decode. Esto causa un gran impacto en el rendimiento, ya que el procesador pasa mucho tiempo procesando nada, pero la velocidad del reloj se puede aumentar ya que hay menos lógica de reenvío que esperar.
Este riesgo de datos se puede detectar con bastante facilidad cuando el compilador escribe el código de máquina del programa. La máquina MIPS de Stanford se basó en el compilador para agregar las instrucciones NOP en este caso, en lugar de tener los circuitos para detectar y (lo que es más difícil) detener las dos primeras etapas de la tubería. De ahí el nombre MIPS: microprocesador sin etapas de canalización interconectadas. Resultó que las instrucciones NOP adicionales agregadas por el compilador expandieron los binarios del programa lo suficiente como para reducir la tasa de aciertos de la caché de instrucciones. El hardware de bloqueo, aunque costoso, se volvió a colocar en diseños posteriores para mejorar la tasa de aciertos de la caché de instrucciones, momento en el que el acrónimo ya no tenía sentido.
Control de peligros
Los peligros de control son causados por ramificaciones condicionales e incondicionales. La canalización RISC clásica resuelve las ramas en la etapa de decodificación, lo que significa que la recurrencia de la resolución de la rama tiene una duración de dos ciclos. Hay tres implicaciones:
- La recurrencia de la resolución de bifurcación pasa por bastante circuitería: la lectura de la caché de instrucciones, la lectura del archivo de registro, el cálculo de la condición de bifurcación (que implica una comparación de 32 bits en las CPU MIPS) y el siguiente multiplexor de direcciones de instrucciones.
- Debido a que los objetivos de bifurcación y salto se calculan en paralelo a la lectura del registro, los ISA RISC normalmente no tienen instrucciones que bifurquen a una dirección de registro + desplazamiento. Saltar para registrarse es compatible.
- En cualquier rama tomada, la instrucción inmediatamente después de la rama siempre se obtiene de la caché de instrucciones. Si se ignora esta instrucción, hay una penalización de IPC de un ciclo por rama tomada , que es suficientemente grande.
Hay cuatro esquemas para resolver este problema de rendimiento con las sucursales:
- Predecir no tomado: siempre obtenga la instrucción después de la rama desde la caché de instrucciones, pero solo ejecútela si la rama no se toma. Si no se toma la rama, la tubería permanece llena. Si se toma la rama, la instrucción se vacía (marcada como si fuera un NOP) y se pierde la oportunidad de un ciclo para finalizar una instrucción.
- Probable bifurcación: siempre recupere la instrucción después de la bifurcación desde la caché de instrucciones, pero solo ejecútela si se tomó la bifurcación. El compilador siempre puede llenar el espacio de retardo de la rama en dicha rama, y dado que las ramas se toman con mayor frecuencia, tales ramas tienen una penalización de IPC menor que el tipo anterior.
- Ranura de retardo de rama : siempre recupere la instrucción después de la rama de la caché de instrucciones y siempre ejecútela, incluso si se toma la rama. En lugar de aplicar una penalización de IPC por una fracción de las ramas tomadas (tal vez el 60%) o no tomadas (tal vez el 40%), las ranuras de retardo de la rama tienen una penalización de IPC para aquellas ramas en las que el compilador no pudo programar la ranura de demora de la rama. Los diseñadores de SPARC, MIPS y MC88K diseñaron una ranura de retardo de rama en sus ISA.
- Predicción de bifurcaciones: en paralelo con la obtención de cada instrucción, adivine si la instrucción es una bifurcación o un salto y, de ser así, adivine el objetivo. En el ciclo posterior a una bifurcación o salto, busque la instrucción en el objetivo adivinado. Cuando la suposición sea incorrecta, elimine el objetivo obtenido incorrectamente.
Las ramas retrasadas fueron controvertidas, en primer lugar, porque su semántica es complicada. Una bifurcación retrasada especifica que el salto a una nueva ubicación ocurre después de la siguiente instrucción. Esa siguiente instrucción es la que inevitablemente carga la caché de instrucciones después de la bifurcación.
Las sucursales retrasadas han sido criticadas [¿ por quién? ] como una mala elección a corto plazo en el diseño de ISA:
- Los compiladores suelen tener algunas dificultades para encontrar instrucciones lógicamente independientes para colocar después de la rama (la instrucción después de la rama se llama ranura de retardo), por lo que deben insertar NOP en las ranuras de retardo.
- Los procesadores superescalares , que obtienen varias instrucciones por ciclo y deben tener alguna forma de predicción de bifurcaciones, no se benefician de las bifurcaciones retrasadas. El Alpha ISA dejó fuera las ramas retrasadas, ya que estaba destinado a procesadores superescalares.
- El inconveniente más grave de las sucursales retrasadas es la complejidad de control adicional que conllevan. Si la instrucción del intervalo de retardo tiene una excepción, el procesador debe reiniciarse en la rama, en lugar de la siguiente instrucción. Las excepciones tienen esencialmente dos direcciones, la dirección de excepción y la dirección de reinicio, y generar y distinguir entre las dos correctamente en todos los casos ha sido una fuente de errores para diseños posteriores.
Excepciones
Suponga que un RISC de 32 bits procesa una instrucción ADD que suma dos números grandes y el resultado no cabe en 32 bits.
La solución más simple, proporcionada por la mayoría de las arquitecturas, es aritmética envolvente. Los números mayores que el valor codificado máximo posible tienen sus bits más significativos cortados hasta que quepan. En el sistema de números enteros habitual, 3000000000 + 3000000000 = 6000000000. Con aritmética de envoltura de 32 bits sin firmar, 3000000000 + 3000000000 = 1705032704 (6000000000 mod 2 ^ 32). Puede que esto no parezca muy útil. El mayor beneficio de la aritmética envolvente es que cada operación tiene un resultado bien definido.
Pero el programador, especialmente si está programando en un lenguaje que admite números enteros grandes (por ejemplo, Lisp o Scheme ), puede que no quiera aritmética envolvente. Algunas arquitecturas (por ejemplo, MIPS) definen operaciones de adición especiales que se ramifican a ubicaciones especiales en caso de desbordamiento, en lugar de envolver el resultado. El software en la ubicación de destino es responsable de solucionar el problema. Esta rama especial se llama excepción. Las excepciones difieren de las bifurcaciones regulares en que la dirección de destino no está especificada por la instrucción en sí, y la decisión de la bifurcación depende del resultado de la instrucción.
El tipo más común de excepción visible por software en una de las máquinas RISC clásicas es un error de TLB .
Las excepciones son diferentes de las ramificaciones y los saltos, porque esos otros cambios de flujo de control se resuelven en la etapa de decodificación. Las excepciones se resuelven en la etapa de escritura diferida. Cuando se detecta una excepción, las siguientes instrucciones (anteriormente en la canalización) se marcan como no válidas y, a medida que fluyen hacia el final de la canalización, sus resultados se descartan. El contador del programa se establece en la dirección de un manejador de excepciones especial y los registros especiales se escriben con la ubicación y la causa de la excepción.
Para que sea fácil (y rápido) que el software solucione el problema y reinicie el programa, la CPU debe realizar una excepción precisa. Una excepción precisa significa que todas las instrucciones hasta la instrucción de excepción se han ejecutado, y la instrucción de excepción y todo lo que sigue no se ha ejecutado.
Para aceptar excepciones precisas, la CPU debe realizar cambios en el estado visible del software en el orden del programa. Esta confirmación en orden ocurre de forma muy natural en la canalización RISC clásica. La mayoría de las instrucciones escriben sus resultados en el archivo de registro en la etapa de escritura diferida, por lo que esas escrituras ocurren automáticamente en el orden del programa. Sin embargo, las instrucciones de la tienda escriben sus resultados en la cola de datos de la tienda en la etapa de acceso. Si la instrucción de almacenamiento tiene una excepción, la entrada de la cola de datos de almacenamiento se invalida para que no se escriba en la SRAM de datos de caché más adelante.
Manejo de errores de caché
Ocasionalmente, la caché de datos o de instrucciones no contiene un dato o una instrucción requeridos. En estos casos, la CPU debe suspender la operación hasta que la caché se pueda llenar con los datos necesarios, y luego debe reanudar la ejecución. El problema de llenar la caché con los datos requeridos (y potencialmente volver a escribir en la memoria la línea de caché desalojada) no es específico de la organización de la canalización y no se trata aquí.
Hay dos estrategias para manejar el problema de suspender / reanudar. La primera es una señal de pérdida global. Esta señal, cuando se activa, evita que las instrucciones avancen por la tubería, generalmente cerrando el reloj en los flip-flops al comienzo de cada etapa. La desventaja de esta estrategia es que hay una gran cantidad de chanclas, por lo que la señal de pérdida global tarda mucho en propagarse. Dado que la máquina generalmente tiene que detenerse en el mismo ciclo en el que identifica la condición que requiere el bloqueo, la señal de bloqueo se convierte en una ruta crítica que limita la velocidad.
Otra estrategia para manejar suspender / reanudar es reutilizar la lógica de excepción. La máquina hace una excepción en la instrucción infractora y todas las instrucciones adicionales se invalidan. Cuando el caché se ha llenado con los datos necesarios, se reinicia la instrucción que causó el error de caché. Para acelerar el manejo de fallas en la caché de datos, la instrucción se puede reiniciar para que su ciclo de acceso ocurra un ciclo después de que se llene la caché de datos.
Ver también
Referencias
- Hennessy, John L .; Patterson, David A. (2011). Arquitectura informática, un enfoque cuantitativo (5ª ed.). Morgan Kaufmann. ISBN 978-0123838728.
- ^ Patterson, David. "RISC I: un equipo VLSI de conjunto de instrucciones reducido" . Cite journal requiere
|journal=
( ayuda ) - ^ Patterson, David. "RISC I: un equipo VLSI de conjunto de instrucciones reducido" . Cite journal requiere
|journal=
( ayuda )