Aquí hay algunas compensaciones diferentes.
Primero, queremos que las instrucciones sean de ancho fijo (32 bits). Esto garantiza que las instrucciones estén bloqueadas en caché y alineadas en la página, lo que simplifica la presencia de caché y presencia de página y las verificaciones de permisos.
En segundo lugar, queremos que los diversos campos de instrucciones ( opcode
/ source regs
/ immediates
) tengan un ancho fijo y una posición fija. Esto los hace más rápidos / menos lógicos para decodificar y son necesarios en las primeras etapas de la tubería. (El destination
registro no es necesario hasta el final de la tubería, por lo que puede estar en diferentes lugares R
e I
instrucciones). La posición y el ancho del function
campo son un poco menos importantes porque eso necesita controlar la función de la ALU, pero eso es en la tercera etapa de canalización, por lo que tiene un poco de tiempo para trabajar con ella si es necesario.
Pero ahora tenemos una tensión entre el número de instrucciones posibles y los tamaños de los inmediatos. Queremos que lo inmediato en las instrucciones I
e J
sea lo más largo posible. Esta es una arquitectura de 32 bits, y la J
instrucción solo tiene una dirección de 26 bits. Se le agregan dos bits de orden inferior cero (otro beneficio de las instrucciones de ancho fijo) pero aún así solo puede saltar una distancia máxima de bytes, lo que dificulta la vida del enlazador. (Una llamada de un procedimiento que está a más de bytes de distancia debe hacerse con una instrucción triple: carga-superior-inmediata, adición-inmediata, salto-y-enlace-registro, en lugar de solo un salto- y-enlace.) El inmediato de 16 bits en228228I
las instrucciones también son buenas para los escritores compiladores / enlazadores. (En el SPARC, donde el campo inmediato era de solo 12 bits, tuvieron que agregar una load-high
clase de instrucción especial completa con un inmediato de 20 bits).
Por lo tanto, realmente no queremos que el código de operación sea de 7 bits (porque se cortará en inmediatos), pero con 6 bits solo tenemos posibles instrucciones diferentes, lo cual es muy poco. El compromiso es: hay exactamente dos instrucciones de tipo (salto y salto y enlace), y solo una o dos instrucciones de tipo , con los 60 códigos de operación restantes que se utilizan para las instrucciones.26 6= 64J
R
I
Pero eso deja un margen de maniobra con las R
instrucciones. Además del código de operación de 6 bits, estos solo necesitan 15 bits adicionales para la especificación del registro, lo que deja 11 bits para el código de operación extendido y / o la cantidad de desplazamiento.
Debe pensar en el function
campo como un código de operación extendido para la R
instrucción. Solo hay un R
código de operación de instrucción, pero hay 64 diferentes functions
que la R
instrucción puede realizar.
Bueno. Tenemos 60 I
instrucciones diferentes y 64 R
instrucciones diferentes , entonces, ¿dónde debemos poner las instrucciones inmediatas de turno?
Bueno, no solo hay menos I
instrucciones, sino que hay muchas más cosas que queremos hacer con las I
instrucciones. Recuerde que todas las instrucciones de ramificación deben ser I
instrucciones porque tienen un desplazamiento relativo (inmediato). Además, todas las instrucciones de carga y almacenamiento están I
formateadas en MIPS. Y finalmente necesitamos que la instrucción load-upper-inmediate sea una I
instrucción. No solo eso, sino que las R
instrucciones todavía tienen 5 bits adicionales no utilizados (que es lo que necesitamos para el inmediato de un cambio inmediato en esta arquitectura), por lo que esto da un mayor incentivo para convertir el cambio inmediato en R
instrucciones especiales (extrañas) .
Muchas de estas decisiones son más arte que ciencia, pero existe una lógica subyacente que se puede discernir. El objetivo clave no es hacer que el número de instrucciones sea lo más pequeño posible, es hacer un alto rendimientoLa tubería encaja en un solo chip (para que pequeñas empresas, como MIPS y Sun en la década de 1980, pudieran competir con IBM y DEC). (El nombre RISC, inventado por David Patterson, es algo desafortunado. Se dio cuenta porque era lindo, no porque las "instrucciones reducidas" es una descripción precisa de lo que las arquitecturas MIPS y SPARC realmente estaban tratando de hacer). ancho fijo de instrucciones (y relativamente pequeño para que obtenga un mejor comportamiento de I-cache) para hacer que la búsqueda, la paginación y la decodificación sean más simples y rápidas. Desea las partes de la instrucción que deben decodificarse temprano (elopcode
, los dos registros de origen y el signo (extendido inmediato) deben tener un ancho fijo y una posición fija. Desea que los inmediatos sean lo más largos posible, y desea tantos tipos diferentes de instrucciones como quepan, dadas todas esas otras restricciones.
Para comprender los formatos de instrucción MIPS I, debe comprender la canalización de MIPS y también volver a pensar en la tecnología de implementación de la CPU alrededor de 1985. Si observa el diagrama (ya sabe cuál), verá que la lectura del archivo de registro está en el Etapa de identificación, justo después de IF.
Para el propósito de una instrucción de tipo R, la etapa de ID debe realizar las siguientes tareas:
Para el propósito de esta discusión, es la primera tarea en la que debe pensar. Si hay mucho trabajo de decodificación de instrucciones que tiene que hacer para incluso resolver si necesita algún valor de los registros, esto aumenta el retraso antes de que pueda comenzar las lecturas del registro. También aumenta la complejidad de la etapa de identificación. Al reservar un único código de operación para todas las instrucciones de tipo R, mantiene la complejidad al mínimo.
Parece un poco extraño que dediques cinco bits solo al cambio. Se me ocurren algunas explicaciones posibles. Una es que simplifica el enrutamiento (esos cinco bits SIEMPRE se introducen directamente en el archivo de registro, esos cinco bits SIEMPRE se introducen en la palanca de cambios de barril, esos seis bits SIEMPRE se enrutan a través de la ALU para determinar qué función realizar).
Es posible que hayan estado pensando en introducir instrucciones combinadas shift-left-and-add en el futuro. Esto presumiblemente sería de la forma:
Hoy, probablemente no lo pensaríamos dos veces antes de tener una etapa de decodificación más compleja, especialmente porque los accesos a los archivos de registro tienden a ocurrir más tarde en la tubería de una CPU superescalar típica. Muchas CPU modernas incluso realizan una descodificación de instrucciones aproximada en el momento en que se inserta una instrucción en el caché L1 . Hace que las líneas I-cache sean un poco más anchas para almacenar la información adicional (gracias a la Ley de Moore, tiene muchos transistores que desperdiciar) para que la decodificación de instrucciones "correctas" sea más simple y rápida.
Una razón por la que probablemente querían mantener el campo de código de operación lo más pequeño posible es para que no penalizara indebidamente las instrucciones de tipo J. Como probablemente sepa, las instrucciones de tipo J usan direccionamiento pseudo-directo. Para beneficio de cualquiera que juegue en casa, lo explicaré brevemente.
El campo de dirección de una instrucción de tipo J es de 26 bits. Debido a que las instrucciones siempre están alineadas con 4 bytes, no necesita almacenar los dos bits menos significativos, lo que significa que efectivamente tiene 28 bits de dirección. Sin embargo, el espacio de direcciones en MIPS I es de 32 bits. Entonces, los cuatro bits superiores de la ubicación de salto se toman del contador del programa.
Esto significa que no puede saltar directamente a una ubicación donde los cuatro bits más significativos de la ubicación de la PC son diferentes. Tendría que hacer un salto de tres instrucciones más costoso a través de un registro de cero en su lugar:
Eso no está tan mal hoy, pero en 1985, son muchos ciclos de reloj.
Robar un poco del campo de dirección reduciría aún más el alcance efectivo de un salto directo. Puede ver cómo esto podría ser un precio demasiado alto para pagar.
fuente