Está en la página 1de 100

Traducido del inglés al español - www.onlinedoctranslator.

com

276 EL NIVEL DE MICROARQUITECTURA CAP. 4

debe estar en la dirección 0). Depende del microensamblador colocar cada microinstrucción
en una dirección adecuada y vincularlas en secuencias cortas utilizando el
SIGUIENTE DIRECCION campo. Cada secuencia comienza en la dirección correspondiente al valor numérico
del código de operación IJVM que interpreta (por ejemplo,MÚSICA POP comienza en 0x57), pero el resto de
la secuencia puede estar en cualquier lugar del almacén de control y no necesariamente en direcciones
consecutivas.
Ahora considere la IJVM AÑADO instrucción. La microinstrucción ramificada por el
bucle principal es la etiquetadaiadd1. Esta instrucción inicia el trabajo específico para
AÑADO:

1. TOS ya está presente, pero la palabra siguiente de la pila debe


obtenerse de la memoria.
2. TOS debe agregarse a la palabra siguiente recuperada de la memoria.

3. El resultado, que se va a insertar en la pila, debe almacenarse de nuevo en la


memoria, así como almacenarse en el TOS Registrarse.

Para obtener el operando de la memoria, es necesario disminuir el puntero de la pila y


escribirlo en MAR. Tenga en cuenta que, convenientemente, esta dirección también es la
dirección que se utilizará para la escritura posterior. Además, dado que esta ubicación será
la nueva parte superior de la pila,SP debe asignarse este valor. Por tanto, una sola
operación puede determinar el nuevo valor deSP y MAR, decremento SP, y escríbalo en
ambos registros.
Estas cosas se logran en el primer ciclo, iadd1, y se inicia la operación de lectura.
Además,MPC obtiene el valor de iadd1's SIGUIENTE DIRECCION campo, que es la dirección
de iadd2, donde sea que esté. Luegoiadd2 se lee desde el almacén de control. Durante
el segundo ciclo, mientras esperamos que se lea el operando de la memoria, copiamos
la palabra superior de la pila deTOS dentro H donde estará disponible para la adición
cuando se complete la lectura.
Al comienzo del tercer ciclo, iadd3, MDR contiene el sumando obtenido de la memoria. En
este ciclo se suma al contenido deH y el resultado se almacena de nuevo en MDR, así como de
regreso a TOS. También se inicia una operación de escritura, almacenando la nueva palabra de la
parte superior de la pila de nuevo en la memoria. En este ciclo elir a tiene el efecto de asignar la
dirección de Main1 para MPC, devolviéndonos al punto de partida para la ejecución de la siguiente
instrucción.
Si el código de operación IJVM subsiguiente, ahora contenido en MBR, es 0x64 (
ISUB), casi exactamente la misma secuencia de eventos vuelve a ocurrir. DespuésMain1
se ejecuta, el control se transfiere a la microinstrucción en 0x64 (isub1). A esta
microinstrucción le sigue isub2 y isub3, y luego Main1 de nuevo. La única diferencia
entre esta secuencia y la anterior es que enisub3, los contenidos de H se restan de MDR
en lugar de agregarle.
La interpretación de YO Y es casi idéntica a la de AÑADO y ISUB, excepto que
las dos palabras superiores de la pila se unen con AND bit a bit en lugar de
sumarlas o restarlas. Algo parecido ocurre conIOR.
SEGUNDO. 4.3 UN EJEMPLO DE IMPLEMENTACIÓN 277

Si el código de operación IJVC es DUP, POP, o INTERCAMBIO, la pila debe ajustarse. los
DUP instrucción simplemente replica la palabra superior de la pila. Dado que el valor de esta
palabra ya está almacenado enTOS, la operación es tan simple como incrementar SP para señalar
la nueva ubicación y almacenar TOS a esa ubicación. losMÚSICA POP la instrucción es casi tan
simple, simplemente decrementando SP para descartar la palabra superior de la pila. Sin
embargo, para mantener la palabra principal enTOS ahora es necesario leer la nueva palabra
principal de la memoria y escribirla en TOS. Finalmente, el INTERCAMBIO La instrucción implica
intercambiar los valores en dos ubicaciones de memoria: las dos palabras superiores de la pila.
Esto se hace algo más fácil por el hecho de queTOS ya contiene uno de esos valores, por lo que no
es necesario leerlo de la memoria. Esta instrucción se discutirá con más detalle más adelante.

los BIPUSH La instrucción es un poco más complicada porque el código de operación va


seguido de un solo byte, como se muestra en la figura 4-18. El byte debe interpretarse
como un entero con signo. Este byte, que ya se ha recuperado enMBR en Main1, debe
extenderse por signo a 32 bits y colocarse en la parte superior de la pila. Esta secuencia, por
lo tanto, debe firmar-extender el byte enMBR a 32 bits y cópielo en MDR. Finalmente,
SP se incrementa y se copia a MAR, permitiendo que el operando se escriba en la parte
superior de la pila. A lo largo del camino, este operando también debe copiarse aTOS. Nota
que antes de volver al programa principal, se inició ordenador personal debe incrementarse y hay una opción de

la eración para que el siguiente código de operación recuperación disponible en Principal1.

BIPUSH
BYTE
(0×10)

Figura 4-18. los BIPUSH formato de instrucción.

A continuación, considere el YO CARGO instrucción. YO CARGO también tiene un byte después


del código de operación, como se muestra en la Fig. 4-19 (a), pero este byte es un índice (sin
signo) para identificar la palabra en el espacio de la variable local que se va a insertar en la pila.
Como solo hay 1 byte, solo 28 = Se pueden distinguir 256 palabras, es decir, las primeras 256
palabras en el espacio variable local. losYO CARGO la instrucción requiere tanto una lectura (para
obtener la palabra) como una escritura (para colocarla en la parte superior de la pila). Sin
embargo, para determinar la dirección de lectura, el desplazamiento, contenido enMBR, debe
agregarse al contenido de LV. Ya que ambos MBR y LV solo se puede acceder a través del bus B,
primero LV se copia en H (en iload1), luego MBR está agregado. El resultado de esta adición
se copia en MAR y una lectura iniciada (en iload2).

YO CARGO AMPLIO YO CARGO ÍNDICE ÍNDICE


ÍNDICE
(0x15) (0xC4) (0x15) BYTE 1 BYTE 2

(a) (B)

Figura 4-19. (a) YO CARGO con un Índice de 1 byte. (B)ILOAD ANCHO con un índice de 2 bytes.
278 EL NIVEL DE MICROARQUITECTURA CAP. 4

Sin embargo, el uso de MBR para un índice es ligeramente diferente que en BIPUSH,
donde se extendió el letrero. En el caso de un índice, el desplazamiento es siempre positivo, por lo que
el desplazamiento de bytes debe interpretarse como un entero sin signo, a diferencia de enBIPUSH,
donde se interpretó como un entero de 8 bits con signo. La interfaz deMBR al bus B está
cuidadosamente diseñado para hacer posibles ambas operaciones. En el caso deBIPUSH
(entero de 8 bits con signo), la operación adecuada es extensión de signo, es decir, el bit más a la
izquierda en el 1 byte MBR se copia en los 24 bits superiores del bus B. En el caso de
YO CARGO (entero de 8 bits sin signo), la operación adecuada es de relleno con ceros. Aquí, los 24
bits superiores del bus B simplemente se suministran con ceros. Estas dos operaciones se
distinguen por señales separadas que indican qué operación se debe realizar (ver Fig. 4-6). En el
microcódigo, esto se indica medianteMBR (firmar extendido, como en
BIPUSH 3) o MBRU (unsigned, como en iload2).
Mientras espera que la memoria suministre el operando (en iload3), SP se incrementa para
contener el valor para almacenar el resultado, la nueva parte superior de la pila. Este valor también se
copia aMAR en preparación para escribir el operando en la parte superior de la pila. ordenador personal
nuevamente debe incrementarse para obtener el siguiente código de operación (en iload4). Finalmente, MDR se
copia a TOS para reflejar la nueva parte superior de la pila (en iload5).
ISTORE es la operación inversa de YO CARGO, es decir, una palabra se quita de la parte superior
de la pila y se almacena en la ubicación especificada por la suma de LV y el índice contenido en la
instrucción. Utiliza el mismo formato queYO CARGO, se muestra en la Fig. 4-19 (a), excepto con el
código de operación 0x36 en lugar de 0x15. Esta instrucción es algo diferente de lo que cabría
esperar porque la palabra superior de la pila ya se conoce (enTOS), por lo que se puede guardar
de inmediato. Sin embargo, la nueva palabra de la parte superior de la pila debe leerse de la
memoria. Por lo tanto, se requieren tanto una lectura como una escritura, pero se pueden
realizar en cualquier orden (o incluso en paralelo, si fuera posible).
Ambos YO CARGO y ISTORE están restringidos porque solo pueden acceder a las primeras 256
variables locales. Si bien para la mayoría de los programas esto puede ser todo el espacio de variables
locales necesario, es, por supuesto, necesario poder acceder a una variable donde sea que esté ubicada
en el espacio de variables locales. Para lograr esto, IJVM usa el mismo mecanismo empleado en JVM
para lograr esto: un código de operación especialAMPLIO, conocido como byte de prefijo,
Seguido por el YO CARGO o ISTORE código de operación. Cuando ocurre esta secuencia, las definiciones de
YO CARGO y ISTORE se modifican, con un índice de 16 bits siguiendo el código de operación en lugar de un
índice de 8 bits, como se muestra en la figura 4-19 (b).
AMPLIO se decodifica de la forma habitual, lo que lleva a una rama a ancho1 que maneja el AMPLIO
código de operación. Aunque el código de operación para ampliar ya está disponible enMBR,
ancho1 obtiene el primer byte después del código de operación, porque la lógica del microprograma
siempre espera que esté allí. Luego se realiza una segunda rama de múltiples vías enancho2,
esta vez usando el byte siguiente AMPLIO para despachar. Sin embargo, desdeILOAD ANCHO
requiere un microcódigo diferente al YO CARGO, y AMPLIO ISTORE requiere un microcódigo diferente
al ISTORE, etc., la segunda rama de múltiples vías no puede simplemente usar el código de
operación como la dirección de destino, la forma Main1 lo hace.
En lugar de, ancho2 OR 0x100 con el código de operación mientras lo coloca en MPC.
Como resultado, la interpretación de ILOAD ANCHO comienza en 0x115 (en lugar de 0x15), el
SEGUNDO. 4.3 UN EJEMPLO DE IMPLEMENTACIÓN 279

Dirección Tienda de control


0×1FF

Microinstrucción
orden de ejecución

AMPLIO
ILOAD ILOAD
0×115 wide_iload1 3

0×100 Main1 1 1

0×C4 ancho1 2

0×15 iload1 2

0×00

Figura 4-20. La secuencia de microinstrucción inicial para YO CARGO y AMPLIA ILOAD.


Las direcciones son ejemplos.

interpretación de AMPLIO ISTORE comienza en 0x136 (en lugar de 0x36), y así sucesivamente. En esto
manera, cada AMPLIO El código de operación comienza en una dirección de 256 (es decir, 0x100) palabras más
altas en el almacén de control que el código de operación regular correspondiente. La secuencia inicial de
microinstrucciones para ambosYO CARGO y ILOAD ANCHO se muestra en la Fig. 4-20.
Una vez que se alcanza el código para implementar ILOAD ANCHO (0x115), el código
difiere del normal YO CARGO solo que el índice debe construirse concatenando 2 bytes de
índice en lugar de simplemente extender el signo de un solo byte. La concatenación y la
posterior adición deben realizarse en etapas, primero copiandoÍNDICE BYTE 1 dentro H
desplazado a la izquierda en 8 bits. Dado que el índice es un entero sin signo,MBR se
extiende a cero usando MBRU. Ahora se agrega el segundo byte del índice (la operación de
suma es idéntica a la concatenación ya que el byte de orden inferior de H ahora es cero, lo
que garantiza que no habrá acarreo entre los bytes), con el resultado nuevamente
almacenado en H. A partir de aquí, la operación puede proceder exactamente como si fuera
un estándar. YO CARGO. En lugar de duplicar las instrucciones finales de ILOAD (iload3 para
iload5), simplemente nos ramificamos de ancho iload4 para iload3. Sin embargo, tenga en cuenta que
ordenador personal debe incrementarse dos veces durante la ejecución de la instrucción para dejarla
apuntando al siguiente código de operación. losYO CARGO la instrucción lo incrementa una vez; los
ILOAD ANCHO La secuencia también lo incrementa una vez.

La misma situación ocurre para AMPLIO ISTORE: después de que se ejecuten las primeras
cuatro microinstrucciones (istore1 ancho para ancho istore4), la secuencia es la misma que la
280 EL NIVEL DE MICROARQUITECTURA CAP. 4

secuencia para ISTORE después de las dos primeras instrucciones, ancho istore4 ramas a
istore3.
Nuestro siguiente ejemplo es un LDC W instrucción. Este código de operación es diferente de YO CARGO
en dos maneras. Primero, tiene un desplazamiento sin firmar de 16 bits (como la versión ancha deYO CARGO).
En segundo lugar, está indexado CPP en vez de LV, ya que su función es leer del grupo
constante en lugar del marco de la variable local. (En realidad, hay una forma corta de
LDC W (LDC), pero no lo incluimos en IJVM, ya que la forma larga incorpora todas las
variaciones posibles de la forma corta, pero toma 3 bytes en lugar de 2.)
los IINC instrucción es la única instrucción IJVM que no sea ISTORE que puede modificar una
variable local. Lo hace incluyendo dos operandos, cada uno de 1 byte de longitud, como
mostrado en la Fig. 4-21.

IINC
ÍNDICE CONST
(0x84)

Figura 4-21. los IINC la instrucción tiene dos diferentes campos de operandos.

los IINC usos de instrucción ÍNDICE para especificar el desplazamiento desde el principio del
marco de la variable local. Lee esa variable, incrementándola enCONST, un valor contenido en la
instrucción y lo almacena en la misma ubicación. Tenga en cuenta que esta instrucción puede
incrementarse en una cantidad negativa, es decir,CONST es una constante de 8 bits con signo, en
el rango -128 al +127. La JVM completa incluye una versión amplia deIINC
donde cada operando tiene 2 bytes de longitud.
Ahora llegamos a la primera instrucción de rama IJVM: IR A. La única función de esta instrucción es
cambiar el valor de ORDENADOR PERSONAL, de modo que la siguiente instrucción IJVM ejecutada es la que se
encuentra en la dirección calculada agregando el desplazamiento (con signo) de 16 bits a la dirección del
código de operación de la rama. Una complicación aquí es que el desplazamiento es relativo al valor que
ordenador personal tenía al inicio de la decodificación de la instrucción, no el valor que tiene después de que se

hayan obtenido los 2 bytes de compensación.


Para aclarar este punto, en la figura 4-22 (a) vemos la situación al comienzo de
Principal1. El código de operación ya está en MBR, pero ordenador personal aún no se ha incrementado. En la
figura 4-22 (b) vemos la situación al comienzo degoto1. Por ahora ordenador personal se ha incrementado,
pero el primer byte de compensación aún no se ha recuperado en MBR. Una microinstrucción más tarde
tenemos la figura 4-22 (c), en la que el antiguo ORDENADOR PERSONAL, que apunta al código de operación,
se ha guardado en OPC y el primer byte de compensación está en MBR. Este valor es necesario porque el
desplazamiento de la IJVM IR A instrucción es relativa a él, no al valor actual de ORDENADOR PERSONAL. De
hecho, esta es la razón por la que necesitábamos OPC registrarse en primer lugar.

La microinstrucción en goto2 inicia la búsqueda del segundo byte de compensación, lo que lleva a la
figura 4-22 (d) al comienzo de goto3. Después de que el primer byte de compensación se haya desplazado 8 bits
a la izquierda y se haya copiado en H llegamos a goto4 y Fig. 4-22 (e). Ahora tenemos el primer byte de
compensación desplazado a la izquierda enH el segundo byte de compensación en MBR, y la base en OPC. Al
construir el desplazamiento completo de 16 bits en H y luego agregarlo a la base, obtenemos el
SEGUNDO. 4.3 UN EJEMPLO DE IMPLEMENTACIÓN 281

Memoria
1 byte
n+3

n+2 BYTE DE DESPLAZAMIENTO 2 OFFSET BYTE 2 OFFSET BYTE 2 OFFSET BYTE 2 OFFSET BYTE 2

n+1 BYTE DE DESPLAZAMIENTO 1 BYTE DE DESPLAZAMIENTO 1 BYTE DE DESPLAZAMIENTO 1 BYTE DE DESPLAZAMIENTO 1 BYTE DE DESPLAZAMIENTO 1

norte GOTO (0xA7) GOTO (0xA7) GOTO (0xA7) GOTO (0xA7) GOTO (0xA7)

Registros

ordenador personal norte n+1 n+1 n+2 n+2

OPC norte norte norte

MBR 0xA7 0xA7 BYTE DE DESPLAZAMIENTO 1 BYTE DE DESPLAZAMIENTO 1 OFFSET BYTE 2

H DESPLAZAMIENTO 1 << 8

(a) (B) (C) (D) (mi)

Figura 4-22. los situación al inicio de varias microinstrucciones. (a)Principal1.


(B) goto1. (C) goto2. (D) goto3. (mi) goto4.

nueva dirección para poner ORDENADOR PERSONAL, en goto5. Tenga en cuenta que usamos MBRU en goto4
en lugar de MBR porque no queremos una extensión de signo del segundo byte. El desplazamiento de
16 bits se construye, de hecho, uniendo las dos mitades en OR. Finalmente, tenemos que buscar el
siguiente código de operación antes de volver aMain1 porque el código allí espera el siguiente código de
operación en MBR. El último ciclo goto6, es necesario porque los datos de la memoria deben obtenerse a
tiempo para aparecer en MBR durante Principal1.
Las compensaciones utilizadas en el ir a Las instrucciones IJVM son valores de 16 bits con
signo, con un mínimo de -32768 y un máximo de +32767. Esto significa que no es posible bifurcar
en ambos sentidos a etiquetas más distantes que estos valores. Esta propiedad se puede
considerar como un error o una característica en IJVM (y también en JVM). El campamento de
errores diría que la definición de JVM no debería restringir su estilo de programación. El campo
de características diría que el trabajo de muchos programadores mejoraría radicalmente si
tuvieran pesadillas sobre el temido mensaje del compilador.

El programa es demasiado grande y peludo. Debes reescribirlo. Compilación abortada.

Desafortunadamente (en nuestra opinión) este mensaje aparece solo cuando un luego o demás La cláusula
supera los 32 KB, normalmente al menos 50 páginas de Java.
Ahora considere las tres instrucciones de rama condicional de IJVM: IFLT, IFEQ, y
SI ICMPEQ. Los dos primeros sacan la palabra superior de la pila, ramificándose si es menor
que cero o igual a cero, respectivamente. SI ICMPEQ saca las dos primeras palabras de la pila
y se ramifica si y solo si son iguales. En los tres casos, es necesario leer una nueva palabra
de la parte superior de la pila para almacenar enTOS.
El control para estas tres instrucciones es similar: el operando o los operandos se colocan primero
en los registros, luego se lee el nuevo valor de la parte superior de la pila TOS, finalmente se hacen la
prueba y la ramificación. ConsiderarIFLT primero. La palabra para probar ya está enTOS,
282 EL NIVEL DE MICROARQUITECTURA CAP. 4

pero desde IFLT saca una palabra de la pila, la nueva parte superior de la pila debe leerse para
almacenarla TOS. Esta lectura se inicia en iflt1. En iflt2, la palabra a ser probada se guarda en
OPC por el momento, por lo que el nuevo valor se puede poner en TOS en breve sin perder el
actual. Eniflt3 la nueva palabra de primer nivel está disponible en MDR, por lo que se copia a TOS.
Finalmente, en iflt4 la palabra para ser probada, ahora guardada en OPC, se ejecuta a través de la
ALU sin ser almacenado y el norte bit bloqueado y probado. Esta microinstrucción también
contiene una rama, eligiendo entreT si la prueba fue exitosa o F de lo contrario.
Si tiene éxito, el resto de la operación es esencialmente el mismo que al
comienzo de la IR A instrucción, y la secuencia simplemente continúa en medio
de la IR A secuencia, con goto2. Si no tiene éxito, una secuencia corta (F, F2,
y F3) Es necesario omitir el resto de la instrucción (el desplazamiento) antes de volver a
Main1 para continuar con la siguiente instrucción.
El código en ifeq2 y ifeq3 sigue la misma lógica, solo usando el Z poco en lugar del norte poco.
En ambos casos, corresponde al ensambladorMAL reconocer que las direcciones T y F son
especiales y para asegurarse de que sus direcciones se coloquen en direcciones de
almacenamiento de control que difieren solo en el bit más a la izquierda.
La lógica de SI ICMPEQ es aproximadamente similar a IFEQ excepto que aquí también
necesitamos leer en el segundo operando. Se almacena enH en si icmpeq3, donde se inicia la
lectura de la nueva palabra de la parte superior de la pila. Nuevamente, la palabra actual de la
parte superior de la pila se guarda enOPC y el nuevo instalado en TOS. Finalmente, la prueba en si
icmpeq6 es parecido a ifeq4.
Ahora, consideramos la implementación de INVOCAR EVIRTUAL y VUELVO, las instrucciones
para invocar una llamada a procedimiento y regresar, como se describe en la Sec. 4.2.3.
INVOCAR EVIRTUAL, una secuencia de 22 microinstrucciones, es la instrucción IJVM más compleja
implementada. Su funcionamiento se muestra en la figura 4-12. La instrucción utiliza su
desplazamiento de 16 bits para determinar la dirección del método que se invocará. En nuestra
implementación, la compensación es simplemente una compensación en el grupo constante.
Esta ubicación en el grupo constante apunta al método que se invocará. Sin embargo, recuerde
que los primeros 4 bytes de cada método sonno instrucciones. En su lugar, son dos punteros de
16 bits. El primero da el número de palabras de parámetro (incluyendoOBJREF—Ver Fig. 4-12). El
segundo da el tamaño del área de la variable local en palabras. Estos campos se obtienen a
través del puerto de 8 bits y se ensamblan como si fueran dos desplazamientos de 16 bits dentro
de una instrucción.
Luego, la información de vinculación necesaria para restaurar la máquina a su estado anterior: la
dirección de inicio del área de variable local anterior y la anterior. ordenador personal: Se almacena
inmediatamente encima del área de variable local recién creada y debajo de la nueva pila. Finalmente,
se obtiene el código de operación de la siguiente instrucción yordenador personal se incrementa antes de
volver a Main1 para comenzar la siguiente instrucción.
VUELVO es una instrucción simple que no contiene operandos. Simplemente usa la dirección
almacenada en la primera palabra del área de variables locales para recuperar la información de
enlace. Entonces restauraSP, LV, y ordenador personal a sus valores anteriores y copia el valor de
retorno de la parte superior de la pila actual a la parte superior de la pila original, como se
muestra en la figura 4-13.
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 283

4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA

Como casi todo lo demás en informática, el diseño del nivel de microarquitectura está
lleno de compensaciones. Las computadoras tienen muchas características deseables, que
incluyen velocidad, costo, confiabilidad, facilidad de uso, requisitos de energía y tamaño
físico. Sin embargo, una compensación determina las decisiones más importantes que debe
tomar el diseñador de la CPU: velocidad versus costo. En esta sección, analizaremos este
tema en detalle para ver qué se puede intercambiar con qué, cómo se puede lograr un alto
rendimiento y a qué precio en hardware y complejidad.

4.4.1 Velocidad versus costo

Si bien la tecnología más rápida ha dado como resultado la mayor aceleración en cualquier
período de tiempo, eso está más allá del alcance de este texto. Las mejoras de velocidad debidas
a la organización, aunque menos sorprendentes que las debidas a los circuitos más rápidos, han
sido impresionantes. La velocidad se puede medir de diversas formas, pero dada una tecnología
de circuito y un ISA, existen tres enfoques básicos para aumentar la velocidad de ejecución:

1. Reducir el número de ciclos de reloj necesarios para ejecutar una instrucción.

2. Simplifique la organización para que el ciclo del reloj pueda ser más corto.

3. Superposición de la ejecución de instrucciones.

Los dos primeros son obvios, pero existe una sorprendente variedad de oportunidades de diseño
que pueden afectar drásticamente el número de ciclos del reloj, el período del reloj o, con mayor
frecuencia, ambos. En esta sección, daremos un ejemplo de cómo la codificación y decodificación
de una operación puede afectar el ciclo del reloj.
El número de ciclos de reloj necesarios para ejecutar un conjunto de operaciones se conoce como
longitud de la trayectoria. A veces, la longitud de la ruta se puede acortar agregando hardware
especializado. Por ejemplo, agregando un incrementador (conceptualmente, un sumador con un lado
conectado permanentemente para agregar 1) aORDENADOR PERSONAL, ya no tenemos que usar la ALU
para avanzar ORDENADOR PERSONAL, eliminando ciclos. El precio que se paga es más hardware. Sin
embargo, esta capacidad no ayuda tanto como cabría esperar. Para la mayoría de las instrucciones, los
ciclos consumidos incrementando la PC también son ciclos en los que se está realizando una operación
de lectura. La instrucción subsiguiente no se pudo ejecutar antes de todos modos porque depende de
los datos provenientes de la memoria.
Reducir el número de ciclos de instrucción necesarios para obtener instrucciones
requiere más que un circuito adicional para incrementar la PC. Para acelerar la
búsqueda de instrucciones en un grado significativo, se debe aprovechar la tercera
técnica, la superposición de la ejecución de las instrucciones. Separar los circuitos para
obtener las instrucciones: el puerto de memoria de 8 bits y elMBR
y ordenador personal registros: es más eficaz si la unidad se hace funcionalmente independiente de la ruta de
datos principal. De esta forma, puede obtener el siguiente código de operación u operando por sí solo,
284 EL NIVEL DE MICROARQUITECTURA CAP. 4

quizás incluso funcionando de forma asincrónica con respecto al resto de la CPU y


obteniendo una o más instrucciones por delante.
Una de las fases de ejecución de muchas instrucciones que consume más tiempo es
obtener un desplazamiento de 2 bytes, extenderlo adecuadamente y acumularlo en el H
registrarse en preparación para una adición, por ejemplo, en una sucursal para ordenador personal ± norte
bytes. Una posible solución, hacer que el puerto de memoria tenga 16 bits de ancho, complica
enormemente la operación, porque la memoria tiene en realidad 32 bits de ancho. Los 16 bits
necesarios pueden abarcar los límites de las palabras, de modo que incluso una sola lectura de 32 bits
no necesariamente obtendrá los dos bytes necesarios.
La superposición de la ejecución de instrucciones es, con mucho, el enfoque más interesante
y ofrece la mayor oportunidad para aumentos drásticos en la velocidad. La simple superposición
de obtención y ejecución de instrucciones es sorprendentemente efectiva. Las técnicas más
sofisticadas van mucho más allá, sin embargo, superponen la ejecución de muchas instrucciones.
De hecho, esta idea está en el corazón del diseño informático moderno. Discutiremos algunas de
las técnicas básicas para la ejecución de instrucciones superpuestas a continuación y
motivaremos algunas de las más sofisticadas.
La velocidad es la mitad de la imagen; el costo es la otra mitad. El costo
también se puede medir de diversas formas, pero una definición precisa de costo
es problemática. Algunas medidas son tan simples como contar el número de
componentes. Esto fue particularmente cierto en los días en que los procesadores
se construían con componentes discretos que se compraban y ensamblaban. Hoy
en día, todo el procesador existe en un solo chip, pero los chips más grandes y
complejos son mucho más caros que los más pequeños y simples. Los
componentes individuales, por ejemplo, transistores, puertas o unidades
funcionales, se pueden contar, pero a menudo el recuento no es tan importante
como la cantidad de área requerida en el circuito integrado. Cuanto mayor sea el
área requerida para las funciones incluidas, mayor será el chip. Y el costo de
fabricación del chip crece mucho más rápido que su área. Por esta razón,
Uno de los circuitos más estudiados de la historia es el sumador binario.
Ha habido miles de diseños y los más rápidos son mucho más rápidos que los
más lentos. También son mucho más complejos. El diseñador del sistema
tiene que decidir si la mayor velocidad vale la pena.
Los sumadores no son el único componente con muchas opciones. Casi todos los
componentes del sistema se pueden diseñar para que funcionen más rápido o más lento, con un
diferencial de costos. El desafío para el diseñador es identificar los componentes del sistema que
se deben acelerar para mejorar al máximo el sistema. Curiosamente, muchos componentes
individuales pueden reemplazarse con un componente mucho más rápido con poco o ningún
efecto sobre la velocidad. En las siguientes secciones, veremos algunos de los problemas de
diseño y las correspondientes compensaciones.
Un factor clave para determinar qué tan rápido puede funcionar el reloj es la cantidad de trabajo
que debe realizarse en cada ciclo del reloj. Obviamente, cuanto más trabajo hay que hacer, más largo es
el ciclo del reloj. No es tan simple, por supuesto, porque el hardware es bastante bueno para hacer
cosas en paralelo, por lo que en realidad es la secuencia de operaciones
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 285

que debe ser realizado en serie en un solo ciclo de reloj que determina la
duración del ciclo de reloj.
Un aspecto que se puede controlar es la cantidad de decodificación que se debe realizar. Recuerde, por
ejemplo, que en la figura 4-6 vimos que, si bien cualquiera de los nueve registros podía leerse en la ALU desde
el bus B, solo necesitábamos 4 bits en la palabra de microinstrucción para especificar qué registro debía
seleccionarse. Desafortunadamente, estos ahorros tienen un precio. El circuito de decodificación agrega
retardo en la ruta crítica. Significa que cualquier registro que habilite sus datos en el bus B recibirá ese
comando un poco más tarde y obtendrá sus datos en el bus un poco más tarde. Este efecto se produce en
cascada, con la ALU recibiendo sus entradas un poco más tarde y produciendo sus resultados un poco más
tarde. Finalmente, el resultado está disponible en el bus C para ser escrito en los registros un poco más tarde.
Dado que este retraso a menudo es el factor que determina la duración del ciclo del reloj, esto puede significar
que el reloj no puede funcionar tan rápido y que todo el equipo debe funcionar un poco más lento. Por lo tanto,
existe una compensación entre velocidad y costo. Reducir el almacenamiento de control en 5 bits por palabra
tiene el costo de ralentizar el reloj. El ingeniero de diseño debe tener en cuenta los objetivos del diseño al
decidir cuál es la elección correcta. Para una implementación de alto rendimiento, probablemente no sea una
buena idea usar un decodificador; para uno de bajo costo, podría serlo. probablemente utilizar un
decodificador no sea una buena idea; para uno de bajo costo, podría serlo. probablemente utilizar un
decodificador no sea una buena idea; para uno de bajo costo, podría serlo.

4.4.2 Reducir la longitud de la ruta de ejecución

El Mic-1 fue diseñado para ser moderadamente simple y moderadamente rápido, aunque
hay que reconocer que existe una enorme tensión entre estos dos objetivos. En pocas palabras,
las máquinas simples no son rápidas y las máquinas rápidas no son simples. La CPU Mic-1
también usa una cantidad mínima de hardware: 10 registros, la ALU simple de la figura 3-19
replicada 32 veces, una palanca de cambios, un decodificador, un almacén de control y un poco
de pegamento aquí y allá. Todo el sistema podría construirse con menos de 5000 transistores
más lo que requiera el almacén de control (ROM) y la memoria principal (RAM).
Habiendo visto cómo se puede implementar IJVM de una manera sencilla en
microcódigo con poco hardware, veamos ahora implementaciones alternativas más
rápidas. A continuación, veremos formas de reducir el número de microinstrucciones por
instrucción ISA (es decir, reducir la longitud de la ruta de ejecución). Después de eso,
consideraremos otros enfoques.

Fusionar el bucle de intérprete con el microcódigo

En el Mic-1, el bucle principal consta de una microinstrucción que debe


ejecutarse al comienzo de cada instrucción IJVM. En algunos casos, es posible
superponerlo con la instrucción anterior. De hecho, esto ya se ha logrado
parcialmente. Note que cuandoMain1 se ejecuta, el código de operación a
interpretar ya está en MBR. Está ahí porque fue captado por el bucle principal
anterior (si la instrucción anterior no tenía operandos) o durante la ejecución de la
instrucción anterior.
286 EL NIVEL DE MICROARQUITECTURA CAP. 4

Este concepto de superposición del comienzo de la instrucción se puede llevar más allá
y, de hecho, el bucle principal en algunos casos se puede reducir a nada. Esto puede ocurrir
de la siguiente manera. Considere cada secuencia de microinstrucciones que termina
ramificándose enPrincipal1. En cada uno de estos lugares, la microinstrucción del bucle
principal se puede agregar al final de la secuencia (en lugar de al comienzo de la siguiente
secuencia), con la rama de múltiples vías ahora replicada en muchos lugares (pero siempre
con el mismo conjunto de objetivos) . En algunos casos elMain1 La microinstrucción se
puede fusionar con microinstrucciones anteriores, ya que esas instrucciones no siempre se
utilizan por completo.
En la figura 4-23, se muestra la secuencia dinámica de instrucciones para un MÚSICA POP
instrucción. El bucle principal ocurre antes y después de cada instrucción; en la figura mostramos
solo la ocurrencia después de laMÚSICA POP instrucción. Nótese que la ejecución de
esta instrucción toma cuatro ciclos de reloj: tres para las microinstrucciones específicas para
MÚSICA POP y uno para el bucle principal.

Etiqueta Operaciones Comentarios


pop1 MAR = SP = SP - 1; rd Leer la palabra siguiente en la pila
pop2 Espere a que se lean nuevos TOS de la memoria Copie
pop3 TOS = MDR; ir a Main1 la nueva palabra en TOS
Main1 PC = PC + 1; ir a buscar; goto (MBR) MBR contiene código de operación; obtener el siguiente byte; envío

Figura 4-23. Secuencia de microprograma original para ejecutar MÚSICA POP.

En la figura 4-24, la secuencia se ha reducido a tres instrucciones fusionando las


instrucciones del bucle principal, aprovechando un ciclo de reloj cuando la ALU no se utiliza
en pop2 para guardar un ciclo y de nuevo en Principal1. Asegúrese de tener en cuenta que el
final de esta secuencia se bifurca directamente al código específico para la instrucción
posterior, por lo que solo se requieren tres ciclos en total. Este pequeño truco reduce el
tiempo de ejecución de la siguiente microinstrucción en un ciclo, por ejemplo, una siguiente
AÑADO va de cuatro ciclos a tres. Por lo tanto, equivale a acelerar el reloj de 250 MHz
(microinstrucciones de 4 nseg) a 333 MHz (microinstrucciones de 3 nsec) de forma gratuita.

Etiqueta Operaciones Comentarios


pop1 MAR = SP = SP - 1; rd PC = Leer en la palabra siguiente en la pila MBR contiene

Main1.pop PC + 1; ir a buscar código de operación; buscar el siguiente byte

pop3 TOS = MDR; goto (MBR) Copia la palabra nueva en TOS; envío en código de operación

Figura 4-24. Secuencia de microprogramas mejorada para ejecutar POP.

los MÚSICA POP La instrucción es particularmente adecuada para este tratamiento, porque
tiene un ciclo muerto en el medio que no usa la ALU. El bucle principal, sin embargo, usa la ALU.
Por lo tanto, para reducir la longitud de la instrucción en uno dentro de una instrucción, es
necesario encontrar un ciclo en la instrucción donde la ALU no está en uso. Tal
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 287

Los ciclos muertos no son comunes, pero ocurren, por lo que la fusión Main1 al final de cada secuencia de
microinstrucciones vale la pena hacerlo. Todo lo que cuesta es una pequeña tienda de control. Por lo tanto,
tenemos nuestra primera técnica para reducir la longitud de la ruta:

Fusionar el bucle del intérprete al final de cada secuencia de microcódigo.

Una arquitectura de tres buses

¿Qué más podemos hacer para reducir la longitud de la ruta de ejecución? Otra solución fácil
es tener dos buses de entrada completos a la ALU, un bus A y un bus B, lo que da tres buses en
total. Todos (o al menos la mayoría) de los registros deben tener acceso a ambos buses de
entrada. La ventaja de tener dos buses de entrada es que luego es posible agregar cualquier
registro a cualquier otro registro en un ciclo. Para ver el valor de esta función, consulte
considerar el Implementación de Mic-1 de YO CARGO, mostrado de nuevo en la Fig. 4-25.

Etiqueta Operaciones Comentarios


iload1 H = LV MBR contiene índice; copiar LV a H MAR = dirección de la variable

iload2 MAR = MBRU + H; rd MAR local para empujar los puntos SP a la nueva parte superior de la

iload3 = SP = SP + 1 PC = PC + 1; pila; preparar escribir Inc PC; obtener el siguiente código de

iload4 ir a buscar; wr TOS = operación; escribir la parte superior de la pila Actualizar TOS

iload5 MDR; ir a Main1


Main1 PC = PC + 1; ir a buscar; ir a (MBR) MBR contiene código de operación; obtener el siguiente byte; envío

Figura 4-25. Código Mic-1 para ejecutar YO CARGO.

Vemos aquí que en iload1 LV se copia en H. La razón es que se puede agregar a MBRU en
iload2. En nuestro diseño original de dos buses, no hay forma de agregar dos registros
arbitrarios, por lo que uno de ellos primero debe copiarse a H. Con nuestro nuevo diseño de tres
buses, podemos ahorrar un ciclo, como se muestra en la Fig. 4-26. Hemos agregado el bucle de
intérprete aYO CARGO aquí, pero al hacerlo no aumenta ni disminuye la longitud de la ruta de
ejecución. Aún así, el bus adicional ha reducido el tiempo total de ejecución deYO CARGO de seis
ciclos a cinco ciclos. Ahora tenemos nuestra segunda técnica para reducir la longitud de la ruta:

Pase de un diseño de dos buses a un diseño de tres buses.

Etiqueta Operaciones Comentarios


iload1 MAR = MBRU + LV; rd MAR = dirección de la variable local para empujar los puntos SP a la

iload2 MAR = SP = SP + 1 PC = nueva parte superior de la pila; preparar escribir Inc PC; obtener el

iload3 PC + 1; ir a buscar; wr siguiente código de operación; escribir la parte superior de la pila

iload4 TOS = MDR Actualizar TOS

iload5 PC = PC + 1; ir a buscar; goto (MBR) MBR ya contiene código de operación; buscar byte de índice

Figura 4-26. Código de tres buses para ejecutar YO CARGO.


288 EL NIVEL DE MICROARQUITECTURA CAP. 4

Una unidad de búsqueda de instrucciones

Vale la pena usar las dos técnicas anteriores, pero para obtener una mejora dramática
necesitamos algo mucho más radical. Demos un paso atrás y observemos las partes
comunes de cada instrucción: la búsqueda y decodificación de los campos de la instrucción.
Tenga en cuenta que para cada instrucción pueden ocurrir las siguientes operaciones:

1. La PC pasa a través de la ALU y se incrementa.


2. La PC se utiliza para buscar el siguiente byte en el flujo de instrucciones.

3. Los operandos se leen de la memoria.

4. Los operandos se escriben en la memoria.

5. La ALU realiza un cálculo y los resultados se almacenan nuevamente.

Si una instrucción tiene campos adicionales (para operandos), cada campo debe buscarse
explícitamente, 1 byte a la vez, y ensamblarse antes de que pueda usarse. Obtener y ensamblar
un campo vincula la ALU durante al menos un ciclo por byte para incrementar la PC y luego
nuevamente para ensamblar el índice o desplazamiento resultante. La ALU se usa casi en todos
los ciclos para una variedad de operaciones que tienen que ver con la obtención de la instrucción
y el ensamblaje de los campos dentro de la instrucción, además del "trabajo" real de la
instrucción.
Para superponer el bucle principal, es necesario liberar la ALU de algunas de estas tareas.
Esto se puede hacer introduciendo una segunda ALU, aunque una ALU completa no es necesaria
para gran parte de la actividad. Tenga en cuenta que, en muchos casos, la ALU se usa
simplemente como una ruta para copiar un valor de un registro a otro. Estos ciclos pueden
eliminarse mediante la introducción de rutas de datos adicionales que no pasen por la ALU. Se
puede obtener algún beneficio, por ejemplo, creando una ruta desdeTOS para
MDR, o de MDR para TOS, ya que la palabra superior de la pila se copia con frecuencia
entre esos dos registros.
En el Mic-1, gran parte de la carga se puede eliminar de la ALU creando una unidad
independiente para buscar y procesar las instrucciones. Esta unidad, llamadaSI TU
(Unidad de búsqueda de instrucciones), puede incrementar de forma independiente ordenador personal
y recuperar bytes del flujo de bytes antes de que sean necesarios. Esta unidad requiere solo un
incrementador, un circuito mucho más simple que un sumador completo. Llevando esta idea más allá,
la IFU también puede ensamblar operandos de 8 y 16 bits para que estén listos para su uso inmediato
cuando sea necesario. Hay al menos dos formas de lograr esto:

1. La IFU puede realmente interpretar cada código de operación, determinando cuántos


campos adicionales deben buscarse y ensamblarlos en un registro listo para ser
utilizado por la unidad de ejecución principal.

2. Las IFU pueden aprovechar la naturaleza de flujo de las instrucciones y


poner a disposición en todo momento las siguientes piezas de 8 y 16
bits, tenga o no sentido hacerlo. La unidad de ejecución principal puede
pedir lo que necesite.
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 289

MBR2

Registro de turnos

De memoria

IMAR MBR1

+1

2 bits de orden inferior


Autobús C Autobús B

ordenador personal

+ 1, 2
Escribir PC

Figura 4-27. Una unidad de búsqueda para el Mic-1.

Mostramos los rudimentos del segundo esquema en la figura 4-27. En lugar de un solo 8 bitsMBR,
ahora hay dos MBRs: el de 8 bits MBR1 y el de 16 bits MBR2. La IFU realiza un seguimiento del byte o
bytes más recientes consumidos por la unidad de ejecución principal. También pone a
disposición enMBR1 el siguiente byte, al igual que en el Mic-1, excepto que detecta
automáticamente cuando el MBR1 se lee, busca previamente el siguiente byte y lo carga en MBR1
inmediatamente. Como en el Mic-1, tiene dos interfaces para el bus B:MBR1
y MBR1U. El primero tiene un signo extendido a 32 bits; el último está extendido a cero.
Similar, MBR2 proporciona la misma funcionalidad pero contiene los siguientes 2 bytes.
También tiene dos interfaces para el bus B:MBR2 y MBR2U, activar los valores de 32 bits con
signo extendido y cero, respectivamente.
La IFU es responsable de obtener un flujo de bytes. Para ello, utiliza un puerto de memoria
convencional de 4 bytes, recupera palabras completas de 4 bytes con anticipación y carga los
bytes consecutivos en un registro de desplazamiento que los proporciona uno o dos a la vez, en
el orden en que se obtienen. La función del registro de desplazamiento es mantener una cola de
bytes de la memoria, para alimentarMBR1 y MBR2.
En todo momento, MBR1 contiene el byte más antiguo en el registro de desplazamiento y MBR2
contiene los 2 bytes más antiguos (el byte más antiguo a la izquierda), formando un entero de 16 bits
[consulte la figura 4-19 (b)]. Los 2 bytes enMBR2 puede ser de diferentes palabras de memoria, porque
las instrucciones de IJVM no se alinean con los límites de palabras en la memoria.
Cuando sea MBR1 se lee, el registro de desplazamiento se desplaza 1 byte a la derecha. Cuando seaMBR2
se lee, se desplaza 2 bytes a la derecha. LuegoMBR1 y MBR2 se recargan desde el byte más antiguo
y el par de bytes, respectivamente. Si ahora queda suficiente espacio en el registro de
desplazamiento para otra palabra completa, la IFU inicia un ciclo de memoria para leerla.
290 EL NIVEL DE MICROARQUITECTURA CAP. 4

Suponemos que cuando alguno de los MBR se lee registros, se rellena al inicio del siguiente
ciclo, por lo que se puede leer en ciclos consecutivos.
El diseño de las instrucciones de uso puede ser modelado por un FSM (máquina de estado finito) como
se muestra en la Fig. 4-28. Todos los FSM constan de dos partes:estados, se muestra como círculos, y
transiciones, se muestra como arcos de un estado a otro. Cada estado representa una situación
posible en la que puede estar la FSM. Esta FSM en particular tiene siete estados, correspondientes a los
siete estados del registro de desplazamiento de la figura 4-27. Los siete estados corresponden a
cuántos bytes hay actualmente en el registro de desplazamiento, un número entre
0 y 6, inclusive.
Palabra obtenida

Palabra obtenida
Palabra obtenida

MBR1 MBR1 MBR1 MBR1 MBR1 MBR1


0 1 2 3 4 5 6

MBR2

MBR2 MBR2
MBR2 MBR2

Transiciones
MBR1: ocurre cuando se lee MBR1
MBR2: ocurre cuando se lee MBR2
Palabra recuperada: ocurre cuando se lee una palabra de memoria y se colocan 4 bytes en el registro de desplazamiento

Figura 4-28. Una máquina de estados finitos para implementar las IFU.

Cada arco representa un evento que puede ocurrir. Aquí pueden ocurrir tres eventos
diferentes. El primer evento es de 1 byte que se leeMBR1. Este evento hace que se active el
registro de desplazamiento y se desplace 1 byte del extremo derecho, reduciendo el estado
en 1. El segundo evento es de 2 bytes que se leen. MBR2, lo que reduce el estado en dos.
Ambas transiciones causanMBR1 y MBR2 para ser recargado. Cuando el FSM pasa a los
estados 0, 1 o 2, se inicia una referencia de memoria para buscar una nueva palabra
(asumiendo que la memoria no está ocupada leyendo una palabra). La llegada de la palabra
avanza el estado en 4.
Para funcionar correctamente, la IFU debe bloquear cuando se le pide que haga algo que no puede hacer,
como proporcionar el valor de MBR2 cuando solo hay 1 byte en el registro de desplazamiento y la memoria
todavía está ocupada buscando una nueva palabra. Además, solo puede hacer una cosa a la vez, por lo que los
eventos entrantes deben serializarse. Finalmente, siempre queordenador personal se cambia, las instrucciones de
uso deben actualizarse. Tales detalles lo hacen más complicado de lo que hemos mostrado. Aún así, muchos
dispositivos de hardware se construyen como FSM.
La IFU tiene su propio registro de direcciones de memoria, llamado IMAR, que se utiliza para
direccionar la memoria cuando se debe buscar una nueva palabra. Este registro tiene su propio
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 291

Incrementador dedicado para que la ALU principal no sea necesaria para incrementarla para obtener la
siguiente palabra. La IFU debe monitorear el bus C para que siempreordenador personal está cargado, el nuevo
ordenador personal el valor también se copia en IMAR. Dado que el nuevo valor en ordenador personal puede no estar
en el límite de una palabra, la IFU tiene que buscar la palabra necesaria y ajustar el registro de desplazamiento
de manera apropiada.
Con las IFU, la unidad de ejecución principal escribe en ordenador personal sólo cuando se deba
cambiar la naturaleza secuencial del flujo de bytes de instrucciones. Escribe en una instrucción de
bifurcación exitosa y enINVOCAR EVIRTUAL y VUELVO.
Dado que el microprograma ya no incrementa explícitamente ordenador personal a medida que
se obtienen los códigos de operación, las IFU deben mantener ordenador personal Actual. Lo hace
detectando cuándo se ha consumido un byte del flujo de instrucciones, es decir, cuandoMBR1 o
MBR2 (o las versiones sin firmar) han sido leídas. Asociado conordenador personal es un
incrementador independiente, capaz de incrementarse en 1 o 2, dependiendo de cuántos bytes
se hayan consumido. Por tanto, el PC siempre contiene la dirección del primer byte que no se ha
consumido. Al comienzo de cada instrucción,MBR contiene la dirección del código de operación
para esa instrucción.
Tenga en cuenta que hay dos incrementadores separados y que realizan funciones diferentes.
ordenador personal cuenta bytes y se incrementa en 1 o 2. IMAR cuenta palabras y se incrementa solo en 1
(para 4 bytes nuevos). Igual queMAR, IMAR está conectado al bus de direcciones '' diagonalmente '' con
IMAR bit 0 conectado a la línea de dirección 2, y así sucesivamente, para realizar una conversión implícita
de direcciones de palabras en direcciones de bytes.
Como veremos en breve en detalle, no tener que incrementar ordenador personal en el bucle principal es
una gran ganancia, porque la microinstrucción en la que ordenador personal se incrementa a menudo hace poco
más que incrementar ORDENADOR PERSONAL. Si se puede eliminar esta microinstrucción, se puede reducir la ruta
de ejecución. La compensación aquí es más hardware para una máquina más rápida, por lo que nuestra
tercera técnica para reducir la longitud de la ruta es

Haga que una unidad funcional especializada obtenga las instrucciones de la memoria.

4.4.3 Un diseño con captación previa: el Mic-2

La IFU puede reducir en gran medida la longitud de la ruta de la instrucción promedio. Primero,
elimina el bucle principal por completo, ya que el final de cada instrucción simplemente se bifurca
directamente a la siguiente instrucción. En segundo lugar, evita inmovilizar la ALU aumentando
ORDENADOR PERSONAL. En tercer lugar, reduce la longitud de la ruta cada vez que se calcula un
índice o desplazamiento de 16 bits, porque ensambla el valor de 16 bits y lo suministra
directamente a la ALU como un valor de 32 bits, evitando la necesidad de ensamblar en H. La
Figura 4-29 muestra el Mic-2, una versión mejorada del Mic-1 donde se ha agregado el IFU de la
Figura 4-27. El microcódigo de la máquina mejorada se muestra en la figura 4-30.
Como ejemplo de cómo funciona el Mic-2, mire AÑADO. Obtiene la segunda palabra
de la pila y hace la suma como antes, solo que ahora no tiene que ir a Main1 cuando se
hace para incrementar ordenador personal y enviar a la siguiente microinstrucción.
Cuando la IFU ve esoMBR1 ha sido referenciada en iadd3, su registro de desplazamiento
interno empuja todo hacia la derecha y recarga MBR1 y MBR2. También hace un
292 EL NIVEL DE MICROARQUITECTURA CAP. 4

Memoria
MAR control
registros

Para MDR
y
de
principal
ordenador personal
memoria
Instrucción
recuperar unidad MBR
(SI TU)

MBR2

SP

LV
Señales de control

CPP
Habilitar en el bus B

Escribir bus C para registrarse TOS

OPC
Autobús C

Autobús B
H

Un autobús

6
ALU norte

control ALU
Z

Cambiador

Figura 4-29. La ruta de datos para Mic-2.

transición a un estado uno más bajo que el actual. Si el nuevo estado es 2, la IFU comienza
a buscar una palabra de la memoria. Todo esto está en hardware. El microprograma no
tiene que hacer nada. Es por eso queAÑADO se puede reducir de cuatro microinstrucciones a
tres.
El Mic-2 mejora algunas instrucciones más que otras. LDC W pasa de nueve
microinstrucciones a solo tres, reduciendo su tiempo de ejecución en un factor de tres.
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 293

Por otra parte, INTERCAMBIO va solo de ocho microinstrucciones a seis. Para el rendimiento
general, la ganancia de las instrucciones más comunes es lo que realmente cuenta. Éstos
incluyenYO CARGO (tenía 6 años, ahora 3), AÑADO (era 4, ahora 3), y SI ICMPEQ (tenía 13 años,
ahora 10 para el caso tomado; era 10, ahora 8 para el caso no tomado). Para medir la
mejora, uno tendría que elegir y ejecutar algunos puntos de referencia, pero claramente
hay una gran ganancia aquí.

4.4.4 Un diseño canalizado: el Mic-3

El Mic-2 es claramente una mejora con respecto al Mic-1. Es más rápido y utiliza menos tienda de
control, aunque sin duda el coste de las IFU compensará con creces la propiedad inmobiliaria ganada al
tener una tienda de control más pequeña. Por tanto, es una máquina considerablemente más rápida a
un precio ligeramente superior. Veamos si podemos hacerlo aún más rápido.
¿Qué tal si intentamos reducir el tiempo del ciclo? En gran medida, el tiempo de ciclo está
determinado por la tecnología subyacente. Cuanto más pequeños sean los transistores y más
pequeñas sean las distancias físicas entre ellos, más rápido se puede ejecutar el reloj. Para una
tecnología dada, el tiempo requerido para realizar una operación de ruta de datos completa es
fijo (al menos desde nuestro punto de vista). Sin embargo, tenemos algo de libertad y la
aprovecharemos al máximo en breve.
Nuestra otra opción es introducir más paralelismo en la máquina. Por el
momento, el Mic-2 es muy secuencial. Coloca registros en sus buses, espera a que la
ALU y la palanca de cambios los procesen y luego escribe los resultados en los
registros. Excepto por las IFU, hay poco paralelismo. Agregar paralelismo es una
oportunidad real.
Como se mencionó anteriormente, el ciclo del reloj está limitado por el tiempo necesario
para que las señales se propaguen a través de la ruta de datos. La Figura 4-3 muestra un
desglose del retraso a través de los diversos componentes durante cada ciclo. Hay tres
componentes principales en el ciclo de ruta de datos real:

1. El tiempo para conducir los registros seleccionados a los buses A y B.

2. El tiempo para que la ALU y el cambiador hagan su trabajo.

3. El tiempo para que los resultados vuelvan a los registros y se almacenen.

En la Fig. 4-31, mostramos una nueva arquitectura de tres buses, incluida la IFU, pero
con tres pestillos (registros) adicionales, uno insertado en el medio de cada bus. Los
pestillos están escritos en cada ciclo. En efecto, estos registros dividen la ruta de datos en
distintas partes que ahora pueden operar independientemente unas de otras. Nos
referiremos a esto comoMic-3, o la canalizado modelo.
¿Cómo pueden ayudar estos registros adicionales? Ahora se necesitan tres ciclos de reloj
para usar la ruta de datos: uno para cargar los pestillos A y B, uno para ejecutar la ALU y la
palanca de cambios y cargar el pestillo C, y otro para almacenar el pestillo C nuevamente en los
registros. Seguramente esto es peor de lo que ya teníamos.
294 EL NIVEL DE MICROARQUITECTURA CAP. 4

Etiqueta Operaciones Comentarios


nop1 ir a (MBR) Pasar a la siguiente instrucción
iadd1 MAR = SP = SP - 1; rd H Leer la palabra siguiente en la pila H = parte
iadd2 = TOS superior de la pila
iadd3 MDR = TOS = MDR + H; wr; ir a (MBR1) Agregue las dos palabras superiores; escribir en la nueva parte superior de la pila

isub1 MAR = SP = SP - 1; rd H Leer la palabra siguiente en la pila H = parte


isub2 = TOS superior de la pila
isub3 MDR = TOS = MDR-H; wr; ir a (MBR1) Restar TOS de TOS-1 obtenido
iand1 MAR = SP = SP - 1; rd H Leer la palabra siguiente en la pila H = parte
iand2 = TOS superior de la pila
iand3 MDR = TOS = MDR Y H; wr; ir a (MBR1) Y obtuvo TOS-1 con TOS
ior1 MAR = SP = SP - 1; rd H Leer la palabra siguiente en la pila H = parte
ior2 = TOS superior de la pila
ior3 MDR = TOS = MDR O H; wr; ir a (MBR1) O TOS-1 obtenido con TOS
dup1 MAR = SP = SP + 1 Incremento de SP; copiar a MAR
dup2 MDR = TOS; wr; ir a (MBR1) Escribir una nueva palabra de pila

pop1 MAR = SP = SP - 1; rd Leer la palabra siguiente en la pila


pop2 Esperar la lectura
pop3 TOS = MDR; ir a (MBR1) Copiar palabra nueva a TOS
swap1 MAR = SP - 1; rd Leer la segunda palabra de la pila; establezca MAR en SP
swap2 MAR = SP Prepárese para escribir una nueva segunda palabra
swap3 H = MDR; wr Guardar nuevos TOS; escribir la segunda palabra en la pila
swap4 MDR = TOS Copiar los TOS antiguos a MDR
swap5 MAR = SP - 1; wr TOS = Escriba los TOS antiguos en el segundo lugar de la
swap6 H; ir a (MBR1) pila Actualizar TOS

bipush1 SP = MAR = SP + 1 Configurar MAR para escribir en la nueva parte superior


bipush2 MDR = TOS = MBR1; wr; ir a (MBR1) de la pila Actualizar la pila en TOS y memoria

iload1 MAR = LV + MBR1U; rd Mueva LV + índice a MAR; leer operando


iload2 MAR = SP = SP + 1 Incremento SP; Mueva el nuevo SP a la pila
iload3 TOS = MDR; wr; ir a (MBR1) de actualización MAR en TOS y memoria
istore1 MAR = LV + MBR1U Establezca MAR en LV + índice Copiar
istore2 MDR = TOS; wr TOS para almacenar Decrement SP;
istore3 MAR = SP = SP - 1; rd leer nuevos TOS Esperar lectura
istore4
istore5 TOS = MDR; ir a (MBR1) ir Actualizar TOS
ancho1 a (MBR1 O 0x100) La siguiente dirección es 0x100 o con código de operación

ancho iload1 MAR = LV + MBR2U; rd; ir a iload2 Idéntica a iload1 pero usando un índice de 2 bytes

istore1 ancho MAR = LV + MBR2U; ir a istore2 MAR = Idéntica a istore1 pero usando un índice de 2 bytes Igual

ldc w1 CPP + MBR2U; rd; ir a iload2 que iload1 ancho pero indexando CPP

iinc1 MAR = LV + MBR1U; rd Establezca MAR en LV + índice para lectura


iinc2 H = MBR1 Establezca H en constante
iinc3 MDR = MDR + H; wr; ir a (MBR1) Incremento por constante y actualización
goto1 H = PC - 1 Copiar PC a H
goto2 PC = H + MBR2 Agregar compensación y actualizar PC
goto3 Tengo que esperar a que IFU recupere el nuevo código de

goto4 ir a (MBR1) operación. Desvío a la siguiente instrucción.

iflt1 MAR = SP = SP - 1; rd Leer la palabra siguiente en la pila Guardar


iflt2 OPC = TOS TOS en OPC temporalmente Poner la nueva
iflt3 TOS = MDR parte superior de la pila en la rama TOS en N
iflt4 N = OPC; si (N) goto T; si no, vaya a F bit

Figura 4-30. El microprograma del Mic-2 (parte 1 de 2).


SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 295

Etiqueta Operaciones Comentarios


ifeq1 MAR = SP = SP - 1; rd Leer en la siguiente palabra de la pila
ifeq2 OPC = TOS Guardar TOS en OPC temporalmente Poner
ifeq3 TOS = MDR la nueva parte superior de la pila en la rama
ifeq4 Z = OPC; si (Z) pasa a T; si no, vaya a F TOS en el bit Z

si icmpeq1 MAR = SP = SP - 1; rd Leer en la palabra siguiente de la pila Establecer MAR


si icmpeq2 MAR = SP = SP- 1 H = para leer en la nueva parte superior de la pila Copiar
si icmpeq3 MDR; rd la segunda palabra de la pila en H Guardar TOS en
si icmpeq4 OPC = TOS OPC temporalmente Poner la nueva parte superior de
si icmpeq5 TOS = MDR la pila en TOS
si icmpeq6 Z = H - OPC; si (Z) pasa a T; else goto F Si las 2 palabras superiores son iguales,
T goto T, else goto FH = PC- 1; goto goto2 Igual que goto1
F H = MBR2 Toque bytes en MBR2 para descartar
F2 ir a (MBR1)
invokevirtual1 MAR = CPP + MBR2U; rd Ponga la dirección del puntero del método en
invokevirtual2 OPC = PC MAR Save Return PC en OPC
invokevirtual3 PC = MDR Configure la PC en el primer byte del código de
invokevirtual4 TOS = SP - MBR2U método. TOS = dirección de OBJREF- 1 TOS =
invokevirtual5 TOS = MAR = H = TOS + 1 dirección de OBJREF Sobrescribir OBJREF con
invokevirtual6 MDR = SP + MBR2U + 1; wr puntero de enlace Establezca SP, MAR en la
invokevirtual7 MAR = SP = MDR ubicación para guardar la PC antigua Prepárese para
invokevirtual8 MDR = OPC; wr guardar la PC antigua
invokevirtual9 MAR = SP = SP + 1 Inc. SP para apuntar a la ubicación para mantener LV antiguo

invokevirtual10 MDR = LV; wr Guardar LV antiguo

invokevirtual11 LV = TOS; ir a (MBR1) Establezca LV para que apunte al parámetro cero.

ireturn1 MAR = SP = LV; rd Restablecer SP, MAR para leer Link ptr
ireturn2 Esperar link ptr
ireturn3 LV = MAR = MDR; rd Configure LV, MAR para vincular ptr; leer PC antiguo
ireturn4 MAR = LV + 1 Configure MAR para que apunte a LV antiguo; leer antiguo
ireturn5 PC = MDR; rd LV Restore PC
ireturn6 MAR = SP
ireturn7 LV = MDR Restaurar LV
ireturn8 MDR = TOS; wr; ir a (MBR1) Guarde el valor de retorno en la parte superior original de la pila

Figura 4-30. El microprograma del Mic-2 (parte 2 de 2).

Estamos locos (Insinuación: No.) El objetivo de insertar los pestillos es doble:

1. Podemos acelerar el reloj porque el retraso máximo ahora es más corto.

2. Podemos utilizar todas las partes de la ruta de datos durante cada ciclo.

Al dividir la ruta de datos en tres partes, reducimos el retraso máximo con el resultado
de que la frecuencia del reloj puede ser mayor. Supongamos que al dividir el ciclo de la ruta
de datos en tres intervalos de tiempo, cada uno es aproximadamente 1/3 del original, por
lo que podemos triplicar la velocidad del reloj. (Esto no es totalmente realista, ya que
también hemos agregado dos registros más en la ruta de datos, pero como primera
aproximación servirá).
Porque hemos asumido que todas las lecturas y escrituras de memoria pueden
satisfacerse con la caché de nivel 1, y esta caché está hecha del mismo material que el
296 EL NIVEL DE MICROARQUITECTURA CAP. 4

Memoria
MAR control
registros

Para MDR
y
de
principal
ordenador personal
memoria
Instrucción
recuperar unidad MBR1
(SI TU)

MBR2

SP

LV
Señales de control

CPP
Habilitar en el bus B

Escribir bus C para registrarse TOS

OPC
Autobús C

Autobús B
H

Un autobús

Pestillo C Un pestillo Pestillo B

6
ALU norte

control ALU
Z

Cambiador

Figura 4-31. La ruta de datos de tres buses utilizada en el Mic-3.

registros, continuaremos asumiendo que una operación de memoria toma un ciclo. En la


práctica, sin embargo, esto puede no ser tan fácil de lograr.
El segundo punto trata del rendimiento en lugar de la velocidad de una
instrucción individual. En el Mic-2, durante la primera y tercera parte de cada ciclo
de reloj, la ALU está inactiva. Al dividir la ruta de datos en tres partes, podremos
usar la ALU en cada ciclo, obteniendo tres veces más trabajo de la máquina.
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 297

Veamos ahora cómo funciona la ruta de datos del Mic-3. Antes de comenzar,
necesitamos una notación para lidiar con los pestillos. La obvia es llamarlosA, B, y C
y tratar les gustan los registros, teniendo en cuenta las limitaciones de la ruta de datos. La figura
ure 4-32 muestra una secuencia de código de ejemplo, la implementación deINTERCAMBIO Para el
Mic-2.

Etiqueta Operaciones Comentarios


swap1 MAR = SP - 1; rd Leer la segunda palabra de la pila; establezca MAR en SP

swap2 MAR = SP Prepárese para escribir una nueva segunda palabra

swap3 H = MDR; wr Guardar nuevos TOS; escribir la segunda palabra en la pila

swap4 MDR = TOS Copiar los TOS antiguos a MDR

swap5 MAR = SP - 1; wr TOS = Escriba los TOS antiguos en el segundo lugar de la pila

swap6 H; ir a (MBR1) Actualizar TOS

Figura 4-32. El código Mic-2 para INTERCAMBIO.

Ahora Reimplementemos esta secuencia en el Mic-3. Recuerda que los datos


ruta ahora requiere tres ciclos para operar: uno para cargar A y B, uno para realizar la
operación y cargar C, y otro para volver a escribir los resultados en los registros.
Llamaremos a cada una de estas piezas unmicropaso.
La implementación de INTERCAMBIO para el Mic-3 se muestra en la Fig. 4-33. En el
ciclo 1, comenzamos enswap1 copiando SP para B. No importa lo que entre A porque
restar 1 de B ENA se niega (ver Fig. 4-2). Para simplificar, no mostraremos asignaciones
que no se utilicen. En el ciclo 2 hacemos la resta. En el ciclo 3, el resultado se almacena
enMAR y la operación de lectura se inicia al final del ciclo 3 (después
MAR ha sido almacenado). Dado que las lecturas de memoria ahora toman un ciclo,
este no se completará hasta el final del ciclo 4, indicado al mostrar la asignación aMDR
en el ciclo 4. El valor en MDR no se puede leer antes del ciclo 5.
Ahora volvamos al ciclo 2. Ahora podemos empezar a separarnos swap2 en micropasos
e iniciarlos también. En el ciclo 2, podemos copiarSP para B, luego ejecútelo a través de la
ALU en el ciclo 3 y finalmente guárdelo en MAR en el ciclo 4. Hasta ahora, todo va bien. Debe
quedar claro que si podemos seguir a este ritmo, iniciando una nueva microinstrucción en
cada ciclo, hemos triplicado la velocidad de la máquina. Esta ganancia proviene del hecho
de que podemos emitir una nueva microinstrucción en cada ciclo de reloj, y el Mic-3 tiene
tres veces más ciclos de reloj por segundo que el Mic-2. De hecho, hemos construido una
CPU canalizada.
Desafortunadamente, encontramos un inconveniente en el ciclo 3. Nos gustaría comenzar a trabajar en
swap3, pero lo primero que hace es correr MDR a través de la ALU, y MDR no estará disponible
en la memoria hasta el inicio del ciclo 5. La situación en la que un micropaso no puede
comenzar porque está esperando un resultado que un micropaso anterior aún no ha
producido se denomina verdadera dependencia o un Dependencia RAW. Las
dependencias a menudo se denominan riesgos. RAW significa Read After Write e indica que
un micropaso quiere leer un registro que aún no se ha escrito. Lo único sensato que se
puede hacer aquí es retrasar el inicio deswap3 Hasta que MDR está disponible, en el ciclo 5.
298 LOS NIVEL DE MICROARQUITECTURA CAP. 4

Swap1 Swap2 Swap3 Swap4 Swap5 Swap6


Cy MAR = SP-1; rd MAR = SP H = MDR; wr MDR = TOS MAR = SP-1; wr TOS = H; ir a (MBR1)
1 B = SP
2 C = B-1 B = SP
3 MAR = C; rd C=B
4 MDR = Mem MAR = C
5 B = MDR
6 C=B B = TOS
7 H = C; wr C=B B = SP
8 Mem = MDR MDR = C C = B-1 B=H
9 MAR = C; wr C=B
10 Mem = MDR TOS = C
11 ir a (MBR1)

Figura 4-33. La implementación de INTERCAMBIO en el Mic-3.

Detenerse para esperar un valor necesario se llama estancamiento. Después de eso, podemos
continuar iniciando microinstrucciones en cada ciclo ya que no hay más dependencias, aunque
swap6 apenas lo hace, ya que se lee H en el ciclo posterior swap3 lo escribe. Si
swap5 había intentado leer H se habría estancado durante un ciclo.
Aunque el programa Mic-3 toma más ciclos que el programa Mic-2, aún se ejecuta más
rápido. Si llamamos tiempo de ciclo Mic-3ΔT nsec, entonces el Mic-3 requiere 11ΔT
nsec para ejecutar INTERCAMBIO. Por el contrario, el Mic-2 tarda 6 ciclos a 3ΔT cada uno, para un total de 18ΔT. La
canalización ha hecho que la máquina sea más rápida, aunque tuvimos que detenernos una vez para evitar una
dependencia.
La canalización es una técnica clave en todas las CPU modernas, por lo que es importante
comprenderla bien. En la figura 4-34 vemos la ruta de datos de la figura 4-31 ilustrada gráficamente
como una tubería. La primera columna representa lo que está sucediendo durante el ciclo 1, la segunda
columna representa el ciclo 2 y así sucesivamente (asumiendo que no hay paradas). La región
sombreada en el ciclo 1 para la instrucción 1 indica que la IFU está ocupada recuperando la instrucción
1. Un tic del reloj más tarde, durante el ciclo 2, los registros requeridos por la instrucción 1 se cargan en
los pestillos A y B mientras que al mismo tiempo en la IFU está ocupado recuperando la instrucción 2,
nuevamente mostrada por los dos rectángulos sombreados en el ciclo 2.
Durante el ciclo 3, la instrucción 1 utiliza la ALU y la palanca de cambios para
realizar su operación, los pestillos A y B se cargan para la instrucción 2 y se recupera la
instrucción 3. Finalmente, durante el ciclo 4, se están trabajando cuatro instrucciones
al mismo tiempo. Se almacenan los resultados de la instrucción 1, se realiza el trabajo
de ALU para la instrucción 2, se cargan los pestillos A y B de la instrucción 3 y se
recupera la instrucción 4.
Si hubiéramos mostrado el ciclo 5 y los ciclos posteriores, el patrón habría sido el mismo que en el
ciclo 4: las cuatro partes de la ruta de datos que pueden ejecutarse de forma independiente
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 299

SI TU SI TU SI TU SI TU
Reg Reg Reg Reg

C A B C A B C A B C A B
1
ALU ALU ALU ALU
Cambiador Cambiador Cambiador Cambiador

SI TU SI TU SI TU SI TU
Reg Reg Reg Reg

C A B C A B C A B C A B
2
ALU ALU ALU ALU
Cambiador Cambiador Cambiador Cambiador

SI TU SI TU SI TU SI TU
Reg Reg Reg Reg

C A B C A B C A B C A B
3
ALU ALU ALU ALU
Instrucción

Cambiador Cambiador Cambiador Cambiador

SI TU SI TU SI TU SI TU
Reg Reg Reg Reg

C A B C A B C A B C A B
4
ALU ALU ALU ALU
Cambiador Cambiador Cambiador Cambiador

Ciclo 1 Ciclo 2 Ciclo 3 Ciclo 4

Tiempo

Figura 4-34. Ilustración gráfica de cómo funciona una tubería.

sería haciéndolo. Este diseño representa una tubería de cuatro etapas, con etapas para
obtención de estructuras, acceso a operandos, operaciones ALU y escritura en los registros.
Es similar a la tubería de la figura 2-4 (a), excepto sin la etapa de decodificación. El punto
importante a retomar aquí es que, aunque una sola instrucción requiere cuatro ciclos de
reloj para ejecutarse, en cada ciclo de reloj se inicia una nueva instrucción y se completa
una instrucción anterior.
Otra forma de ver la figura 4-34 es seguir cada instrucción horizontalmente a lo largo de la
página. Para la instrucción 1, en el ciclo 1, las IFU están trabajando en ello. En el ciclo 2, sus
registros se colocan en los buses A y B. En el ciclo 3, la ALU y la palanca de cambios están
trabajando para ello. Finalmente, en el ciclo 4, sus resultados se almacenan nuevamente en el
300 EL NIVEL DE MICROARQUITECTURA CAP. 4

registros. Lo que hay que tener en cuenta aquí es que hay cuatro secciones del hardware
disponibles y, durante cada ciclo, una instrucción dada usa solo una de ellas, liberando las otras
secciones para diferentes instrucciones.
Una analogía útil de nuestro diseño de tuberías es una línea de montaje en una fábrica que
ensambla automóviles. Para abstraer lo esencial de este modelo, imagine que suena un gran
gong cada minuto, momento en el que todos los automóviles se mueven una estación más
adelante en la línea. En cada estación, los trabajadores realizan alguna operación en el automóvil
que se encuentra frente a ellos, como agregar el volante o instalar los frenos. A cada latido del
gong (1 ciclo), se inyecta un nuevo automóvil al inicio de la línea de montaje y un automóvil
terminado arranca al final. Por lo tanto, aunque puede llevar cientos de ciclos completar un
automóvil, en cada ciclo se completa un automóvil completo. La fábrica puede producir un
automóvil por minuto, independientemente del tiempo que lleve realmente ensamblar un
automóvil. Este es el poder de la canalización, y se aplica tanto a las CPU como a las fábricas de
automóviles.

4.4.5 Una tubería de siete etapas: el Mic-4

Un punto que hemos pasado por alto es que cada microinstrucción selecciona su propio sucesor.
La mayoría de ellos simplemente selecciona el siguiente en la secuencia actual, pero el último, como
swap6, a menudo hace una bifurcación de múltiples vías, lo que atasca la tubería, ya que es imposible
continuar con la búsqueda previa después de que sea posible. Necesitamos una mejor forma de
abordar este punto.
Nuestra última microarquitectura es el Mic-4. Sus partes principales se muestran en la figura 4-35,
pero se ha suprimido una cantidad sustancial de detalles para mayor claridad. Al igual que el Mic-3,
tiene un IFU que recupera palabras de la memoria y mantiene las distintasMBRs. La IFU también
alimenta el flujo de bytes entrante a un nuevo componente, elunidad de decodificación. Esta
unidad tiene una ROM interna indexada por código de operación IJVM. Cada entrada (fila) contiene dos
partes: la longitud de esa instrucción IJVM y un índice en otra ROM, la ROM de microoperación. La
longitud de la instrucción IJVM se utiliza para permitir que la unidad de decodificación analice el flujo de
bytes entrante en instrucciones, por lo que siempre sabe qué bytes son códigos de operación y cuáles
son operandos. Si la longitud de la instrucción actual es de 1 byte (p. Ej.,MÚSICA POP), entonces la unidad
de decodificación sabe que el siguiente byte es un código de operación. Sin embargo, si la longitud de la
instrucción actual es de 2 bytes, la unidad de decodificación sabe que el siguiente byte es un operando,
seguido inmediatamente por otro código de operación. Cuando elAMPLIO se ve el prefijo, el siguiente
byte se transforma en un código de operación ancho especial, por ejemplo, AMPLIO + ILOAD se convierte
en AMPLIA ILOAD.
La unidad de decodificación envía el índice a la ROM de microoperación que encontró
en su tabla al siguiente componente, el unidad de cola. Esta unidad contiene algo de lógica
más dos tablas internas, una en ROM y otra en RAM. La ROM contiene el microprograma, y
cada instrucción IJVM tiene un número de entradas consecutivas, llamadas
microoperaciones. Las entradas deben estar en orden, así que trucos como
ancho iload2 ramificándose a iload2 en Mic-2 no están permitidos. Cada secuencia de IJVM
debe estar completa, duplicando secuencias en algunos casos.
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 301

Final
Microoperación
IJVM Ir a
ROM
largo
índice Unidad de cola
1 3
2
De ROM de microoperación
memoria AÑADO
Instrucción ISUB
recuperar unidad YO CARGO

Unidad de decodificación
IFLT

Cola
de pendiente
microoperaciones

Hacia / desde

memoria
Conduce la etapa 4 ALU C METRO AB MIR1
4
7 Registros
Conduce la etapa 5 ALU C MAB MIR2

Conduce la etapa 6 ALU C MAB MIR3

6
Conduce la etapa 7 ALU C METRO AB MIR4

C A B

ALU 5

Cambiador

Figura 4-35. Los principales componentes de el Mic-4.

Las microoperaciones son similares a las microinstrucciones de la figura 4-5 excepto


que el SIGUIENTE DIRECCION y MERMELADA los campos están ausentes y se necesita un nuevo
campo codificado para especificar la entrada del bus A. También se proporcionan dos
nuevos bits: Final y Goto. El bit Final se establece en la última microoperación de cada
secuencia de microoperación IJVM para marcarlo. El bit Goto está configurado para marcar
las microoperaciones que son microramaes condicionales. Tienen un formato diferente al
de las microoperaciones normales, que consiste enMERMELADA bits y un índice en la ROM de
microoperación. Microinstrucciones que anteriormente hicieron algo con la ruta de datos y
también realizaron una microrama condicional (p. Ej.,iflt4) ahora hay que dividirlo en dos
microoperaciones.
La unidad de cola funciona de la siguiente manera. Recibe un índice ROM de
microoperación de la unidad de decodificación. Luego busca la microoperación y la
copia en una cola interna. Luego copia la siguiente microoperación también en la cola,
y también la siguiente. Continúa hasta que golpea a uno con el bit final activado.
También copia ese y se detiene. Suponiendo que no ha afectado a una microoperación
302 EL NIVEL DE MICROARQUITECTURA CAP. 4

con el bit Goto activado y todavía queda mucho espacio en la cola, la unidad de cola envía
una señal de confirmación de regreso a la unidad de decodificación. Cuando la unidad de
decodificación ve el acuse de recibo, envía el índice de la siguiente instrucción IJVM a la
unidad de cola.
De esta manera, la secuencia de instrucciones IJVM en la memoria se convierte
finalmente en una secuencia de microoperaciones en una cola. Estas microoperaciones
alimentanMIRs, que envían las señales para controlar la ruta de datos. Sin embargo, otro
factor que debemos considerar ahora es que los campos de cada microoperación no están
activos al mismo tiempo. losA y B Los campos estn activos durante el primer ciclo, el ALU
El campo está activo durante el segundo ciclo, el C El campo está activo durante el tercer ciclo y
cualquier operación de memoria tiene lugar en el cuarto ciclo.
Para que esto funcione correctamente, hemos introducido cuatro MIRs en la Fig. 4-35.
Al comienzo de cada ciclo de reloj (elΔw tiempo en la figura 4-3), MIR3 se copia a MIR4, MIR2 se
copia a MIR3, MIR1 se copia a MIR2, y MIR1 se carga con una nueva microoperación de la cola
de microoperaciones. Entonces cadaMIR emite sus señales de control, pero solo se utilizan
algunas de ellas. losA y B campos de MIR1 se utilizan para seleccionar los registros que
controlan los pestillos A y B, pero los ALU campo en MIR1
no se utiliza y no está conectado a nada más en la ruta de datos.
Un ciclo de reloj más tarde, esta microoperación ha pasado a MIR2 y los registros que
seleccionó ahora están sentados de forma segura en los pestillos A y B esperando que
lleguen las aventuras. SuALU El campo ahora se usa para impulsar la ALU. En el siguiente
ciclo, esC campo volverá a escribir los resultados en los registros. Después de eso, pasará a
MIR4 e iniciar cualquier operación de memoria necesaria utilizando el ahora cargado MAR
(y MDR, para escribir).
Un último aspecto del Mic-4 necesita un poco de discusión ahora: las micro-ramas. Algunas
instrucciones de IJVM, comoIFLT, necesidad de bifurcar condicionalmente en función de, digamos,
el norte poco. Cuando ocurre una micro-sucursal, la tubería no puede continuar. Para lidiar con
eso, hemos agregado el bit Goto a la microoperación. Cuando la unidad de cola activa una
microoperación con este bit establecido mientras lo copia a la cola, se da cuenta de que hay un
problema por delante y se abstiene de enviar un acuse de recibo a la unidad de decodificación.
Como resultado, la máquina se detendrá en este punto hasta que se haya resuelto la microrama.

Posiblemente, algunas instrucciones IJVM más allá de la rama ya se han introducido en


la unidad de decodificación (pero no en la unidad de cola), ya que no envía una señal de
reconocimiento (es decir, continuar) cuando llega a una microoperación con el bit Goto.
sobre. Se necesitan hardware y mecanismos especiales para limpiar el desorden y volver al
camino, pero están más allá del alcance de este libro. Cuando Edsger Dijkstra escribió su
famosa carta "Declaración de GOTO considerada perjudicial" (Dijkstra, 1968a), no tenía idea
de la razón que tenía.
Hemos recorrido un largo camino desde el Mic-1. El Mic-1 era una pieza de hardware
muy simple, con casi todo el control realizado en software. El Mic-4 es un diseño altamente
canalizado, con siete etapas y hardware mucho más complejo. La tubería se muestra
esquemáticamente en la figura 4-36, con los números encerrados en un círculo
SEGUNDO. 4.4 DISEÑO DEL NIVEL DE MICROARQUITECTURA 303

componentes en la figura 4-35. El Mic-4 obtiene automáticamente un flujo de bytes de la memoria, los
decodifica en instrucciones IJVM, los convierte en una secuencia de microoperaciones usando una ROM
y los pone en cola para usarlos según sea necesario. Las primeras tres etapas de la canalización se
pueden vincular al reloj de la ruta de datos si se desea, pero no siempre habrá trabajo por hacer. Por
ejemplo, la IFU ciertamente no puede alimentar un nuevo código de operación IJVM a la unidad de
decodificación en cada ciclo de reloj porque las instrucciones IJVM
tardaría varios ciclos en ejecutarse y la cola desbordamiento rápido.

1 2 3 4 5 6 7

Escribir
SI TU Descifrador Cola Operandos Ejecutiva emory
METRO
espalda

Figura 4-36. El oleoducto Mic-4.

En cada ciclo de reloj, el MIRs se desplazan hacia adelante y la microoperación en la parte


inferior de la cola se copia en MIR1 para iniciar su ejecución. Las señales de control de los cuatro
MIRLos s luego se extienden a lo largo de la ruta de datos, lo que provoca que se produzcan
acciones. CadaMIR controla una parte diferente de la ruta de datos y, por lo tanto, diferentes
micropasos.
En este diseño, tenemos una CPU profundamente interconectada, lo que permite que los pasos
individuales sean muy cortos y, por lo tanto, la frecuencia del reloj sea alta. Muchas CPU están diseñadas
esencialmente de esta manera, especialmente aquellas que tienen que implementar un conjunto de
instrucciones más antiguo (CISC). Por ejemplo, la implementación del Core i7 es conceptualmente similar al
Mic-4 en algunos aspectos, como veremos más adelante en este capítulo.

4.5 MEJORA DEL RENDIMIENTO

Todos los fabricantes de computadoras quieren que sus sistemas funcionen lo más rápido posible.
En esta sección, veremos una serie de técnicas avanzadas que se están investigando actualmente para
mejorar el rendimiento del sistema (principalmente CPU y memoria). Debido a la naturaleza altamente
competitiva de la industria informática, el desfase entre las nuevas ideas de investigación que pueden
hacer que una computadora sea más rápida y su incorporación a los productos es sorprendentemente
corto. En consecuencia, la mayoría de las ideas que discutiremos ya están en uso en una amplia
variedad de productos existentes.
Las ideas que se discutirán se dividen aproximadamente en dos categorías: mejoras de
implementación y mejoras arquitectónicas. Las mejoras de implementación son formas de
construir una nueva CPU o memoria para que el sistema funcione más rápido sin cambiar
la arquitectura. Modificar la implementación sin cambiar la arquitectura significa que los
programas antiguos se ejecutarán en la nueva máquina, un importante punto de venta.
Una forma de mejorar la implementación es utilizar un reloj más rápido, pero esto es
304 EL NIVEL DE MICROARQUITECTURA CAP. 4

no es la única forma. Las ganancias de rendimiento del 80386 al 80486, Pentium y


diseños posteriores como el Core i7 se deben a mejores implementaciones, ya que la
arquitectura se ha mantenido esencialmente igual en todos ellos.
Algunos tipos de mejoras solo se pueden realizar cambiando la arquitectura. A veces, estos
cambios son incrementales, como agregar nuevas instrucciones o registros, para que los
programas antiguos continúen ejecutándose en los nuevos modelos. En este caso, para obtener
el máximo rendimiento, el software debe cambiarse o al menos volver a compilarse con un nuevo
compilador que aproveche las nuevas funciones.
Sin embargo, una vez en unas pocas décadas, los diseñadores se dan cuenta de
que la arquitectura antigua ha dejado de ser útil y que la única forma de progresar es
empezar de nuevo. La revolución RISC en la década de 1980 fue uno de esos avances;
otro está en el aire ahora. Veremos un ejemplo (el Intel IA-64) en el Cap. 5.
En el resto de esta sección, veremos cuatro técnicas diferentes para mejorar el
rendimiento de la CPU. Comenzaremos con tres mejoras de implementación bien
establecidas y luego pasaremos a una que necesite un poco de soporte arquitectónico para
funcionar mejor. Estas técnicas son la memoria caché, la predicción de ramas, la ejecución
desordenada con cambio de nombre de registros y la ejecución especulativa.

4.5.1 Memoria caché

Uno de los aspectos más desafiantes del diseño de computadoras a lo largo de la historia ha
sido proporcionar un sistema de memoria capaz de proporcionar operandos al procesador a la
velocidad con la que puede procesarlos. La reciente alta tasa de crecimiento en la velocidad del
procesador no ha ido acompañada de una aceleración correspondiente en las memorias. En
relación con las CPU, los recuerdos se han vuelto más lentos durante décadas. Dada la enorme
importancia de la memoria primaria, esta situación ha limitado en gran medida el desarrollo de
sistemas de alto rendimiento y ha estimulado la investigación sobre formas de sortear el
problema de las velocidades de la memoria que son mucho más lentas que las velocidades de la
CPU y, en términos relativos, empeoran cada año. .
Los procesadores modernos imponen demandas abrumadoras a un sistema de memoria, en
términos de latencia (el retraso en el suministro de un operando) y ancho de banda (la cantidad de
datos suministrados por unidad de tiempo). Desafortunadamente, estos dos aspectos de un sistema de
memoria están en gran parte reñidos. Muchas técnicas para aumentar el ancho de banda lo hacen solo
aumentando la latencia. Por ejemplo, las técnicas de canalización utilizadas en el Mic-3 se pueden
aplicar a un sistema de memoria, con múltiples solicitudes de memoria superpuestas manejadas de
manera eficiente. Desafortunadamente, al igual que con el Mic-3, esto da como resultado una mayor
latencia para las operaciones de memoria individuales. A medida que las velocidades de reloj del
procesador se vuelven más rápidas, se vuelve cada vez más difícil proporcionar un sistema de memoria
capaz de suministrar operandos en uno o dos ciclos de reloj.
Una forma de atacar este problema es proporcionando cachés. Como vimos en la Sec.
2.2.5, un caché contiene las palabras de memoria utilizadas más recientemente en una memoria pequeña y
rápida, lo que acelera el acceso a ellas. Si hay un porcentaje suficientemente grande de las palabras de
memoria necesarias en la memoria caché, la latencia de memoria efectiva se puede reducir enormemente.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 305

Una de las formas más efectivas de mejorar tanto el ancho de banda como la latencia es usar
múltiples cachés. Una técnica básica que funciona de manera muy eficaz es introducir una caché
separada para instrucciones y datos. Hay varios beneficios de tener cachés separados para
instrucciones y datos, a menudo llamadoscaché dividido. Primero, las operaciones de memoria se
pueden iniciar de forma independiente en cada caché, duplicando efectivamente el ancho de banda del
sistema de memoria. Es por eso que tiene sentido proporcionar dos puertos de memoria separados,
como hicimos en el Mic-1: cada puerto tiene su propia caché. Tenga en cuenta que cada caché tiene
acceso independiente a la memoria principal.
Hoy en día, muchos sistemas de memoria son más complicados que esto, y una caché
adicional, llamada caché de nivel 2, puede residir entre las cachés de instrucciones y datos y la
memoria principal. De hecho, como se requieren sistemas de memoria más sofisticados, puede
haber tres o más niveles de caché. En la figura 4-37 vemos un sistema con tres niveles de caché.
El chip de la CPU en sí contiene una pequeña caché de instrucciones y una pequeña caché de
datos, normalmente de 16 KB a 64 KB. Luego está el caché de nivel 2, que no está en el chip de la
CPU pero puede estar incluido en el paquete de la CPU, junto al chip de la CPU y conectado a él
mediante una ruta de alta velocidad. Esta caché generalmente está unificada y contiene una
combinación de datos e instrucciones. Un tamaño típico para la caché L2 es de 512 KB a 1 MB. La
caché de tercer nivel está en la placa del procesador y consta de unos pocos megabytes de SRAM,
que es mucho más rápida que la memoria DRAM principal.
Los cachés son generalmente inclusivos, con el contenido de la caché de nivel 1 está en
la caché de caché de y el completo completo del nivel la caché 2 está en el nivel 3
nivel 2.

UPC
paquete
Chip de la CPU
Unificado
L2 Unificado
cache Caché L3
L1-I L1-D
Principal

memoria
(DRACMA)
Procesador
tablero
Teclado Gráficos Disco
controlador controlador controlador

Dividir cachés de datos e instrucciones L1 Caché a nivel de placa (SRAM)

Figura 4-37. Un sistema con tres niveles de caché.

Para lograr su objetivo, las cachés dependen de dos tipos de localidad de dirección. Localidad
espacial es la observación de que es probable que se acceda a ubicaciones de memoria con direcciones
numéricamente similares a una ubicación de memoria a la que se ha accedido recientemente
306 EL NIVEL DE MICROARQUITECTURA CAP. 4

futuro. Los cachés intentan explotar esta propiedad al traer más datos de los que se han solicitado, con
la expectativa de que se puedan anticipar solicitudes futuras.Localidad temporal se produce cuando se
vuelve a acceder a ubicaciones de memoria a las que se ha accedido recientemente. Esto puede ocurrir,
por ejemplo, en ubicaciones de memoria cerca de la parte superior de la pila o instrucciones dentro de
un bucle. La localidad temporal se explota en los diseños de caché principalmente mediante la elección
de qué descartar en una falla de caché. Muchos algoritmos de reemplazo de caché explotan la localidad
temporal descartando aquellas entradas a las que no se ha accedido recientemente.

Todas las cachés utilizan el siguiente modelo. La memoria principal se divide en bloques de
tamaño fijo llamadoslíneas de caché. Una línea de caché generalmente consta de 4 a 64 bytes
consecutivos. Las líneas se numeran consecutivamente comenzando en 0, por lo que con un tamaño de
línea de 32 bytes, la línea 0 es de 0 a 31 bytes, la línea 1 es de 32 a 63, y así sucesivamente. En cualquier
momento, algunas líneas están en la caché. Cuando se hace referencia a la memoria, el circuito del
controlador de caché comprueba si la palabra a la que se hace referencia se encuentra actualmente en
la caché. Si es así, se puede usar el valor allí, guardando un viaje en la memoria principal. Si la palabra
no está allí, se elimina alguna entrada de línea del caché y la línea necesaria se recupera de la memoria
o del caché más distante para reemplazarla. Existen muchas variaciones de este esquema, pero en
todas ellas la idea es mantener las líneas más utilizadas en la caché tanto como sea posible, para
maximizar el número de referencias de memoria satisfechas fuera de la caché.

Cachés asignados directamente

La caché más simple se conoce como caché de asignación directa. En la figura 4-38 (a) se
muestra un ejemplo de caché de asignación directa de un solo nivel. Este ejemplo de caché contiene
2048 entradas. Cada entrada (fila) en la caché puede contener exactamente una línea de caché de la
memoria principal. Con un tamaño de línea de caché de 32 bytes (para este ejemplo), el caché puede
contener 2048 entradas de 32 bytes o 64 KB en total. Cada entrada de caché consta de tres partes:

1. El Válido bit indica si hay datos válidos en esta entrada o no. Cuando se
inicia el sistema (se inicia), todas las entradas se marcan como
inválidas.
2. El Etiqueta El campo consta de un valor único de 16 bits que identifica la línea de
memoria correspondiente de la que proceden los datos.

3. El Datos El campo contiene una copia de los datos en la memoria. Este campo
contiene una línea de caché de 32 bytes.

En una caché de asignación directa, una palabra de memoria determinada se puede


almacenar exactamente en un lugar dentro de la caché. Dada una dirección de memoria, solo hay
un lugar para buscarla en la caché. Si no está allí, entonces no está en la caché. Para almacenar y
recuperar datos de la caché, la dirección se divide en cuatro componentes, como se muestra en la
figura 4-38 (b):
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 307

Válido
Direcciones que usan esta entrada
Etiqueta Datos
Entrada

2047 65504-65535, 131040-131071,…

7
6
5
4
3 96-127, 65632-65663, 131168-131199
2 64-95, 65600-65631, 131136-131167,…
1 32-63, 65568-65599, 131104-131135,… 0-31,
0 65536-65567, 131072-131103,…

(a)

Bits dieciséis 11 3 2

ETIQUETA LÍNEA BYTE DE PALABRA

(B)

Figura 4-38. (a) Una caché mapeada directamente. (b) Una dirección virtual de 32 bits.

1. El ETIQUETA campo corresponde al Etiqueta bits almacenados en una entrada de caché.

2. El LÍNEA El campo indica qué entrada de caché contiene los datos


correspondientes, si están presentes.

3. El PALABRA campo indica a qué palabra dentro de una línea se hace referencia.

4. El BYTE Por lo general, el campo no se usa, pero si solo se solicita un solo byte,
indica qué byte dentro de la palabra se necesita. Para una caché que solo
proporciona palabras de 32 bits, este campo siempre será 0.

Cuando la CPU produce una dirección de memoria, el hardware extrae los 11 LÍNEA
bits de la dirección y los usa para indexar en la caché para encontrar una de las 2048 entradas. Si
esa entrada es válida, elETIQUETA campo de la dirección de memoria y el Etiqueta se comparan los
campos en la entrada de la caché. Si están de acuerdo, la entrada de la caché contiene la palabra
solicitada, una situación llamadagolpe de caché. Con un acierto, una palabra que se lee se puede
sacar de la caché, eliminando la necesidad de ir a la memoria. Solo la palabra realmente
necesaria se extrae de la entrada de la caché. El resto de la entrada no se utiliza. Si la entrada de
la caché no es válida o las etiquetas no coinciden, la entrada necesaria no está presente en la
caché, una situación llamadafalta de caché. En este caso, la línea de caché de 32 bytes se obtiene
de la memoria y se almacena en la entrada de caché, reemplazando lo que estaba allí. Sin
embargo, si la entrada de caché existente se ha modificado desde que se cargó, se debe volver a
escribir en la memoria principal antes de sobrescribirla.
308 EL NIVEL DE MICROARQUITECTURA CAP. 4

A pesar de la complejidad de la decisión, el acceso a una palabra necesaria puede


ser notablemente rápido. Tan pronto como se conoce la dirección, se conoce la
ubicación exacta de la palabrasi está presente en la caché. Esto significa que es posible
leer la palabra fuera de la caché y entregarla al procesador al mismo tiempo que se
determina si esta es la palabra correcta (comparando etiquetas). Entonces, el
procesador recibe una palabra de la caché simultáneamente, o posiblemente incluso
antes de saber si la palabra es la solicitada.
Este esquema de mapeo coloca líneas de memoria consecutivas en entradas de caché
consecutivas. De hecho, se pueden almacenar hasta 64 KB de datos contiguos en la caché. Sin
embargo, dos líneas que difieren en su dirección precisamente en 65,536 bytes o cualquier
múltiplo integral de ese número no se pueden almacenar en la caché al mismo tiempo (porque
tienen el mismoLÍNEA valor). Por ejemplo, si un programa accede a datos en la ubicaciónX
y luego ejecuta una instrucción que necesita datos en la ubicación X + 65,536 (o cualquier otra
ubicación dentro de la misma línea), la segunda instrucción forzará la recarga de la entrada de
caché, sobrescribiendo lo que estaba allí. Si esto sucede con suficiente frecuencia, puede resultar
en un mal comportamiento. De hecho, el peor comportamiento de un caché es peor que si no
hubiera ningún caché, ya que cada operación de memoria implica leer una línea de caché
completa en lugar de solo una palabra.
Los cachés de mapeo directo son el tipo más común de caché y funcionan de manera
bastante efectiva, porque las colisiones como la descrita anteriormente se pueden hacer que
ocurran solo en raras ocasiones, o no ocurran en absoluto. Por ejemplo, un compilador muy
inteligente puede tener en cuenta las colisiones de caché al colocar instrucciones y datos en la
memoria. Observe que el caso particular descrito no ocurriría en un sistema con cachés de
instrucciones y datos separados, porque las solicitudes en conflicto serían atendidas por
diferentes cachés. Por lo tanto, vemos un segundo beneficio de dos cachés en lugar de uno: más
flexibilidad para lidiar con patrones de memoria conflictivos.

Cachés asociativos de conjuntos

Como se mencionó anteriormente, muchas líneas diferentes en la memoria compiten por las
mismas ranuras de caché. Si un programa que usa la memoria caché de la figura 4-38 (a) usa mucho
palabras en las direcciones 0 y 65.536, habrá conflictos constantes, con cada referencia potencialmente
expulsando a la otra de la memoria caché. Una solución es permitir dos o más líneas en cada entrada de
caché. Un caché connorte posibles entradas para cada dirección se llama un caché asociativo de
conjuntos de n vías. En la figura 4-39 se ilustra una caché asociativa de conjuntos de cuatro vías.

Un caché asociativo de conjuntos es intrínsecamente más complicado que un caché mapeado


directamente porque, aunque el conjunto correcto de entradas de caché para examinar se puede calcular a
partir de la dirección de memoria a la que se hace referencia, un conjunto de norte Las entradas de la caché
deben comprobarse para ver si la línea necesaria está presente. Y tienen que comprobarse muy rápido. Sin
embargo, las simulaciones y la experiencia muestran que las memorias caché de dos y cuatro vías funcionan lo
suficientemente bien como para que valga la pena este circuito adicional.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 309

Válido Válido Válido Válido

Etiqueta Datos Etiqueta Datos Etiqueta Datos Etiqueta Datos


2047

7
6
5
4
3
2
1
0

Entrada A Entrada B Entrada C Entrada D

Figura 4-39. Una caché asociativa de conjuntos de cuatro vías.

El uso de un caché asociativo de conjuntos presenta al diseñador una opción. Cuando se va a


introducir una nueva entrada en la caché, ¿cuál de los elementos presentes se debe descartar? La
decisión óptima, por supuesto, requiere un vistazo al futuro, pero un algoritmo bastante bueno para la
mayoría de los propósitos esLRU (menos usado recientemente). Este algoritmo mantiene un orden de
cada conjunto de ubicaciones a las que se puede acceder desde una ubicación de memoria
determinada. Siempre que se accede a alguna de las líneas presentes, actualiza la lista, marcando esa
entrada como la última a la que se accedió. Cuando llega el momento de reemplazar una entrada, la
que está al final de la lista, la que se accedió menos recientemente, es la que se descarta.

Llevado al extremo, también es posible una caché de 2048 vías que contenga un solo conjunto de
entradas de 2048 líneas. Aquí, todas las direcciones de memoria se asignan a un solo conjunto, por lo
que la búsqueda requiere comparar la dirección con las 2048 etiquetas de la caché. Tenga en cuenta
que ahora cada entrada debe tener una lógica de coincidencia de etiquetas. Desde elLÍNEA El campo es
de longitud 0, el ETIQUETA El campo es la dirección completa excepto por el PALABRA y BYTE los campos.
Además, cuando se reemplaza una línea de caché, todas las 2048 ubicaciones son posibles candidatos
para el reemplazo. Mantener una lista ordenada de 2048 entradas requiere una gran cantidad de
contabilidad, lo que hace que el reemplazo de LRU no sea factible. (Recuerde que esta lista debe
actualizarse en cada operación de memoria, no solo en un error). Sorprendentemente, los cachés de
alta asociatividad no mejoran mucho el rendimiento sobre los cachés de baja asociatividad en la
mayoría de las circunstancias y, en algunos casos, funcionan peor. Por estas razones, la asociatividad de
conjuntos más allá de cuatro vías es relativamente inusual.
Por último, las escrituras plantean un problema especial para las cachés. Cuando un procesador
escribe una palabra y la palabra está en la caché, obviamente debe actualizar la palabra o descartar la
entrada de la caché. Casi todos los diseños actualizan la caché. Pero, ¿qué pasa con la actualización de la
copia en la memoria principal? Esta operación se puede aplazar hasta más tarde, cuando la línea de
caché esté lista para ser reemplazada por el algoritmo LRU. Esta elección es difícil,
310 EL NIVEL DE MICROARQUITECTURA CAP. 4

y ninguna de las opciones es claramente preferible. La actualización inmediata de la entrada en la


memoria principal se denominaescriba por medio de. Este enfoque es generalmente más simple
de implementar y más confiable, ya que la memoria siempre está actualizada; es útil, por
ejemplo, si ocurre un error y es necesario recuperar el estado de la memoria.
Desafortunadamente, también suele requerir más tráfico de escritura en la memoria, por lo que
las implementaciones más sofisticadas tienden a emplear la alternativa, conocida comoescribir
diferido, o respóndeme.
Se debe abordar un problema relacionado para las escrituras: ¿qué sucede si se produce una escritura en
una ubicación que no está almacenada en caché actualmente? ¿Deben llevarse los datos a la caché o
simplemente escribirse en la memoria? Una vez más, ninguna de las respuestas es siempre la mejor. La
mayoría de los diseños que difieren las escrituras en la memoria tienden a llevar datos a la caché en caso de
error de escritura, una técnica conocida comoasignación de escritura. La mayoría de los diseños que emplean
escritura directa, por otro lado, tienden a no asignar una entrada en una escritura porque esta opción complica
un diseño que de otro modo sería simple. La asignación de escritura gana solo si hay escrituras repetidas en la
misma palabra o en palabras diferentes dentro de una línea de caché.
El rendimiento de la caché es fundamental para el rendimiento del sistema porque la brecha entre la velocidad de
la CPU y la velocidad de la memoria es muy grande. En consecuencia, la investigación sobre mejores estrategias de
almacenamiento en caché sigue siendo un tema candente (Sánchez y Kozyrakis, 2011, y Gaur et. Al,
2011).

4.5.2 Predicción de ramas

Las computadoras modernas están altamente canalizadas. La tubería de la figura 4-36 tiene siete
etapas; Las computadoras de gama alta a veces tienen tuberías de 10 etapas o incluso más. La
canalización funciona mejor en código lineal, por lo que la unidad de búsqueda puede leer palabras
consecutivas de la memoria y enviarlas a la unidad de decodificación antes de que se necesiten.

El único problema menor con este maravilloso modelo es que no es el más mínimo
realista. Los programas no son secuencias de códigos lineales. Están llenas de instrucciones
de bifurcación. Considere los enunciados simples de la figura 4-40 (a). Una variable,I, se
compara con 0 (probablemente la prueba más común en la práctica). Dependiendo del
resultado, otra variable,k, se le asigna uno de los dos valores posibles.

si (i == 0) CMP i, 0 ; comparar i con 0


k = 1; BNE Else ; bifurcar a Else si no es
demás Luego: MOV k, 1 igual; mover 1 a k
k = 2; BR Siguiente ; rama incondicional a Siguiente;
Demás: MOV k, 2 mover 2 a k
Próximo:

(a) (B)

Figura 4-40. (a) Un fragmento de programa. (b) Su traducción a un lenguaje ensamblador


genérico.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 311

En la figura 4-40 (b) se muestra una posible traducción al lenguaje ensamblador.


Estudiaremos el lenguaje ensamblador más adelante en este libro, y los detalles no son
importantes ahora, pero dependiendo de la máquina y el compilador, es probable que el código
se parezca más o menos al de la figura 4-40 (b). La primera instrucción comparaI a 0. El segundo
bifurca a la etiqueta Demás (el comienzo de la demás cláusula) si I no es 0. La tercera instrucción
asigna 1 a k. La cuarta instrucción se bifurca al código para la siguiente instrucción. El compilador
ha colocado convenientemente una etiqueta,Próximo, allí, por lo que hay un lugar al que
ramificarse. La quinta instrucción asigna 2 ak.
Lo que hay que observar aquí es que dos de las cinco instrucciones son bifurcaciones.
Además, uno de estos,BNE, es una rama condicional (una rama que se toma si y solo si se
cumple alguna condición, en este caso, que los dos operandos en el anterior
CMP son desiguales). La secuencia de código lineal más larga aquí son dos instrucciones. Como
consecuencia, obtener instrucciones a un ritmo elevado para alimentar la tubería es muy difícil.
A primera vista, podría parecer que las ramas incondicionales, como la instrucción BR
Siguiente en la Fig. 4-40 (b), no son un problema. Después de todo, no hay ambigüedad
sobre a dónde ir. ¿Por qué la unidad de recuperación no puede seguir leyendo las
instrucciones de la dirección de destino (el lugar al que se ramificará)?
El problema radica en la naturaleza de la canalización. En la figura 4-36, por ejemplo, vemos
que la decodificación de instrucciones ocurre en la segunda etapa. Por lo tanto, la unidad de
búsqueda tiene que decidir dónde buscar a continuación antes de saber qué tipo de instrucción
acaba de recibir. Solo un ciclo después puede saber que acaba de tomar una rama incondicional,
y para entonces ya ha comenzado a buscar la instrucción que sigue a la rama incondicional.
Como consecuencia, una cantidad sustancial de máquinas canalizadas (como UltraSPARC III)
tienen la propiedad de que la instrucciónsiguiente se ejecuta una rama incondicional, aunque
lógicamente no debería ser así. La posición después de una rama se llamaranura de retardo. El
Core i7 [y la máquina utilizada en la figura 4-40 (b)] no tienen esta propiedad, pero la complejidad
interna para solucionar este problema es a menudo enorme. Un compilador de optimización
intentará encontrar alguna instrucción útil para colocar en la ranura de retardo, pero con
frecuencia no hay nada disponible, por lo que se ve obligado a insertar unaNOP instrucción allí.
Hacerlo mantiene el programa correcto, pero lo hace más grande y más lento.

Por molestas que sean las ramas incondicionales, las ramas condicionales son peores. No
solo tienen ranuras de retardo, sino que ahora la unidad de búsqueda no sabe desde dónde leer
hasta mucho más tarde en la canalización. Las primeras máquinas canalizadas soloestancado
hasta que se supo si la rama sería tomada o no. Detener tres o cuatro ciclos en
cada rama condicional, especialmente si el 20% de las instrucciones son ramas
condicionales, causa estragos en el rendimiento.
En consecuencia, lo que hacen la mayoría de las máquinas cuando llegan a una rama
condicional es predecir si se tomará o no. Sería bueno si pudiéramos conectar una bola de
cristal en una ranura PCIe libre (o mejor aún, en la IFU) para ayudar con la predicción, pero
hasta ahora este enfoque no ha dado frutos.
A falta de un periférico tan agradable, se han ideado varias formas de hacer la predicción.
Una forma muy simple es la siguiente: suponga que todos los condicionales hacia atrás
312 EL NIVEL DE MICROARQUITECTURA CAP. 4

Se tomarán ramas y que no se tomarán todas las de adelante. El razonamiento detrás de la


primera parte es que las ramas hacia atrás se ubican con frecuencia al final de un bucle. La
mayoría de los bucles se ejecutan varias veces, por lo que adivinar que se tomará una rama hacia
la parte superior del bucle generalmente es una buena apuesta.
La segunda parte es más inestable. Algunas bifurcaciones hacia adelante ocurren cuando se
detectan condiciones de error en el software (por ejemplo, no se puede abrir un archivo). Los errores
son raros, por lo que la mayoría de las ramas asociadas con ellos no se toman. Por supuesto, hay
muchas ramas hacia adelante que no están relacionadas con el manejo de errores, por lo que la tasa de
éxito no es tan buena como con las ramas hacia atrás. Si bien no es fantástico, esta regla es al menos
mejor que nada.
Si una rama se predice correctamente, no hay nada especial que hacer. La ejecución
simplemente continúa en la dirección de destino. El problema surge cuando una rama se
predice incorrectamente. No es difícil averiguar adónde ir y llegar allí. La parte difícil es
deshacer las instrucciones que ya se han ejecutado y no deberían haberse ejecutado.

Hay dos formas de hacerlo. La primera forma es permitir que las instrucciones obtenidas
después de una rama condicional predicha se ejecuten hasta que intenten cambiar el estado de
la máquina (por ejemplo, almacenar en un registro). En lugar de sobrescribir el registro, el valor
calculado se coloca en un registro temporal (secreto) y solo se copia en el registro real después
de que se sabe que la predicción fue correcta. La segunda forma es registrar el valor de cualquier
registro que esté a punto de sobrescribirse (por ejemplo, en un registro cero secreto), para que la
máquina pueda volver al estado que tenía en el momento de la bifurcación mal pronosticada.
Ambas soluciones son complejas y requieren una contabilidad de nivel industrial para hacerlas
bien. Y si se golpea una segunda rama condicional antes de que se sepa si la primera se predijo
correctamente, las cosas pueden complicarse mucho.

Predicción dinámica de ramas

Claramente, tener predicciones precisas es de gran valor, ya que permite que la


CPU funcione a toda velocidad. Como consecuencia, gran parte de la investigación en
curso tiene como objetivo mejorar los algoritmos de predicción de ramas (Chen et al.,
2003, Falcon et al., 2004, Jimenez, 2003 y Parikh et al., 2004). Un enfoque es que la CPU
mantenga una tabla de historial (en hardware especial), en la que registra las ramas
condicionales a medida que ocurren, de modo que se puedan buscar cuando vuelvan
a ocurrir. La versión más simple de este esquema se muestra en la figura 4-41 (a).
Aquí, la tabla de historial contiene una entrada para cada instrucción de bifurcación
condicional. La entrada contiene la dirección de la instrucción de bifurcación junto con
un bit que indica si se tomó la última vez que se ejecutó. Usando este esquema, la
predicción es simplemente que la rama seguirá el mismo camino que la última vez.

Hay varias formas de organizar la tabla de historial. De hecho, estas son precisamente las mismas formas
que se utilizan para organizar un caché. Considere una máquina con instrucciones de 32 bits que están
alineadas con palabras de modo que los 2 bits de orden inferior de cada dirección de memoria sean
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 313

Rama/ Predicción
Válido sin rama Válido
Predicción
Válido bits
Rama Rama Rama Objetivo
bits
Espacio dirección / etiqueta Espacio dirección / etiqueta Espacio dirección / etiqueta Dirección

6 6 6
5 5 5
4 4 4
3 3 3
2 2 2
1 1 1
0 0 0

(a) (B) (C)

Figura 4-41. (a) Un bit de 1 historia de la sucursal. (b) Un historial de bifurcaciones de 2 bits. (c) Un mapa-
ping entre la dirección de instrucción de bifurcación y la dirección de destino.

00. Con una tabla de historial mapeada directamente que contiene 2norte entradas, el orden bajo n + Se
pueden extraer 2 bits de una dirección de destino de instrucción de bifurcación y desplazarlos 2 bits a la
derecha. Estanorte-El número de bit se puede usar como un índice en la tabla de historial donde se
realiza una verificación para ver si la dirección almacenada allí coincide con la dirección de la sucursal. Al
igual que con un caché, no es necesario almacenar losn + 2 bits, por lo que se pueden omitir (es decir,
solo se almacenan los bits de la dirección superior, la etiqueta). Si hay un acierto, el bit de predicción se
usa para predecir la rama. Si la etiqueta incorrecta está presente o la entrada no es válida, se produce
un error, al igual que con un caché. En este caso, se puede utilizar la regla de bifurcación hacia
adelante / hacia atrás.
Si la tabla del historial de sucursales tiene, digamos, 4096 entradas, entonces se ramifica en las direcciones 0,
16384, 32768, ... entrará en conflicto, de forma análoga al mismo problema con un caché. La
misma solución es posible: bidireccional, cuádruple onorte-forma de entrada asociativa. Al igual
que con un caché, el caso límite es un solonorte-forma de entrada asociativa, que requiere
asociatividad total de búsqueda.
Dado un tamaño de tabla suficientemente grande y suficiente asociatividad, este esquema
funciona bien en la mayoría de las situaciones. Sin embargo, siempre ocurre un problema sistemático.
Cuando finalmente se sale de un bucle, la rama al final se predecirá mal y, lo que es peor, la predicción
errónea cambiará el bit en la tabla de historial para indicar una predicción futura de "sin rama". La
próxima vez que se ingrese al bucle , la rama al final de la primera iteración se predecirá
incorrectamente. Si el bucle está dentro de un bucle externo, o en un procedimiento llamado con
frecuencia, este error puede ocurrir con frecuencia.
Para eliminar este error de predicción, podemos darle una segunda oportunidad a la entrada de la
tabla. Con este método, la predicción se cambia solo después de dos predicciones incorrectas
consecutivas. Este enfoque requiere tener dos bits de predicción en la tabla de historial, uno para lo que
"se supone" que debe hacer la rama y otro para lo que hizo la última vez, como se muestra en la figura
4-41 (b).
Una forma ligeramente diferente de ver este algoritmo es verlo como una máquina de
estados finitos con cuatro estados, como se muestra en la figura 4-42. Después de una serie de
predicciones consecutivas exitosas de '' sin bifurcación '', el FSM estará en el estado 00 y predecirá
314 EL NIVEL DE MICROARQUITECTURA CAP. 4

"sin rama" la próxima vez. Si esa predicción es incorrecta, pasará al estado 01, pero también
predecirá "sin rama" la próxima vez. Solo si esta predicción es incorrecta, ahora pasará al estado
11 y predecirá las ramas todo el tiempo. En efecto, el bit más a la izquierda del estado es la
predicción y el bit más a la derecha es lo que hizo la rama la última vez. Si bien este diseño usa
solo 2 bits de historia, un diseño que realiza un seguimiento de 4 u 8 bits de historia
La historia también es posible.

Sin sucursal
Rama Rama

01 10 No
00 Rama rama 11
Predecir Predecir
Predecir sin rama rama Predecir
sin rama No uno mas uno mas Rama rama
rama tiempo tiempo

Sin sucursal

Figura 4-42. Una máquina de estados finitos de 2 bits para la predicción de ramas.

Este no es nuestro primer FSM. La figura 4-28 también era un FSM. De hecho, todos nuestros
microprogramas pueden considerarse FSM, ya que cada línea representa un estado específico en el que
puede estar la máquina, con transiciones bien definidas a un conjunto finito de otros estados. Los FSM
se utilizan ampliamente en todos los aspectos del diseño de hardware.
Hasta ahora, hemos asumido que el objetivo de cada rama condicional se conocía,
normalmente como una dirección explícita a la que se bifurca (contenida dentro de la
propia instrucción), o como un desplazamiento relativo de la instrucción actual (es
decir, un número con signo para agregar al contador de programas). A menudo, esta
suposición es válida, pero algunas instrucciones de rama condicional calculan la
dirección de destino haciendo aritmética en los registros y luego yendo allí. Incluso si
el FSM de la figura 4-42 predice con precisión que se tomará la rama, tal predicción no
sirve de nada si se desconoce la dirección de destino. Una forma de lidiar con esta
situación es almacenar la dirección real bifurcada hasta la última vez en la tabla de
historial, como se muestra en la figura 4-41 (c). De esta forma, si la tabla dice que la
última vez que se tomó la sucursal en la dirección 516 fue a la dirección 4000,

Un enfoque diferente para la predicción de ramas es realizar un seguimiento de si la última k


Se tomaron las ramas condicionales encontradas, independientemente de las instrucciones que fueran.
Estak-número de bits, guardado en el registro de cambios del historial de sucursales, luego se
compara en paralelo con todas las entradas de una tabla de historial con un k-bit clave y, si se produce
un acierto, se utiliza la predicción que se encuentra allí. Sorprendentemente, esta técnica funciona
bastante bien en la práctica.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 315

Predicción de rama estática

Todas las técnicas de predicción de ramas discutidas hasta ahora son dinámicas, es decir, se
llevan a cabo en tiempo de ejecución mientras el programa se está ejecutando. También se
adaptan al comportamiento actual del programa, lo cual es bueno. La desventaja es que
requieren un hardware costoso y especializado y una gran complejidad de chip.
Una forma diferente de hacerlo es contar con la ayuda del compilador. Cuando el compilador ve una
declaración como

para (i = 0; i <1000000; i ++) {...}

sabe muy bien que la rama al final del ciclo se tomará casi todo el tiempo. Si tan solo tuviera
una forma de avisarle al hardware, se podría ahorrar mucho esfuerzo.
Aunque se trata de un cambio de arquitectura (y no solo un problema de implementación),
algunas máquinas, como UltraSPARC III, tienen un segundo conjunto de instrucciones de
bifurcación condicionales, además de las normales (que son necesarias para la compatibilidad
con versiones anteriores). Los nuevos contienen un bit en el que el compilador puede especificar
que cree que la rama se tomará (o no). Cuando se encuentra uno de estos, la unidad de
búsqueda simplemente hace lo que se le ha dicho. Además, no es necesario desperdiciar un
espacio precioso en la tabla del historial de la sucursal para estas instrucciones, lo que reduce los
conflictos allí.
Finalmente, nuestra última técnica de predicción de ramas se basa en la elaboración de
perfiles (Fisher y Freudenberger, 1992). Esto también es una técnica estática, pero en lugar de
que el compilador intente averiguar qué ramas se tomarán y cuáles no, el programa se ejecuta
realmente (generalmente en un simulador) y se captura el comportamiento de la rama. Esta
información se alimenta al compilador, que luego usa las instrucciones especiales de bifurcación
condicional para decirle al hardware qué hacer.

4.5.3 Ejecución fuera de orden y cambio de nombre de registros

La mayoría de las CPU modernas son tanto canalizadas como superescalares, como se muestra en la
figura 2-6. Lo que esto generalmente significa es que una unidad de búsqueda extrae palabras de instrucción
de la memoria antes de que se necesiten para alimentar una unidad de decodificación. La unidad de
decodificación emite las instrucciones decodificadas a las unidades funcionales adecuadas para su ejecución.
En algunos casos, puede dividir instrucciones individuales en microoperaciones antes de emitirlas,
dependiendo de lo que puedan hacer las unidades funcionales.
Claramente, el diseño de la máquina es más simple si todas las instrucciones se ejecutan en
el orden en que se obtienen (asumiendo por el momento que el algoritmo de predicción de rama
nunca adivina mal). Sin embargo, la ejecución en orden no siempre ofrece un rendimiento
óptimo debido a las dependencias entre instrucciones. Si una instrucción necesita un valor
calculado por la instrucción anterior, la segunda no puede comenzar a ejecutarse hasta que la
primera haya producido el valor necesario. En esta situación (una dependencia de RAW), la
segunda instrucción tiene que esperar. También existen otros tipos de dependencias, como
veremos pronto.
316 EL NIVEL DE MICROARQUITECTURA CAP. 4

En un intento por solucionar estos problemas y producir un mejor rendimiento, algunas CPU
permiten que se omitan las instrucciones dependientes para acceder a instrucciones futuras que
no son dependientes. No hace falta decir que el algoritmo interno de programación de
instrucciones utilizado debe producir el mismo efecto que si el programa se ejecutara en el orden
en que se escribió. Ahora demostraremos cómo funciona el reordenamiento de instrucciones
usando un ejemplo detallado.
Para ilustrar la naturaleza del problema, comenzaremos con una máquina que siempre
emite instrucciones en el orden del programa y también requiere que completen la ejecución en
el orden del programa. El significado de este último se aclarará más adelante.
Nuestra máquina de ejemplo tiene ocho registros visibles para el programador, R0
mediante R7. Todas las instrucciones aritméticas usan tres registros: dos para los operandos
y uno para el resultado, lo mismo que el Mic-4. Supondremos que si una instrucción se
decodifica en ciclonorte, la ejecución comienza en ciclo n + 1. Para una instrucción simple,
como una suma o resta, la escritura en el registro de destino ocurre al final del ciclo. n + 2.
Para una instrucción más complicada, como una multiplicación, la escritura diferida ocurre
al final del ciclo. n + 3. Para que el ejemplo sea realista, permitiremos que la unidad de
decodificación emita hasta dos instrucciones por ciclo de reloj. Las CPU superescalares
comerciales a menudo pueden emitir cuatro o incluso seis instrucciones por ciclo de reloj.

Nuestro ejemplo de secuencia de ejecución se muestra en la figura 4-43. Aquí la


primera columna da el número del ciclo y la segunda da el número de instrucción. La
tercera columna enumera la instrucción decodificada. El cuarto indica qué instrucción se
está emitiendo (con un máximo de dos por ciclo de reloj). El quinto indica qué instrucción se
ha retirado (completado). Recuerde que en este ejemplo estamos requiriendo tanto la
emisión en orden como la finalización en orden, por lo que la instrucciónk + 1 no se puede
emitir hasta que se instruya k se ha emitido, y la instrucción k + 1 no se puede retirar (lo
que significa que se realiza la reescritura en el registro de destino) hasta que la instrucción
k ha sido retirado. Las otras 16 columnas se analizan a continuación.
Después de decodificar una instrucción, la unidad de decodificación tiene que decidir si
puede emitirse inmediatamente o no. Para tomar esta decisión, la unidad de decodificación
necesita conocer el estado de todos los registros. Si, por ejemplo, la instrucción actual necesita
un registro cuyo valor aún no se ha calculado, la instrucción actual no se puede emitir y la CPU
debe detenerse.
Realizaremos un seguimiento del uso del registro con un dispositivo llamado marcador, que
estaba presente por primera vez en el CDC 6600. El marcador tiene un pequeño contador para cada
registro que indica cuántas veces ese registro está en uso como fuente mediante la ejecución de
instrucciones en ese momento. Si un máximo de, digamos, 15 instrucciones se pueden ejecutar a la vez,
entonces un contador de 4 bits será suficiente. Cuando se emite una instrucción, se incrementan las
entradas del marcador para sus registros de operandos. Cuando se retira una instrucción, las entradas
se reducen.
El marcador también tiene contadores para realizar un seguimiento de los registros que se utilizan
como destinos. Dado que solo se permite una escritura a la vez, estos contadores pueden tener 1 bit de
ancho. Las 16 columnas de la derecha en la figura 4-43 muestran el marcador.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 317

Registros que se están leyendo Registros en escritura


Cy # Descifrado Iss Retirado 01234567 01234567
1 1 R3 = R0 * R1 1 11 1
2 R4 = R0 + R2 2 211 11
2 3 R5 = R0 + R1 3 321 111
4 R6 = R1 + R4 - 321 111
3 321 111
4 1 211 11
2 11 1
3
5 4 1 1 1
5 R7 = R1 * R2 5 21 1 11
6 6 R1 = R0-R2 - 21 1 11
7 4 11 1
8 5
9 6 1 1 1
7 R3 = R3 * R1 - 1 1 1
10 1 1 1
11 6
12 7 1 1 1
8 R1 = R4 + R4 - 1 1 1
13 1 1 1
14 1 1 1
15 7
dieciséis 8 2 1
17 2 1
18 8

Figura 4-43. Una CPU superescalar con problemas en orden y finalización en orden.

En máquinas reales, el marcador también realiza un seguimiento del uso de la unidad funcional,
para evitar emitir una instrucción para la que no hay una unidad funcional disponible. Para simplificar,
asumiremos que siempre hay una unidad funcional adecuada disponible, por lo que no mostraremos
las unidades funcionales en el marcador.
La primera línea de la figura 4-43 muestra I1 (instrucción 1), que multiplica R0 por R1
y pone el resultado en R3. Dado que ninguno de estos registros está todavía en uso, se emite la
instrucción y el marcador se actualiza para reflejar que R0 y R1 están siendo leídos y R3 se está
escribiendo. Ninguna instrucción posterior puede escribir en ninguno de estos o puede leerR3
hasta que I1 se haya retirado. Dado que esta instrucción es una multiplicación, se terminará al
final del ciclo 4. Los valores del marcador que se muestran en cada línea reflejan su estado
después de que se haya emitido la instrucción en esa línea. Los espacios en blanco son ceros.
318 EL NIVEL DE MICROARQUITECTURA CAP. 4

Dado que nuestro ejemplo es una máquina superescalar que puede emitir dos instrucciones
por ciclo, se emite una segunda instrucción (I2) durante el ciclo 1. Agrega R0 y R2, almacenar el
resultado en R4. Para ver si se puede emitir esta instrucción, se aplican estas reglas:

1. Si se está escribiendo algún operando, no lo emita (dependencia RAW).

2. Si se está leyendo el registro de resultados, no lo emita (dependencia de WAR).

3. Si se está escribiendo el registro de resultados, no lo emita (dependencia de


WAW).

Ya hemos visto dependencias RAW, que ocurren cuando una instrucción necesita usar como
fuente un resultado que una instrucción anterior aún no ha producido. Las otras dos
dependencias son menos graves. Son esencialmente conflictos de recursos. en unDependencia
de WAR (Write After Read), una instrucción está tratando de sobrescribir un registro que una
instrucción anterior aún no ha terminado de leer. ADependencia WAW (Write After Write) es
similar. A menudo, estos pueden evitarse haciendo que la segunda instrucción coloque sus
resultados en otro lugar (quizás temporalmente). Si no existe ninguna de las tres dependencias
anteriores y la unidad funcional que necesita está disponible, se emite la instrucción. En este
caso, I2 usa un registro (R0) que está siendo leído por una instrucción pendiente, pero esta
superposición está permitida, por lo que se emite I2. Del mismo modo, I3 se emite durante el
ciclo 2.
Ahora llegamos a I4, que necesita usar R4. Desafortunadamente, vemos en la línea 3 que R4 se está
escribiendo. Aquí tenemos una dependencia RAW, por lo que la unidad de decodificación se detiene hastaR4 se
vuelve disponible. Mientras está detenido, deja de extraer instrucciones de la unidad de recuperación. Cuando
los búferes internos de la unidad de búsqueda se llenan, se detiene la búsqueda previa.
Vale la pena señalar que la siguiente instrucción en el orden del programa, I5, no tiene
conflictos con ninguna de las instrucciones pendientes. Podría haber sido decodificado y emitido
si no fuera por el hecho de que este diseño requiere emitir instrucciones en orden.
Ahora veamos lo que sucede durante el ciclo 3. I2, siendo una suma (dos ciclos),
termina al final del ciclo 3. Desafortunadamente, no se puede retirar (liberando así R4
para I4). ¿Por qué no? La razón es que este diseño también requiere un retiro en
orden. ¿Por qué? ¿Qué daño podría provenir de hacer la tienda en
R4 ahora y marcarlo como disponible?
La respuesta es sutil, pero importante. Suponga que las instrucciones pudieran completarse
fuera de orden. Entonces, si ocurriera una interrupción, sería difícil guardar el estado de la
máquina para poder restaurarla más tarde. En particular, no sería posible decir que se han
ejecutado todas las instrucciones hasta alguna dirección y que no todas las instrucciones
posteriores. A esto se le llamainterrupción precisa y es una característica deseable en una CPU
(Moudgill y Vassiliadis, 1996). La retirada fuera de servicio hace que las interrupciones sean
imprecisas, por lo que algunas máquinas completan las instrucciones en orden.
Volviendo a nuestro ejemplo, al final del ciclo 4, las tres instrucciones pendientes se pueden
retirar, por lo que en el ciclo 5 finalmente se puede emitir I4, junto con el I5 recién decodificado.
Siempre que se retira una instrucción, la unidad de decodificación debe verificar si hay una
instrucción detenida que ahora se puede emitir.
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 319

En el ciclo 6, I6 se detiene porque necesita escribir en R1 y R1 está ocupado.


Finalmente se inicia en el ciclo 9. La secuencia completa de ocho instrucciones tarda 18
ciclos en completarse debido a muchas dependencias, aunque el hardware es capaz
de emitir dos instrucciones en cada ciclo. Observe, sin embargo, al leer elIss
columna de la Fig. 4-43, que todas las instrucciones se han emitido en orden. Asimismo, el
Retirado La columna muestra que también se han retirado en orden.
Ahora consideremos un diseño alternativo: ejecución fuera de orden. En este diseño, las
instrucciones se pueden emitir fuera de servicio y se pueden retirar fuera de servicio como
bien. La misma secuencia con de ocho instrucciones se muestra en la Fig. 4-44, sólo ahora se
un problema desordenado y permite la retirada fuera de servicio.

Registros que se están leyendo Registros en escritura


Cy # Descifrado Iss Retirado 01234567 01234567
1 1 R3 = R0 * R1 1 11 1
2 R4 = R0 + R2 2 211 11
2 3 R5 = R0 + R1 3 321 111
4 R6 = R1 + R4 - 321 111
3 5 R7 = R1 * R2 5 332 111 1
6 S1 = R0-R2 6 433 111 1
2 332 11 1
4 4 342 1 1111
7 R3 = R3 * S1 - 342 1 1111
8 S2 = R4 + R4 8 342 3 1111
1 232 3 111
3 122 3 11
5 6 21 3 1 11
6 7 2113 1 1 11
4 1112 1 1 1
5 12 1 1
8 1 1
7 1 1
8 1 1
9 7

Figura 4-44. Operación de una CPU superescalar con problema de avería y finalización
fuera de orden.

La primera diferencia ocurre en el ciclo 3. Aunque I4 se ha estancado, podemos decodificar y emitir


I5 ya que no entra en conflicto con ninguna instrucción pendiente. Sin embargo, omitir las instrucciones
provoca un nuevo problema. Suponga que I5 hubiera usado un operando calculado por la instrucción
omitida, I4. Con el cuadro de indicadores actual, no nos habríamos dado cuenta de esto. Como
consecuencia, tenemos que ampliar el marcador para realizar un seguimiento de las tiendas realizadas
mediante instrucciones omitidas. Esto se puede hacer agregando un segundo mapa de bits, 1 bit por
registro, para realizar un seguimiento de las tiendas realizadas
320 EL NIVEL DE MICROARQUITECTURA CAP. 4

por instrucciones estancadas. (Estos contadores no se muestran en la figura). La regla para


emitir instrucciones ahora debe ampliarse para evitar la emisión de cualquier instrucción
con un operando programado para ser almacenado por una instrucción que vino antes
pero que fue omitida.
Ahora echemos un vistazo a I6, I7 e I8 en la figura 4-43. Aquí vemos que I6 calcula un
valor enR1 que utiliza I7. Sin embargo, también vemos que el valor nunca se vuelve a utilizar
porque I8 sobrescribeR1. No hay una razón real para usar R1 como el lugar para guardar el
resultado de I6. Peor aún,R1 es una elección terrible de registro intermedio, aunque
perfectamente razonable para un compilador o programador acostumbrado a la idea de
ejecución secuencial sin superposición de instrucciones.
En la figura 4-44 presentamos una nueva técnica para resolver este problema: registro de
cambio de nombre. La unidad de decodificación inteligente cambia el uso de R1 en I6 (ciclo 3) e
I7 (ciclo 4) a un registro secreto, S1, no visible para el programador. Ahora I6 se puede emitir al
mismo tiempo que I5. Las CPU modernas a menudo tienen docenas de registros secretos para
usar con el cambio de nombre de registros. Esta técnica a menudo puede eliminar las
dependencias de WAR y WAW.
En I8, usamos el cambio de nombre de registros nuevamente. Esta vezR1 se renombra en S2
para que la adición se pueda iniciar antes R1 es gratis, al final del ciclo 6. Si resulta que el
resultado realmente tiene que estar en R1 esta vez, el contenido de S2 siempre se puede copiar allí
justo a tiempo. Aún mejor, todas las instrucciones futuras que lo necesiten pueden cambiar el
nombre de sus fuentes al registro donde realmente está almacenado. En cualquier caso, la
adición de I8 comenzó antes de esta manera.
En muchas máquinas reales, el cambio de nombre está profundamente arraigado en la
forma en que se organizan los registros. Hay muchos registros secretos y una tabla que mapea
los registros visibles para el programador en los registros secretos. Así, el registro real se utiliza
para, digamos,R0 se encuentra mirando la entrada 0 de esta tabla de mapeo. De esta forma, no
existe un registro realR0, solo un enlace entre el nombre R0 y uno de los registros secretos. Este
enlace cambia con frecuencia durante la ejecución para evitar dependencias.

Observe en la Fig. 4-44, al leer la cuarta columna, que las instrucciones no se


han emitido en orden. Tampoco se han retirado en regla. La conclusión de este
ejemplo es simple: usando la ejecución fuera de orden y el cambio de nombre de
registros, pudimos acelerar el cálculo en un factor de dos.

4.5.4 Ejecución especulativa

En la sección anterior, presentamos el concepto de reordenar instrucciones para


mejorar el rendimiento. Aunque no lo mencionamos explícitamente, la atención se centró
en reordenar las instrucciones dentro de un solo bloque básico. Ha llegado el momento de
examinar este punto más de cerca.
Los programas de computadora se pueden dividir en bloques básicos, cada uno consta de una
secuencia lineal de código con un punto de entrada en la parte superior y una salida en la parte inferior.
Un bloque básico no contiene ninguna estructura de control (p. Ej.,si declaraciones o tiempo
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 321

declaraciones) para que su traducción al lenguaje de máquina no contenga ramas. Los


bloques básicos están conectados por declaraciones de control.
Un programa de esta forma se puede representar como un gráfico dirigido, como se
muestra en la figura 4-45. Aquí calculamos la suma de los cubos de los enteros pares e impares
hasta cierto límite y los acumulamos enevensum y oddsum, respectivamente. Dentro de cada
bloque básico, las técnicas de reordenación de la sección anterior funcionan bien.

evensum = 0; evensum = 0;
oddsum = 0;
oddsum = 0;
i = 0;
i = 0;
i> = límite
while (i <límite) { mientras (i <límite)

k = yo * yo * yo;

k = yo * yo * yo;
si (((i / 2) * 2) == i) si ((i / 2) * 2) = = i)

evensum = evensum + k; T F

demás evensum = evensum + k; suma de probabilidades = suma de probabilidades + k;

suma de probabilidades = suma de probabilidades + k;

i = i + 1;
i = i + 1;

}
(a) (B)

Figura 4-45. (a) Un fragmento de programa. (b) El gráfico de bloques básico correspondiente.

El problema es que la mayoría de los bloques básicos son cortos y no hay suficiente paralelismo en ellos
para explotarlos de manera efectiva. En consecuencia, el siguiente paso es permitir que el reordenamiento
cruce los límites de los bloques básicos en un intento de llenar todos los espacios de emisión. Las mayores
ganancias se obtienen cuando una operación potencialmente lenta se puede mover hacia arriba en el gráfico
para comenzar temprano. Esto podría ser unCARGA instrucción, una operación de punto flotante, o incluso el
comienzo de una larga cadena de dependencia. Mover el código hacia arriba sobre una rama se llamaizar.

Imagine que en la figura 4-45 todas las variables se mantuvieron en registros excepto
evensum y oddsumpor falta de registros). Entonces podría tener sentido mover su
CARGA instrucciones en la parte superior del bucle, antes de calcular k, para que comiencen desde
el principio, por lo que los valores estarán disponibles cuando sea necesario. Por supuesto, solo
uno de ellos será necesario en cada iteración, por lo que el otroCARGA se desperdiciará, pero si la
memoria caché y la memoria están canalizadas y hay espacios disponibles para problemas,
puede que valga la pena hacerlo. Ejecutar código antes de que se sepa si va a ser necesario se
llamaejecución especulativa. El uso de esta técnica requiere el soporte del compilador y el
hardware, así como algunas extensiones arquitectónicas. Normalmente, reordenar las
instrucciones sobre los límites de los bloques básicos está más allá de la capacidad del hardware,
por lo que el compilador debe mover las instrucciones explícitamente.
322 EL NIVEL DE MICROARQUITECTURA CAP. 4

La ejecución especulativa presenta algunos problemas interesantes. Por un lado, es esencial


que ninguna de las instrucciones especulativas tenga resultados irrevocables porque puede
resultar más tarde que no deberían haberse ejecutado. En la figura 4-45, está bien buscar
evensum y oddsum, y también está bien hacer la adición tan pronto como k está disponible
(incluso antes de si declaración), pero no está bien volver a almacenar los resultados en la
memoria. En secuencias de código más complicadas, una forma común de evitar que el código
especulativo sobrescriba los registros antes de que se sepa si se desea, es cambiar el nombre de
todos los registros de destino utilizados por el código especulativo. De esta forma, solo se
modifican los registros scratch, por lo que no hay problema si el código finalmente no es
necesario. Si se necesita el código, los registros temporales se copian en los registros de destino
verdaderos. Como se puede imaginar, el marcador para realizar un seguimiento de todo esto no
es simple, pero con suficiente hardware, se puede hacer.
Sin embargo, existe otro problema introducido por el código especulativo que no puede
resolverse mediante el cambio de nombre de registros. ¿Qué sucede si una instrucción ejecutada
especulativamente causa una excepción? Un ejemplo doloroso, pero no fatal, es unCARGA
instrucción que causa una falta de caché en una máquina con un tamaño de línea de caché
grande (digamos, 256 bytes) y una memoria mucho más lenta que la CPU y la caché. Si unCARGA
que realmente se necesita detiene la máquina en seco durante muchos ciclos mientras se carga
la línea de caché, bueno, así es la vida, ya que se necesita la palabra. Sin embargo, detener la
máquina para buscar una palabra que resulta no ser necesaria es contraproducente. Demasiadas
de estas "optimizaciones" pueden hacer que la CPU sea más lenta que si no las tuviera en
absoluto. (Si la mquina tiene memoria virtual, que se analiza en el captulo 6, unaCARGA incluso
podría causar una falla de página, lo que requiere una operación de disco para traer la página
necesaria. Los errores de página falsos pueden tener un efecto terrible en el rendimiento, por lo
que es importante evitarlos).
Una solución presente en una serie de máquinas modernas es tener un especial
CARGA ESPECULATIVA instrucción que intenta obtener la palabra de la caché, pero si no está
allí, simplemente se rinde. Si el valor está ahí cuando realmente se necesita, se puede usar,
pero si no lo está, el hardware debe salir y obtenerlo en el acto. Si resulta que el valor no es
necesario, no se ha pagado ninguna penalización por la falta de caché.
Una situación mucho peor se puede ilustrar con la siguiente declaración:

si (x> 0) z = y / x;

dónde x, y, y z son variables de coma flotante. Suponga que todas las variables se
recuperan en registros de antemano y que la división (lenta) de punto flotante se eleva por
encima de lasi prueba. Desafortunadamente,X es 0 y la trampa resultante de dividir por
cero termina el programa. El resultado neto es que la especulación ha provocado que un
programa correcto falle. Peor aún, el programador puso un código explícito para evitar esta
situación y sucedió de todos modos. No es probable que esta situación lleve a un
programador feliz.
Una posible solución es tener versiones especiales de instrucciones que puedan causar
excepciones. Además, un poco, llamadoun poco de veneno se agrega a cada registro. Cuando
una instrucción especulativa especial falla, en lugar de causar una trampa, establece la
SEGUNDO. 4.5 MEJORANDO EL DESEMPEÑO 323

bit de veneno en el registro de resultados. Si ese registro es tocado más tarde por una instrucción
regular, la trampa ocurre entonces (como debería). Sin embargo, si el resultado nunca se usa, el bit de
veneno finalmente se borra y no se produce ningún daño.

4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA

En esta sección, mostraremos breves ejemplos de tres procesadores de última


generación, mostrando cómo emplean los conceptos explorados en este capítulo. Estos
serán necesariamente breves porque las máquinas reales son enormemente complejas y
contienen millones de puertas. Los ejemplos son los mismos que hemos estado usando
hasta ahora: Core i7, OMAP4430 y ATmega168.

4.6.1 La microarquitectura de la CPU Core i7

En el exterior, el Core i7 parece ser una máquina CISC tradicional, con procesadores
que admiten un conjunto de instrucciones enorme y difícil de manejar que admite
operaciones de enteros de 8, 16 y 32 bits, así como funciones flotantes de 32 y 64 bits.
operaciones puntuales. Tiene solo ocho registros visibles por procesador y no hay dos
iguales. Las longitudes de las instrucciones varían de 1 a 17 bytes. En resumen, es una
arquitectura heredada que parece hacer todo mal.
Sin embargo, en el interior, el Core i7 contiene un núcleo RISC moderno, delgado y medio,
profundamente canalizado que se ejecuta a una frecuencia de reloj extremadamente rápida que
probablemente aumentará en los próximos años. Es bastante sorprendente cómo los ingenieros de
Intel lograron construir un procesador de última generación para implementar una arquitectura
antigua. En esta sección veremos la microarquitectura Core i7 para ver cómo funciona.

Descripción general de la microarquitectura Sandy Bridge del Core i7

La microarquitectura Core i7, llamada Sandy Bridge microarquitectura, es un


refinamiento significativo de las microarquitecturas de Intel de la generación anterior,
incluidas las anteriores P4 y P6. En la figura 4-46 se ofrece una descripción general de la
microarquitectura Core i7.
El Core i7 consta de cuatro subsecciones principales: el subsistema de memoria, el front-end,
el control fuera de servicio y las unidades de ejecución. Examinemos estos uno a la vez
comenzando en la parte superior izquierda y yendo en sentido antihorario alrededor del chip.
Cada procesador del Core i7 contiene un subsistema de memoria con una caché L2
(nivel 2) unificada, así como la lógica para acceder a la caché L3 (nivel 3). Todos los
procesadores comparten una única caché L3 grande, y es la última parada antes de dejar el
chip de la CPU y hacer un viaje muy largo a la RAM externa a través del bus de memoria.
Los cachés L2 del Core i7 tienen un tamaño de 256 KB y cada uno está organizado como un
324 EL NIVEL DE MICROARQUITECTURA CAP. 4

Al subsistema de memoria

caché L3 compartida Unidad de ejecución

Nivel 1
Interfaz del sistema
caché de datos

Caché de nivel 2 ALU enteros, punto flotante


(instrucciones y datos) unidades, búfer de almacenamiento

Obtener / decodificar Microoperación Renombrar, Jubilación


unidad cache Planificación unidad

Nivel 1 Rama
caché inst vaticinador

Interfaz Control fuera de servicio

Figura 4-46. El diagrama de bloques de la microarquitectura Sandy Bridge del Core i7.

caché asociativo con líneas de caché de 64 bytes. La caché L3 compartida varía en tamaño
de 1 MB a 20 MB. Si paga más efectivo a Intel, obtiene más caché a cambio.
Independientemente de su tamaño, el L3 está organizado como un caché asociativo de 12
vías con líneas de caché de 64 bytes. En el caso de que se pierda un acceso a la caché L3, se
envía a la RAM externa a través del bus RAM DDR3.
Asociadas con la caché L1 hay dos unidades de captación previa (no se muestran en la
figura) que intentan captar datos de niveles inferiores del sistema de memoria en la L1 antes de
que sean necesarios. Una unidad de captación previa capta previamente el siguiente bloque de
memoria cuando detecta que se está captando un "flujo" secuencial de memoria en el
procesador. Un segundo prefetcher, más sofisticado, rastrea la secuencia de direcciones de
cargas y almacenes de programas específicos. Si progresan con paso regular (p. Ej., 0x1000 ...
0x1020 ... 0x1040 ...) precargará el siguiente elemento al que probablemente se accederá antes
del programa. Esta captación previa orientada a zancadas hace maravillas para los programas
que marchan a través de matrices de variables estructuradas.
El subsistema de memoria de la figura 4-46 está conectado tanto al front-end como al caché de
datos L1. El front-end es responsable de obtener instrucciones del subsistema de memoria,
decodificarlas en microoperaciones similares a RISC y almacenarlas en dos cachés de almacenamiento
de instrucciones. Todas las instrucciones obtenidas se colocan en la caché de instrucciones L1 (nivel 1).
La caché L1 tiene un tamaño de 32 KB, organizada como una caché asociativa de 8 vías con bloques de
64 bytes. A medida que las instrucciones se obtienen de la caché L1, ingresan a los decodificadores que
determinan la secuencia de microoperaciones utilizadas para
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 325

implementar la instrucción en la canalización de ejecución. Este mecanismo de decodificador


cierra la brecha entre un antiguo conjunto de instrucciones CISC y una ruta de datos RISC
moderna.
Las microoperaciones decodificadas se introducen en el caché de micro-operaciones, a la que
Intel se refiere como caché de instrucciones L0 (nivel 0). La caché de microoperaciones es similar a una
caché de instrucciones tradicional, pero tiene mucho espacio adicional para almacenar las secuencias
de microoperaciones que producen las instrucciones individuales. Cuando se almacenan en caché las
microoperaciones decodificadas en lugar de las instrucciones originales, no es necesario decodificar la
instrucción en ejecuciones posteriores. A primera vista, podría pensar que Intel hizo esto para acelerar
la canalización (y de hecho acelera el proceso de producción de una instrucción), pero Intel afirma que
la caché micro-op se agregó para reducir el consumo de energía del front-end. Con la memoria caché
de microoperaciones en su lugar, el resto de la interfaz duerme en un modo de bajo consumo
desbloqueado el 80% del tiempo.
La predicción de rama también se realiza en el front-end. El predictor de bifurcaciones es
responsable de adivinar cuándo el flujo del programa se rompe de la búsqueda secuencial pura,
y debe poder hacerlo mucho antes de que se ejecuten las instrucciones de bifurcación. El
predictor de ramas en el Core i7 es bastante notable. Desafortunadamente para nosotros, los
detalles de los predictores de rama de procesador son secretos muy guardados para la mayoría
de los diseños. Esto se debe a que el rendimiento del predictor suele ser el componente más
crítico de la velocidad general del diseño. Cuanta más precisión de predicción puedan obtener los
diseñadores de cada micrómetro cuadrado de silicio, mejor será el rendimiento de todo el
diseño. Como tal, las empresas ocultan estos secretos bajo llave e incluso amenazan a los
empleados con un proceso penal si alguno de ellos decide compartir estas joyas del
conocimiento. Sin embargo, basta con decir que todos ellos hacen un seguimiento del camino
que tomaron las ramas anteriores y lo usan para hacer predicciones. Son los detalles de lo que
registran y cómo almacenan y buscan la información lo que es de alto secreto. Después de todo,
si tuviera una manera fantástica de predecir el futuro, probablemente no la pondría en la Web
para que todo el mundo la viera.
Las instrucciones se envían desde la memoria caché de microoperaciones al planificador fuera de
servicio en el orden dictado por el programa, pero no necesariamente se emiten en el orden del
programa. Cuando se encuentra una micro-operación que no se puede ejecutar, el planificador la
retiene pero continúa procesando el flujo de instrucciones para emitir instrucciones subsiguientes
cuyos recursos (registros, unidades funcionales, etc.) están disponibles. El cambio de nombre del
registro también se realiza aquí para permitir que las instrucciones con una dependencia de WAR o
WAW continúen sin demora.
Aunque las instrucciones se pueden emitir fuera de orden, el requisito de interrupciones
precisas de la arquitectura Core i7 significa que las instrucciones ISA deben retirarse (es decir,
hacer visibles sus resultados) en el orden original del programa. La unidad de retiro se encarga
de esta tarea.
En el back-end del procesador tenemos las unidades de ejecución, que ejecutan las
instrucciones enteras, de coma flotante y especializadas. Existen varias unidades de ejecución
que se ejecutan en paralelo. Obtienen sus datos del archivo de registro y del caché de datos L1.
326 EL NIVEL DE MICROARQUITECTURA CAP. 4

El oleoducto Sandy Bridge del Core i7

La figura 4-47 es una versión simplificada de la microarquitectura de Sandy Bridge que muestra la tubería.
En la parte superior está la interfaz, cuyo trabajo es recuperar instrucciones de la memoria y prepararlas para
su ejecución. El front-end recibe nuevas instrucciones x86 desde la caché de instrucciones L1. Los decodifica en
microoperaciones para su almacenamiento en la caché de microoperaciones, que contiene aproximadamente
1.5K microoperaciones. Una caché de microoperaciones de este tamaño funciona de manera comparable a una
caché L0 convencional de 6 KB. El caché de microoperaciones contiene grupos de seis microoperaciones en una
sola línea de seguimiento. Por más tiempo
sucesivas de microoperaciones, se pueden enlazar varias líneas de seguimiento.

Nivel 1
caché inst
Rama
vaticinador/
Parte delantera
Unidad de decodificación Rama
fin objetivo
buffer
Compartir
Microoperación
Caché L3
cache Nivel 2
unificado
cache
Fuera- Asignar / cambiar el nombre de la unidad

de-
pedido
control No- Memoria
programador de memoria planificador

ALU 1 ALU 2 ALU 3 Tienda Carga 1 Carga 2

Nivel 1
datos
Unidad de retiro cache

Figura 4-47. Una vista simplificada de la ruta de datos del Core i7.

Si la unidad de decodificación golpea una rama condicional, busca su dirección


predicha en el Predictor de ramas. El predictor de bifurcaciones contiene el historial de
bifurcaciones encontradas en el pasado y utiliza este historial para adivinar si se tomará o
no una bifurcación condicional la próxima vez que se encuentre. Aquí es donde se utiliza el
algoritmo de alto secreto.
Si la instrucción de bifurcación no está en la tabla, se utiliza la predicción estática. Se
asume que una rama hacia atrás es parte de un bucle y se asume que se toma. La precisión
de estas predicciones estáticas es extremadamente alta. Se supone que una bifurcación
directa es parte de unasi declaración y se supone que no se tomará. La precisión de estas
predicciones estáticas es mucho menor que la de las ramas hacia atrás.
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 327

Para una rama tomada el BTB (búfer de destino de rama) se consulta para determinar la
dirección de destino. El BTB contiene la dirección de destino de la sucursal la última vez que se
tomó. La mayoría de las veces esta dirección es correcta (de hecho, siempre es correcta para
ramas con un desplazamiento constante). Ramas indirectas, como las que utilizan las llamadas a
funciones virtuales y C ++cambiar declaraciones, van a muchas direcciones, y la BTB puede
predecirlas erróneamente.
La segunda parte de la canalización, la lógica de control fuera de orden, se alimenta desde la memoria caché de
microoperaciones. A medida que cada micro-operación ingresa desde el extremo frontal, hasta cuatro por ciclo, el
unidad de asignación / cambio de nombre lo registra en una tabla de 168 entradas llamada ROBAR
(Búfer de reorden). Esta entrada realiza un seguimiento del estado de la microoperación hasta que se
retira. La unidad de asignación / cambio de nombre luego verifica si los recursos que necesita el
microop están disponibles. Si es así, la microoperación se pone en cola para su ejecución en uno de los
planificador colas. Se mantienen colas separadas para microoperaciones de memoria y no memoria. Si
una microoperación no se puede ejecutar, se retrasa, pero las microoperaciones posteriores se
procesan, lo que lleva a una ejecución desordenada de las microoperaciones. Esta estrategia fue
diseñada para mantener todas las unidades funcionales lo más ocupadas posible. Hasta 154
instrucciones pueden estar en vuelo en cualquier instante, y hasta 64 de estas pueden cargarse desde la
memoria y hasta 36 pueden almacenarse en la memoria.
A veces, una micro-operación se detiene porque necesita escribir en un registro que está
siendo leído o escrito por una micro-operación anterior. Estos conflictos se denominan
dependencias WAR y WAW, respectivamente, como vimos anteriormente. Al cambiar el nombre
del destino de la nueva microoperación para permitirle escribir su resultado en uno de los 160
registros temporales en lugar de en el destino previsto, pero aún ocupado, es posible programar
la microoperación para que se ejecute de inmediato. . Si no hay ningún registro de scratch
disponible, o el micro-op tiene una dependencia RAW (que nunca se puede tapar), el asignador
anota la naturaleza del problema en la entrada ROB. Cuando todos los recursos necesarios estén
disponibles más tarde, la microoperación se coloca en una de las colas del planificador.
Las colas del programador envían microoperaciones a las seis unidades funcionales cuando están
listas para ejecutarse. Las unidades funcionales son las siguientes:

1. ALU 1 y la unidad de multiplicación de coma flotante.

2. ALU 2 y la unidad de suma / resta de coma flotante.

3. ALU 3 y unidad de procesamiento de ramas y comparaciones de coma flotante.

4. Almacene las instrucciones.

5. Cargue las instrucciones 1.

6. Cargue las instrucciones 2.

Dado que los programadores y las ALU pueden procesar una operación por ciclo, un Core i7 de 3 GHz
tiene el rendimiento del programador para emitir 18 mil millones de operaciones por segundo; sin
embargo, en realidad, el procesador nunca alcanzará este nivel de rendimiento. Dado que el front-end
suministra como máximo cuatro microoperaciones por ciclo, seis microoperaciones solo pueden
328 EL NIVEL DE MICROARQUITECTURA CAP. 4

emitido en ráfagas cortas ya que pronto las colas del planificador se vaciarán. Además, las
unidades de memoria necesitan cuatro ciclos para procesar sus operaciones, por lo que podrían
contribuir al rendimiento máximo de ejecución solo en pequeñas ráfagas. A pesar de no poder
saturar por completo los recursos de ejecución, las unidades funcionales sí brindan una
capacidad de ejecución significativa, y es por eso que el control de desorden se toma tantas
molestias para encontrar trabajo que hacer.
Las tres ALU enteras no son idénticas. ALU 1 puede realizar todas las operaciones
aritméticas y lógicas y multiplica y divide. ALU 2 solo puede realizar operaciones aritméticas
y lógicas. ALU 3 puede realizar operaciones aritméticas y lógicas y resolver ramas. Del
mismo modo, las dos unidades de coma flotante tampoco son idénticas. El primero puede
realizar operaciones aritméticas de punto flotante, incluidas multiplicaciones, mientras que
el segundo solo puede realizar sumas, restas y movimientos de punto flotante.
Las unidades ALU y de coma flotante son alimentadas por un par de archivos de registro de 128
entradas, uno para números enteros y otro para números de coma flotante. Estos proporcionan todos
los operandos para que se ejecuten las instrucciones y proporcionan un repositorio de resultados.
Debido al cambio de nombre de los registros, ocho de ellos contienen los registros visibles a nivel ISA (
EAX, EBX, ECX, EDX, etc.), pero cuáles ocho contienen los valores '' reales '' varían con el tiempo a medida
que cambia el mapeo durante la ejecución.
La arquitectura Sandy Bridge introdujo Advanced Vector Extensions (AVX), que admite
operaciones vectoriales paralelas de datos de 128 bits. Las operaciones vectoriales incluyen
vectores de punto flotante y enteros, y esta nueva extensión ISA representa un aumento de dos
veces en el tamaño de los vectores ahora admitidos en comparación con las extensiones ISA SSE
y SSE2 anteriores. ¿Cómo implementa la arquitectura operaciones de 256 bits con solo rutas de
datos y unidades funcionales de 128 bits? Coordina inteligentemente dos puertos del
programador de 128 bits para producir una sola unidad funcional de 256 bits.
La caché de datos L1 está estrechamente acoplada al extremo posterior de la tubería Sandy
Bridge. Es una caché de 32 KB y contiene enteros, números de punto flotante y otros tipos de
datos. A diferencia del caché de microoperaciones, no se decodifica de ninguna manera. Solo
tiene una copia de los bytes en la memoria. La caché de datos L1 es una caché asociativa de 8 vías
con 64 bytes por línea de caché. Es un caché de escritura diferida, lo que significa que cuando se
modifica una línea de caché, el bit sucio de esa línea se establece y los datos se copian de nuevo
al caché L2 cuando se expulsan del caché de datos L1. La caché puede manejar dos operaciones
de lectura y una de escritura por ciclo de reloj. Estos accesos múltiples se implementan usando
bancario, que divide el caché en subcachés separados (8 en el caso de Sandy Bridge). Siempre
que los tres accesos sean a bancos separados, pueden proceder en conjunto; de lo contrario,
todos los accesos bancarios en conflicto, excepto uno, tendrán que pararse. Cuando una palabra
necesaria no está presente en la caché L1, se envía una solicitud a la caché L2, que responde
inmediatamente o recupera la línea de la caché de la caché L3 compartida y luego responde.
Hasta diez solicitudes de la caché L1 a la caché L2 pueden estar en curso en cualquier momento.

Debido a que las microoperaciones se ejecutan fuera de orden, las tiendas en la caché L1 no están
permitidas hasta que se hayan retirado todas las instrucciones que preceden a una tienda en particular. los
unidad de jubilación tiene el trabajo de retirar las instrucciones, en orden, y realizar un seguimiento
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 329

de donde está. Si ocurre una interrupción, las instrucciones que aún no se han retirado se abortan, por lo que
el Core i7 tiene "interrupciones precisas", de modo que tras una interrupción, todas las instrucciones hasta
cierto punto se han completado y ninguna instrucción más allá de eso tiene ningún efecto.
Si se ha retirado una instrucción de almacenamiento, pero las instrucciones anteriores aún
están en curso, la caché L1 no se puede actualizar, por lo que los resultados se colocan en un
búfer especial de almacenamiento pendiente. Este búfer tiene 36 entradas, correspondientes a
las 36 tiendas que podrían estar en ejecución a la vez. Si una carga posterior intenta leer los
datos almacenados, se puede pasar del búfer de almacenamiento pendiente a la instrucción,
aunque aún no esté en la caché de datos L1. Este proceso se llamatienda a carga reenvío. Si bien
este mecanismo de reenvío puede parecer sencillo, en la práctica es bastante complicado de
implementar porque es posible que las tiendas intermedias aún no hayan calculado sus
direcciones. En este caso, la microarquitectura no puede saber definitivamente qué almacén en el
búfer de almacenamiento producirá el valor necesario. El proceso de determinar qué tienda
proporciona el valor para una carga se llamadesambiguación.
Debería estar claro a estas alturas que el Core i7 tiene una microarquitectura altamente
compleja cuyo diseño fue impulsado por la necesidad de ejecutar el antiguo conjunto de
instrucciones Pentium en un núcleo RISC moderno y altamente canalizado. Logra este objetivo al
dividir las instrucciones de Pentium en microoperaciones, almacenarlas en caché y enviarlas a la
tubería cuatro a la vez para su ejecución en un conjunto de ALU capaces de ejecutar hasta seis
microoperaciones por ciclo en condiciones óptimas. Las microoperaciones se ejecutan fuera de
servicio pero se retiran en orden, y los resultados se almacenan en las cachés L1 y L2 en orden.

4.6.2 La microarquitectura de la CPU OMAP4430

En el corazón del sistema en un chip OMAP4430 se encuentran dos procesadores


ARM Cortex A9. El Cortex A9 es una microarquitectura de alto rendimiento que
implementa el conjunto de instrucciones ARM (versión 7). El procesador fue diseñado
por ARM Ltd. y se incluye con ligeras variaciones en una amplia variedad de
dispositivos integrados. ARM no fabrica el procesador, solo suministra el diseño a los
fabricantes de silicio que desean incorporarlo en su diseño de sistema en un chip
(Texas Instruments, en este caso).
El procesador Cortex A9 es una máquina de 32 bits, con registros de 32 bits y una ruta de datos de
32 bits. Como la arquitectura interna, el bus de memoria tiene 32 bits de ancho. A diferencia del Core i7,
el Cortex A9 es una verdadera arquitectura RISC, lo que significa que no necesita un mecanismo
complejo para convertir antiguas instrucciones CISC en microoperaciones para su ejecución. De hecho,
las instrucciones centrales ya son instrucciones ARM similares a microoperaciones. Sin embargo, en los
últimos años se han agregado gráficos e instrucciones multimedia más complejas, que requieren
instalaciones especiales de hardware para su ejecución.

Descripción general de la microarquitectura Cortex A9 de OMAP4430

El diagrama de bloques de la microarquitectura Cortex A9 se muestra en la figura 4-48.


En general, es mucho más simple que la microarquitectura Sandy Bridge del Core i7 porque
tiene una arquitectura ISA más simple de implementar. Sin embargo, algunos de
330 EL NIVEL DE MICROARQUITECTURA CAP. 4

los componentes clave son similares a los que se utilizan en el Core i7. Las similitudes se deben
principalmente a la tecnología, las limitaciones de energía y la economía. Por ejemplo, ambos
diseños emplean una jerarquía de caché multinivel para cumplir con las estrictas restricciones de
costos de las aplicaciones integradas típicas; sin embargo, el último nivel del sistema de memoria
caché de Cortex A9 (L2) tiene un tamaño de solo 1 MB, significativamente más pequeño que el
Core i7 que admite cachés de último nivel (L3) de hasta 20 MB. Las diferencias, por el contrario,
se deben principalmente a la diferencia entre tener que cerrar la brecha entre un conjunto de
instrucciones CISC antiguo y un núcleo RISC moderno y no tener que hacerlo.

A LPDDR2
memoria

Predictor de rama Nivel 1 Bucle rápido


caché inst mira a un lado Sistema
/ interfaz
Destino de la rama
Dirección Memoria
Instrucción unidad de emisión /
cache controlador
decodificador / renombrador

Nivel 2
Instrucción Nivel 1 unificado
cola caché de datos cache

ALU FPU
Unidad de almacenamiento de carga /

almacenar búfer

Jubilación

Figura 4-48. El diagrama de bloques de la corteza del OMAP4430 Microarquitectura A9.

En la parte superior de la figura 4-48 se encuentra la caché de instrucciones asociativas de 4 vías de 32 KB,
que utiliza líneas de caché de 32 bytes. Dado que la mayoría de las instrucciones ARM son de 4 bytes, hay
espacio para aproximadamente 8K instrucciones aquí en este caché, bastante más grande que el caché micro-
op del Core i7.
los unidad de emisión de instrucciones prepara hasta cuatro instrucciones para su
ejecución por ciclo de reloj. Si hay una falta en la caché L1, se emitirán menos instrucciones.
Cuando se encuentra una rama condicional, se consulta un predictor de rama con entradas de 4K
para predecir si se tomará o no la rama. Si se predice, se consulta la caché de dirección de
destino de rama de entrada de 1K para la dirección de destino pronosticada. Además, si la
interfaz detecta que el programa está ejecutando un bucle cerrado (es decir, un bucle pequeño
no anidado), lo cargará en la caché de búsqueda lateral de bucle rápido. Esta optimización
acelera la búsqueda de instrucciones y reduce la energía, ya que las cachés y los predictores de
rama pueden estar en un modo de suspensión de baja energía mientras se ejecuta el ciclo
cerrado.
La salida de la unidad de emisión de instrucciones fluye hacia los decodificadores, que
determinan qué recursos y entradas necesitan las instrucciones. Como el Core i7,
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 331

las instrucciones se renombran después de la decodificación para eliminar los peligros de WAR que
pueden ralentizar la ejecución fuera de orden. Después de cambiar el nombre, las instrucciones se
colocan en la cola de envío de instrucciones, que las emitirá cuando sus entradas estén listas para las
unidades funcionales, potencialmente fuera de servicio.
La cola de envío de instrucciones envía instrucciones a las unidades funcionales, como se
muestra en la figura 4-48. La unidad de ejecución de enteros contiene dos ALU, así como una
canalización corta para instrucciones de bifurcación. Allí también se incluye el archivo de registro
físico, que contiene registros ISA y algunos registros temporales. La canalización Cortex A9 puede
contener opcionalmente también uno más motores de cómputo, que actúan como unidades
funcionales adicionales. ARM admite un motor de cálculo para el cálculo de punto flotante
llamadoVFP y cálculo vectorial de SIMD entero llamado NEÓN.
La unidad de carga / almacenamiento maneja varias instrucciones de carga y almacenamiento.
Tiene rutas a la caché de datos y al búfer de almacenamiento. loscaché de datos es una caché de datos
L1 asociativa de 4 vías tradicional de 32 KB que utiliza un tamaño de línea de 32 bytes. losalmacenar
búfer contiene las tiendas que aún no han escrito su valor en la caché de datos (en el momento de la
jubilación). Una carga que se ejecuta primero intentará recuperar su valor del búfer de
almacenamiento, utilizando el reenvío de almacenamiento a carga como el del Core i7. Si el valor no
está disponible en el búfer de la tienda, lo obtendrá de la caché de datos. Un posible resultado de la
ejecución de una carga es una indicación del búfer de la tienda de que debe esperar, porque una tienda
anterior con una dirección desconocida está bloqueando su ejecución. En caso de que se pierda el
acceso a la caché de datos L1, el bloque de memoria se obtendrá de la caché L2 unificada. En
determinadas circunstancias, el Cortex A9 también realiza una captación previa de hardware de la caché
L2 a la caché de datos L1, con el fin de mejorar el rendimiento de las cargas y las tiendas.

El chip OMAP 4430 también contiene lógica para controlar el acceso a la memoria. Esta
lógica se divide en dos partes: la interfaz del sistema y el controlador de memoria. La interfaz del
sistema interactúa con la memoria a través de un bus LPDDR2 de 32 bits de ancho. Todas las
solicitudes de memoria al mundo exterior pasan a través de esta interfaz. El bus LPDDR2 admite
una dirección de 26 bits (palabra, no byte) a 8 bancos que devuelven una palabra de datos de 32
bits. En teoría, la memoria principal puede ser de hasta 2 GB por canal LPDDR2. El OMAP4430
tiene dos de ellos, por lo que puede direccionar hasta 4 GB de RAM externa.
El controlador de memoria asigna direcciones virtuales de 32 bits a direcciones físicas de 32
bits. El Cortex A9 admite memoria virtual (que se describe en el capítulo 6), con un tamaño de
página de 4 KB. Para acelerar el mapeo, tablas especiales, llamadasTLB (Buffers Lookaside de
traducción), se proporcionan para comparar la dirección virtual actual a la que se hace
referencia con aquellas a las que se hace referencia en el pasado reciente. Se proporcionan dos
de estas tablas para mapear direcciones de instrucciones y datos.

El oleoducto Cortex A9 de OMAP4430

El Cortex A9 tiene una tubería de 11 etapas, ilustrada de forma simplificada en la figura 4-49.
Las 11 etapas están designadas por nombres de etapas cortos que se muestran en el lado
izquierdo de la figura. Examinemos ahora brevemente cada etapa. losFe1 (Obtener
332 EL NIVEL DE MICROARQUITECTURA CAP. 4

# 1) la etapa está al comienzo de la tubería. Es aquí donde la dirección de la siguiente instrucción


que se va a buscar se usa para indexar la caché de instrucciones e iniciar una predicción de
bifurcación. Normalmente, esta dirección es la que sigue a la instrucción anterior. Sin embargo,
este orden secuencial puede romperse por una variedad de razones, como cuando una
instrucción anterior es una rama que se ha predicho que se tomará, o una trampa o interrupción
necesita ser reparada. Debido a que la búsqueda de instrucciones y la predicción de
bifurcaciones toman más de un ciclo, laFeLa etapa 2 (Fetch # 2) proporciona tiempo adicional
para llevar a cabo estas operaciones. En elFe3 (Fetch # 3) las instrucciones obtenidas (hasta
cuatro) se introducen en la cola de instrucciones.
los Delaware1 y Delaware2 etapas (Decodificar) decodifican las instrucciones. Este paso
determina qué instrucciones de entrada necesitarán (registros y memoria) y qué recursos
requerirán para ejecutarse (unidades funcionales). Una vez que se completa la decodificación, las
instrucciones ingresan alRe (Renombrar) etapa en la que se cambia el nombre de los registros a
los que se accede para eliminar los peligros WAR y WAW durante la ejecución fuera de orden.
Esta etapa contiene la tabla de cambio de nombre que registra qué registro físico contiene
actualmente todos los registros arquitectónicos. Con esta tabla, se puede renombrar fácilmente
cualquier registro de entrada. El registro de salida debe recibir un nuevo registro físico, que se
toma de un grupo de registros físicos no utilizados. El registro físico asignado estará en uso por
la instrucción hasta que se retire.
A continuación, las instrucciones ingresan Iss (Emisión de instrucciones), donde se colocan
en la cola de emisión de instrucciones. La cola de problemas busca instrucciones cuyas entradas
estén listas. Cuando está listo, se adquieren sus entradas de registro (del archivo de registro
físico o del bus de derivación), y luego la instrucción se envía a las etapas de ejecución. Al igual
que el Core i7, el Cortex A9 potencialmente emite instrucciones fuera del orden del programa. Se
pueden emitir hasta cuatro instrucciones en cada ciclo. La elección de instrucciones está limitada
por las unidades funcionales disponibles.
los Ex (Ejecutar) es donde se ejecutan realmente las instrucciones. La mayoría de las
instrucciones aritméticas, booleanas y de turno utilizan las ALU enteras y se completan en un
ciclo. Las cargas y las tiendas tardan dos ciclos (si llegan a la caché L1) y las multiplicaciones
tardan tres ciclos. losEx Las etapas contienen múltiples unidades funcionales, que son:

1. Entero ALU 1.
2. Entero ALU 2.
3. Multiplica la unidad.

4. ALU vectorial de punto flotante y SIMD (opcional con soporte VFP y


NEON).
5. Cargue y almacene la unidad.

Las instrucciones de bifurcación condicionales también se procesan en la primera Ex etapa


y se determina su dirección (rama / sin rama). En caso de una predicción errónea, se envía
una señal alFe1 etapa y la tubería anulada.
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 333

Escenario

Nivel 1 Bucle rápido


Fe1 caché inst mira a un lado

Rama
Fe2 vaticinador Instrucción unidad de emisión

Instrucción
Fe3
cola

Id1
Instrucción
descodificación

Id2

Re Cambio de nombre de instrucciones

Iss Cola de problemas de instrucciones

Ex1 ALU 1 ALU 2 Unidad de almacenamiento de carga

FPU / Nivel 1
Ex2 Mult
NEÓN caché de datos

Nivel 2
Ex3 unificado
cache

...
WB Jubilación

Figura 4-49. Una representación simplificada de la tubería Cortex A9 de OMAP4430.

Después de completar la ejecución, las instrucciones ingresan al WB (WriteBack) donde cada


instrucción actualiza el archivo de registro físico inmediatamente. Potencialmente más tarde,
cuando la instrucción sea la más antigua en vuelo, escribirá el resultado de su registro en el
archivo de registro arquitectónico. Si se produce una trampa o una interrupción, son estos
valores, no los de los registros físicos, los que se hacen visibles. El acto de almacenar el registro
en el archivo arquitectónico es equivalente a la jubilación en el Core i7. Además, en el
WB etapa, las instrucciones de la tienda ahora completan la escritura de sus resultados en la caché de datos L1.
334 EL NIVEL DE MICROARQUITECTURA CAP. 4

Esta descripción del Cortex A9 está lejos de ser completa, pero debería dar una idea
razonable de cómo funciona y en qué se diferencia de la microarquitectura Core i7.

4.6.3 La microarquitectura del microcontrolador ATmega168

Nuestro último ejemplo de microarquitectura es el Atmel ATmega168, que se muestra


en la figura 4-50. Éste es considerablemente más simple que el del Core i7 y el OMAP4430.
La razón es que el chip debe ser muy pequeño y barato para servir al mercado del diseño
integrado. Como tal, el objetivo principal del diseño del ATmega168 era hacer que el chip
fuera barato, no rápido. Barato y Simple son buenos amigos. Barato y
Rápido no son buenos amigos.

Bus principal de 8 bits

Destello Programa Estado


programa encimera y control
memoria
Interrumpir

32 × 8 unidad

general
Instrucción
objetivo SPI
Registrarse
registros unidad

Perro guardián
Instrucción Temporizador
Direccionamiento indirecto
Direccionamiento directo

descifrador
ALU
Cosa análoga

comparador
Líneas de control

Módulo de E / S 1

Datos
SRAM Módulo de E / S 2

Módulo de E / S 3

EEPROM

Figura 4-50. La microarquitectura del ATmega168.

El corazón del ATmega168 es el bus principal de 8 bits. Se adjuntan a él los registros y bits de
estado, ALU, memoria y dispositivos de E / S. Vamos a describirlos ahora brevemente. El archivo
de registro contiene 32 registros de 8 bits, que se utilizan para almacenar valores de programa
temporales. losestado y control El registro contiene los códigos de condición de la última
operación de ALU (es decir, signo, desbordamiento, negativo, cero y acarreo), más un bit
SEGUNDO. 4.6 EJEMPLOS DEL NIVEL DE MICROARQUITECTURA 335

que indica si hay una interrupción pendiente. loscontador de programa contiene la dirección de
la instrucción que se está ejecutando actualmente. Para realizar una operación ALU, primero se
leen los operandos del registro y se envían a la ALU. La salida ALU se puede escribir en cualquiera
de los registros grabables a través del bus principal.
El ATmega168 tiene varias memorias para datos e instrucciones. La SRAM de datos es de 1
KB, demasiado grande para direccionarse completamente con una dirección de 8 bits en el bus
principal. Por lo tanto, la arquitectura AVR permite construir direcciones con un par secuencial de
registros de 8 bits, produciendo así una dirección de 16 bits que admite hasta 64 KB de memoria
de datos. La EEPROM proporciona hasta 1 KB de almacenamiento no volátil donde los programas
pueden escribir variables que necesitan sobrevivir a un corte de energía.
Existe un mecanismo similar para abordar la memoria del programa, pero 64 KB de código es
demasiado pequeño, incluso para sistemas integrados de bajo costo. Para permitir que se direccione
más memoria de instrucciones, la arquitectura AVR define tres registros de página RAM (RAMPX, RAMPY
y RAMPZ), cada uno de 8 bits de ancho. El registro de la página RAM se concatena con un par de
registros de 16 bits para producir una dirección de programa de 24 bits, lo que permite 16 MB de
espacio de direcciones de instrucciones.
Deténgase a pensar en eso por un minuto. 64 KB de código es demasiado pequeño para un
microcontrolador que podría alimentar un juguete o un pequeño electrodoméstico. En 1964, IBM lanzó
el System 360 Model 30, que tenía 64 KB de memoria total (sin trucos para actualizarlo). Se vendió por
250.000 dólares, lo que equivale aproximadamente a 2 millones de dólares en dólares de hoy. El
ATmega168 cuesta alrededor de $ 1, menos si compra muchos a la vez. Si consulta, digamos, la lista de
precios de Boeing, descubrirá que los precios de los aviones no se han reducido en un factor de 250.000
en los últimos 50 años aproximadamente. Tampoco tienen los precios de los coches ni de los televisores
ni de nada más que de los ordenadores.
Además, el ATmega168 tiene un controlador de interrupciones en chip, una interfaz de
puerto serie (SPI) y temporizadores, que son esenciales para las aplicaciones en tiempo
real. También hay tres puertos de E / S digitales de 8 bits, que permiten que el ATmega168
controle hasta 24 botones externos, luces, sensores, actuadores, etc. Es la presencia de los
temporizadores y los puertos de E / S más que cualquier otra cosa lo que hace posible usar
el ATmega168 para aplicaciones integradas sin ningún chip adicional.
El ATmega168 es un procesador síncrono, y la mayoría de las instrucciones toman un ciclo
de reloj, aunque algunas toman más. El procesador está en canalización, de modo que mientras
se recupera una instrucción, se ejecuta la instrucción anterior. La canalización consta de solo dos
etapas, sin embargo, recuperar y ejecutar. Para ejecutar instrucciones en un ciclo, el ciclo de reloj
debe acomodar la lectura del registro del archivo de registro, seguido de la ejecución de la
instrucción en la ALU, seguido de la escritura del registro de nuevo en el archivo de registro.
Debido a que todas estas operaciones ocurren en un ciclo de reloj, no hay necesidad de lógica de
derivación o detección de bloqueo. Las instrucciones del programa se ejecutan en orden, en un
ciclo y sin superponerse con otras instrucciones.
Si bien podríamos entrar en más detalles sobre el ATmega168, la descripción anterior y la
Fig. 4-50 dan la idea básica. El ATmega168 tiene un solo bus principal (para reducir el área del
chip), un conjunto heterogéneo de registros y una variedad de memorias y dispositivos de E / S
que cuelgan del bus principal. En cada ciclo de ruta de datos, dos operandos son
336 EL NIVEL DE MICROARQUITECTURA CAP. 4

leer desde el archivo de registro y ejecutar a través de la ALU y los resultados se almacenan de nuevo en un
registro, al igual que en las computadoras más modernas.

4.7 COMPARACIÓN DEL I7, OMAP4430 Y ATMEGA168

Nuestros tres ejemplos son muy diferentes, sin embargo, incluso ellos exhiben una cierta
cantidad de puntos en común. El Core i7 tiene un antiguo conjunto de instrucciones CISC que a
los ingenieros de Intel les encantaría lanzar a la Bahía de San Francisco, excepto que al hacerlo
violaría las leyes de contaminación del agua de California. El OMAP4430 es un diseño RISC puro,
con un conjunto de instrucciones delgado y medio. El ATmega168 es un procesador simple de 8
bits para aplicaciones integradas. Sin embargo, el corazón de cada uno de ellos es un conjunto de
registros y una o más ALU que realizan operaciones aritméticas y booleanas simples en
operandos de registro.
A pesar de sus obvias diferencias externas, el Core i7 y el OMAP4430 tienen unidades
de ejecución bastante similares. Ambas unidades de ejecución aceptan microoperaciones
que contienen un código de operación, dos registros de origen y un registro de destino.
Ambos pueden ejecutar una microoperación en un ciclo. Ambos tienen pipelines profundos,
predicción de ramificaciones y cachés I y D divididos.
Esta similitud interna no es un accidente o incluso se debe a la interminable búsqueda de
trabajo de los ingenieros de Silicon Valley. Como vimos con nuestros ejemplos Mic-3 y Mic-4, es
fácil y natural construir una ruta de datos canalizada que toma dos registros fuente, los ejecuta a
través de una ALU y almacena los resultados en un registro. La Figura 4-34 muestra esta
canalización gráficamente. Con la tecnología actual, este es el diseño más efectivo.
La principal diferencia entre el Core i7 y el OMAP4430 es cómo pasan de su conjunto de
instrucciones ISA a la unidad de ejecución. El Core i7 tiene que dividir sus instrucciones CISC para
ponerlas en el formato de tres registros que necesita la unidad de ejecución. De eso se trata la
interfaz de la figura 4-47: piratear grandes instrucciones para convertirlas en microoperaciones
agradables y ordenadas. El OMAP4430 no tiene que hacer nada porque sus instrucciones ARM
nativas ya son microoperaciones agradables y ordenadas. Ésta es la razón por la que la mayoría
de las ISA nuevas son del tipo RISC, para proporcionar una mejor coincidencia entre el conjunto
de instrucciones ISA y el motor de ejecución interno.
Es instructivo comparar nuestro diseño final, el Mic-4, con estos dos ejemplos del mundo
real. El Mic-4 se parece más al Core i7. Ambos tienen la función de interpretar un conjunto de
instrucciones ISA no RISC. Ambos hacen esto dividiendo las instrucciones ISA en
microoperaciones con un código de operación, dos registros de origen y un registro de destino.
En ambos casos, las microoperaciones se depositan en una cola para su posterior ejecución. El
Mic-4 tiene un estricto problema en orden, ejecución en orden, diseño de retiro en orden,
mientras que el Core i7 tiene una política de emisión en orden, ejecución fuera de orden, retiro
en orden.
El Mic-4 y el OMAP4430 no son realmente comparables en absoluto porque el OMAP4430
tiene instrucciones RISC (es decir, microoperaciones de tres registros) como su ISA
SEGUNDO. 4,7 COMPARACIÓN DEL I7, OMAP4430 Y ATMEGA168 337

conjunto de instrucciones. No es necesario romperlos. Se pueden ejecutar tal cual, cada uno en
un único ciclo de ruta de datos.
A diferencia del Core i7 y el OMAP4430, el ATmega168 es una máquina sencilla. Es
más parecido a RISC que a CISC porque la mayoría de sus sencillas instrucciones se
pueden ejecutar en un ciclo de reloj y no es necesario descomponerlas. No tiene
canalización ni almacenamiento en caché, y tiene problemas en orden, ejecución en
orden y retiro en orden. En su simplicidad, se parece mucho al Mic-1.

4.8 RESUMEN

El corazón de cada computadora es la ruta de datos. Contiene algunos registros, uno,


dos o tres buses y una o más unidades funcionales como ALU y cambiadores. El bucle de
ejecución principal consiste en obtener algunos operandos de los registros y enviarlos a
través de los buses a la ALU y otra unidad funcional para su ejecución. Luego, los resultados
se almacenan nuevamente en los registros.
La ruta de datos se puede controlar mediante un secuenciador que obtiene
microinstrucciones de una tienda de control. Cada microinstrucción contiene bits que controlan
la ruta de datos durante un ciclo. Estos bits especifican qué operandos seleccionar, qué operación
realizar y qué hacer con los resultados. Además, cada microinstrucción especifica su sucesor,
normalmente de forma explícita al contener su dirección. Algunas microinstrucciones modifican
esta dirección base colocando bits en la dirección OR antes de usarla.
La máquina IJVM es una máquina de pila con códigos de operación de 1 byte que empujan
palabras a la pila, extraen palabras de la pila y combinan (por ejemplo, agregan) palabras en la
pila. Se dio una implementación microprogramada para la microarquitectura Mic-1. Al agregar
una unidad de búsqueda de instrucciones para precargar los bytes en el flujo de instrucciones, se
podrían eliminar muchas referencias al contador del programa y la máquina se aceleró
enormemente.
Hay muchas formas de diseñar el nivel de microarquitectura. Existen muchas
compensaciones, incluidos diseños de dos buses frente a tres buses, campos de microinstrucción
codificados o decodificados, presencia o ausencia de captación previa, tuberías poco profundas o
profundas, y mucho más. El Mic-1 es una máquina simple, controlada por software, con ejecución
secuencial y sin paralelismo. Por el contrario, el Mic-4 es una microarquitectura muy paralela con
una tubería de siete etapas.
El rendimiento se puede mejorar de diversas formas. La memoria caché es una de las principales.
Los cachés de asignación directa y los cachés asociativos de conjuntos se utilizan comúnmente para
acelerar las referencias de memoria. La predicción de rama, tanto estática como dinámica, es
importante, al igual que la ejecución fuera de orden y la ejecución especulativa.
Nuestras tres máquinas de ejemplo, Core i7, OMAP4430 y ATmega168, tienen
microarquitecturas que no son visibles para los programadores de lenguaje ensamblador de ISA.
El Core i7 tiene un esquema complejo para convertir las instrucciones ISA en microoperaciones,
almacenarlas en caché y alimentarlas en un núcleo RISC superescalar para ejecución fuera de
orden, cambio de nombre de registros y todos los demás trucos del libro para obtener
338 EL NIVEL DE MICROARQUITECTURA CAP. 4

la última caída posible de velocidad del hardware. El OMAP4430 tiene una tubería
profunda, pero además es relativamente simple, con problemas en orden, ejecución
en orden y retiro en orden. El ATmega168 es muy simple, con un bus principal sencillo
y sencillo al que se adjuntan un puñado de registros y una ALU.

PROBLEMAS

1. ¿Cuáles son los cuatro pasos que utilizan las CPU para ejecutar instrucciones?

2. En la figura 4-6, el registro del bus B está codificado en un campo de 4 bits, pero el bus C se representa como un
mapa de bits. ¿Por qué?

3. En la Fig. 4-6 hay un cuadro etiquetado como "Bit alto". Proporcione un diagrama de circuito para ello.

4. Cuando el JMPC campo en una microinstrucción está habilitado, MBR está ORed con SIGUIENTE
DIRECCION para formar la dirección de la siguiente microinstrucción. ¿Hay alguna
circunstancia en la que tenga sentido tenerSIGUIENTE DIRECCION ser 0x1FF y utilizar JMPC?

5. Suponga que en el ejemplo de la figura 4-14 (a) el enunciado

k = 5;

se agrega después de la si declaración. ¿Cuál sería el nuevo código de ensamblaje? Suponga que el
compilador es un compilador optimizador.

6. Proporcione dos traducciones IJVM diferentes para la siguiente declaración de Java:

i = k + n + 5;

7. Proporcione la declaración de Java que produjo el siguiente código IJVM:

ILOAD j
ILOAD n
ISUB
BIPUSH 7
ISUB
DUP
AÑADO

ISTORE i

8. En el texto mencionamos que al traducir la declaración


si (Z) pasa a L1; de lo contrario, vaya a L2

a binario, L2 tiene que estar en las 256 palabras inferiores del almacén de control. ¿No sería
igualmente posible tenerL1 en, digamos, 0x40 y L2 en 0x140? Explica tu respuesta.

9. En el microprograma para Mic-1, en si icmpeq3, MDR se copia a H. Unas pocas líneas más tarde se
resta de TOS para comprobar la igualdad. Seguramente es mejor tener una declaración:
CAP. 4 PROBLEMAS 339
si cmpeq3 Z = TOS - MDR; rd

¿Por qué no se hace esto?

10. ¿Cuánto tiempo tarda un Mic-1 de 2,5 GHz en ejecutar la siguiente declaración de Java?

i = j + k;

Da tu respuesta en nanosegundos.

11. Repita la pregunta anterior, solo ahora para un Mic-2 de 2.5 GHz. Según este cálculo, ¿cuánto
tiempo tomaría un programa que se ejecuta durante 100 segundos en el Mic-1 en el Mic-2?

12. Escriba un microcódigo para el Mic-1 para implementar la JVM POPTWO instrucción. Esta instrucción
elimina dos palabras de la parte superior de la pila.

13. En la máquina JVM completa, hay códigos de operación especiales de 1 byte para cargar locales de 0
a 3 en la pila en lugar de usar el código general YO CARGO instrucción. ¿Cómo debería modificarse la
IJVM para aprovechar al máximo estas instrucciones?

14. La instrucción ISHR (entero de desplazamiento aritmético a la derecha) existe en JVM pero no en IJVM. Utiliza
los dos valores superiores de la pila, reemplazándolos con un valor único, el resultado. La segunda
palabra desde arriba de la pila es el operando que se va a desplazar. Su contenido se desplaza hacia la
derecha en un valor entre 0 y 31, inclusive, dependiendo del valor de los 5 bits menos significativos de la
palabra superior en la pila (los otros 27 bits de la palabra superior se ignoran). El bit de signo se replica a
la derecha para tantos bits como el recuento de cambios. El código de operación paraISHR es 122 (0x7A).

una. ¿Cuál es la operación aritmética equivalente al desplazamiento a la izquierda con una cuenta de 2?
B. Amplíe el microcódigo para incluir esta instrucción como parte de IJVM.

15. La instrucción ISHL (shift left integer) existe en JVM pero no en IJVM. Utiliza los dos valores superiores
de la pila, reemplazando los dos con un solo valor, el resultado. La segunda palabra desde arriba
de la pila es el operando que se va a desplazar. Su contenido se desplaza hacia la izquierda en un
valor entre 0 y 31, inclusive, dependiendo del valor de los 5 bits menos significativos de la palabra
superior en la pila (los otros 27 bits de la palabra superior se ignoran). Los ceros se desplazan
desde la derecha para tantos bits como el recuento de cambios. El código de operación para
ISHL es 120 (0x78).

una. ¿Cuál es la operación aritmética equivalente a desplazarse a la izquierda con una cuenta de 2?
B. Amplíe el microcódigo para incluir esta instrucción como parte de IJVM.

dieciséis. La JVM INVOCAR EVIRTUAL La instrucción necesita saber cuántos parámetros tiene. ¿Por
qué?

17. Implementar la JVM DESCARGAR instrucción para el Mic-2. Tiene un índice de 1 byte y empuja la
variable local en esta posición a la pila. Luego también empuja la siguiente palabra más alta
a la pila.

18. Dibuja una máquina de estados finitos para anotar tenis. Las reglas del tenis son las siguientes. Para
ganar, necesitas al menos cuatro puntos y debes tener al menos dos puntos más que tu oponente.
Empiece con un estado (0, 0) que indique que nadie ha puntuado todavía. Luego agregue un
estado (1, 0) lo que significa queA ha anotado. Rotule el arco de (0, 0) a (1, 0) con unUNA.
Ahora agregue un estado (0, 1) que indique que B ha anotado, y etiquete el arco de (0, 0) con un
B. Continúe agregando estados y arcos hasta que se hayan incluido todos los estados posibles.
340 EL NIVEL DE MICROARQUITECTURA CAP. 4

19. Reconsidere el problema anterior. ¿Hay estados que podrían colapsar sin cambiar el
resultado de ningún juego? Si es así, ¿cuáles son equivalentes?

20. Dibuje una máquina de estados finitos para la predicción de ramas que sea más tenaz que la figura 4-42. Debería
cambiar solo las predicciones después de tres predicciones erróneas consecutivas.

21. El registro de desplazamiento de la figura 4-27 tiene una capacidad máxima de 6 bytes. ¿Podría construirse una
versión más económica de la IFU con un registro de desplazamiento de 5 bytes? ¿Qué tal uno de 4 bytes?

22. Habiendo examinado las IFU más baratas en la pregunta anterior, ahora examinemos las más
caras. ¿Sería útil tener un registro de desplazamiento mucho más grande en la IU, digamos,
12 bytes? ¿Por qué o por qué no?

23. En el microprograma del Mic-2, el código para si icmpeq6 va a T cuando Z se establece en


1. Sin embargo, el código en T es lo mismo que goto1. Habría sido posible ir a
goto1 ¿directamente? ¿Habría hecho así la máquina más rápida?

24. En el Mic-4, la unidad de decodificación mapea el código de operación IJVM en el índice ROM donde se
almacenan las microoperaciones correspondientes. Parecería más sencillo simplemente omitir la etapa de
decodificación y alimentar el código de operación IJVM directamente en la cola. Podría usar el código de
operación IJVM como un índice en la ROM, de la misma manera que funciona el Mic-1. ¿Qué hay de malo
en este plan?

25. ¿Por qué las computadoras están equipadas con múltiples capas de caché? ¿No sería mejor
simplemente tener uno grande?

26. Una computadora tiene una caché de dos niveles. Suponga que el 60% de las referencias de memoria se encuentran
en la caché de primer nivel, el 35% en el segundo nivel y el 5% fallan. Los tiempos de acceso son 5 nseg, 15 nsec y
60 nsec, respectivamente, donde los tiempos para la memoria caché y la memoria de nivel 2 comienzan a contar
en el momento en que se sabe que son necesarios (por ejemplo, un acceso a la memoria caché de nivel 2 ni
siquiera comienza hasta que ocurra la falta de caché de nivel 1). ¿Cuál es el tiempo medio de acceso?

27. Al final de la Sec. 4.5.1, dijimos que la asignación de escritura gana solo si es probable que haya
varias escrituras en la misma línea de caché seguidas. ¿Qué pasa con el caso de una escritura
seguida de varias lecturas? ¿No sería eso también una gran victoria?

28. En el primer borrador de este libro, la figura 4-39 mostraba una caché asociativa de tres vías en
lugar de una caché asociativa de cuatro vías. Uno de los revisores hizo un berrinche, alegando que
los estudiantes estarían terriblemente confundidos por esto porque 3 no es una potencia de 2 y las
computadoras hacen todo en binario. Dado que el cliente siempre tiene la razón, la figura se
cambió a una caché asociativa de cuatro vías. ¿Tenía razón el revisor? Analice su respuesta.

29. Muchos arquitectos informáticos dedican mucho tiempo a profundizar sus procesos. ¿Por qué?

30. Una computadora con una tubería de cinco etapas se ocupa de las ramas condicionales deteniéndose durante los
siguientes tres ciclos después de llegar a uno. ¿En qué medida el estancamiento perjudica el rendimiento si el 20%
de todas las instrucciones son ramas condicionales? Ignore todas las fuentes de estancamiento excepto las ramas
condicionales.

31. Una computadora obtiene hasta 20 instrucciones por adelantado. Sin embargo, en promedio, cuatro de
estas son ramas condicionales, cada una con una probabilidad del 90% de ser predichas correctamente.
¿Cuál es la probabilidad de que la captación previa esté en el camino correcto?
CAP. 4 PROBLEMAS 341
32. Suponga que cambiamos el diseño de la máquina utilizada en la figura 4-43 para que tenga 16
registros en lugar de 8. Luego cambiamos I6 para usar R8 como su destino. ¿Qué sucede en los
ciclos que comienzan en el ciclo 6?

33. Normalmente, las dependencias causan problemas con las CPU canalizadas. ¿Hay alguna
optimización que se pueda hacer con las dependencias de WAW que realmente puedan mejorar las
cosas? ¿Qué?

34. Vuelva a escribir el intérprete Mic-1 pero teniendo LV ahora apunte a la primera variable local en lugar de al
puntero de enlace.

35. Escriba un simulador para un caché mapeado directo unidireccional. Realice el número de entradas y los
parámetros de tamaño de línea de la simulación. Experimente con él e informe sobre sus hallazgos.
Esta página se dejó en blanco intencionalmente
5
EL CONJUNTO DE INSTRUCCIONES

NIVEL DE ARQUITECTURA

Este capítulo trata en detalle el nivel de Arquitectura de conjunto de instrucciones (ISA).


Este nivel, como vimos en la Fig. 1-2, se sitúa entre el nivel de microarquitectura y el nivel
del sistema operativo. Históricamente, este nivel se desarrolló antes que cualquiera de los
otros niveles y, de hecho, fue originalmente el único nivel. Hasta el día de hoy, este nivel a
veces se denomina simplemente "la arquitectura" de una máquina o, a veces
(incorrectamente) como "lenguaje ensamblador".
El nivel ISA tiene una importancia especial para los arquitectos de sistemas: es la
interfaz entre el software y el hardware. Si bien es posible que el hardware ejecute
directamente programas escritos en C, C ++, Java o algún otro lenguaje de alto nivel,
esto no sería una buena idea. Entonces se perdería la ventaja de rendimiento de la
compilación sobre la interpretación. Además, para ser de gran utilidad práctica, la
mayoría de las computadoras deben poder ejecutar programas escritos en varios
idiomas, no solo en uno.
El enfoque que adoptan esencialmente todos los diseñadores de sistemas es
traducir los programas en varios lenguajes de alto nivel a una forma intermedia
común, el nivel ISA, y crear hardware que pueda ejecutar programas de nivel ISA
directamente. El nivel ISA define la interfaz entre los compiladores y el hardware.
Es el idioma que ambos deben entender. La relación entre los compiladores, el
nivel ISA y el hardware se muestra en la figura 5-1.
Idealmente, al diseñar una nueva máquina, los arquitectos dedicarán tiempo a hablar tanto con los
redactores del compilador como con los ingenieros de hardware para averiguar qué características
quieren en el nivel ISA. Si los escritores del compilador quieren alguna característica que el

343
344 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

FORTRAN
Programa C
programa

FORTRAN Programa C
programa compilado compilado
al programa ISA al programa ISA

Software
Nivel ISA
Hardware
Programa ISA ejecutado
por microprograma o hardware

Hardware

Figura 5-1. El nivel ISA es la interfaz entre los compiladores y el hardware.

Los ingenieros no pueden implementar de una manera rentable (por ejemplo, una instrucción de
sucursal y hacer nómina), no entra. De manera similar, si la gente del hardware tiene alguna
característica nueva ingeniosa que quieren poner (por ejemplo, una memoria en el que las palabras
cuyas direcciones son números primos son superrápidas), pero la gente del software no puede
averiguar cómo generar código para usarlo, morirá en el tablero de dibujo. Después de mucha
negociación y simulación, surgirá y se implementará un ISA perfectamente optimizado para los
lenguajes de programación previstos.
Esa es la teoría. Ahora la cruda realidad. Cuando aparece una nueva máquina, la primera
pregunta que hacen todos los clientes potenciales es: '' ¿Es compatible con su predecesora? '' La
segunda es: '' ¿Puedo ejecutar mi antiguo sistema operativo en ella? '' La tercera es: '' ¿Ejecutará
todos mis programas de aplicación existentes sin modificaciones? '' Si alguna de las respuestas es
'' no '', los diseñadores tendrán mucho que explicar. Los clientes rara vez están interesados en
deshacerse de todo su software antiguo y empezar de nuevo.
Esta actitud ejerce una gran presión sobre los arquitectos informáticos para
mantener el ISA igual entre modelos, o al menos hacerlo compatible con versiones
anteriores. Con esto queremos decir que la nueva máquina debe poder ejecutar
programas antiguos sin cambios. Sin embargo, es completamente aceptable que la
nueva máquina tenga nuevas instrucciones y otras características que solo puedan ser
explotadas por un nuevo software. En términos de la figura 5-1, siempre que los
diseñadores hagan que el ISA sea compatible con versiones anteriores de los modelos
anteriores, son prácticamente libres de hacer lo que quieran con el hardware, ya que
casi nadie se preocupa por el hardware real (o incluso sabe Que hace). Pueden
cambiar de un diseño microprogramado a ejecución directa, o agregar tuberías o
instalaciones superescalares o cualquier otra cosa que deseen, siempre que
mantengan la compatibilidad con versiones anteriores de ISA. El objetivo es
asegurarse de que los programas antiguos se ejecuten en la nueva máquina.
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 345

Lo anterior no pretende implicar que el diseño de ISA no importe. Un buen ISA tiene
ventajas significativas sobre uno deficiente, particularmente en cuanto a potencia
informática bruta frente al costo. Para diseños equivalentes, diferentes NIA pueden
representar una diferencia de hasta un 25% en el rendimiento. Nuestro punto es
simplemente que las fuerzas del mercado hacen que sea difícil (pero no imposible)
desechar una ISA antigua e introducir una nueva. Sin embargo, de vez en cuando surge un
nuevo ISA de propósito general, y en mercados especializados (por ejemplo, sistemas
embebidos o procesadores multimedia) esto ocurre con mucha más frecuencia. En
consecuencia, es importante comprender el diseño de ISA.
¿Qué hace un buen ISA? Hay dos factores principales. Primero, una buena ISA debe definir
un conjunto de instrucciones que se puedan implementar de manera eficiente en las tecnologías
actuales y futuras, lo que resultará en diseños rentables a lo largo de varias generaciones. Un
diseño deficiente es más difícil de implementar y puede requerir muchas más puertas para
implementar un procesador y más memoria para ejecutar programas. También puede funcionar
más lento porque ISA oculta las oportunidades de superponer operaciones, lo que requiere
diseños mucho más sofisticados para lograr un rendimiento equivalente. Un diseño que
aproveche las peculiaridades de una tecnología en particular puede ser un flash en la sartén,
proporcionando una sola generación de implementaciones rentables, solo para ser superado por
ISA más progresistas.
En segundo lugar, una buena ISA debería proporcionar un objetivo limpio para el código
compilado. La regularidad y la integridad de una variedad de opciones son rasgos importantes
que no siempre están presentes en una ISA. Estas son propiedades importantes para un
compilador, que puede tener problemas para elegir la mejor opción entre alternativas limitadas,
particularmente cuando algunas alternativas aparentemente obvias no están permitidas por la
ISA. En resumen, dado que ISA es la interfaz entre el hardware y el software, debería hacer felices
a los diseñadores de hardware (ser fácil de implementar de manera eficiente) y hacer felices a los
diseñadores de software (ser fácil generar un buen código).

5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA

Comencemos nuestro estudio del nivel ISA preguntándonos qué es. Esto puede parecer una
pregunta simple, pero tiene más complicaciones de las que uno podría imaginar en un principio.
En la siguiente sección plantearemos algunos de estos problemas. Luego, veremos modelos de
memoria, registros e instrucciones.

5.1.1 Propiedades del nivel ISA

En principio, el nivel ISA se define por la apariencia de la máquina para un programador en


lenguaje de máquina. Dado que ya ninguna persona (en sus cuerdos) hace mucha programación en
lenguaje de máquina, redefinamos esto para decir que el código de nivel ISA es lo que genera un
compilador (ignorando las llamadas al sistema operativo e ignorando el lenguaje ensamblador
simbólico por el momento). Para producir código de nivel ISA, el escritor del compilador tiene
346 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

para saber cuál es el modelo de memoria, qué registros hay, qué tipos de datos e
instrucciones están disponibles, etc. La recopilación de toda esta información es lo que
define el nivel ISA.
Según esta definición, cuestiones como si la microarquitectura está
microprogramada o no, si está canalizada o no, si es superescalar o no, etc., no
forman parte del nivel ISA porque no son visibles para el redactor del compilador. Sin
embargo, esta observación no es del todo cierta porque algunas de estas propiedades
afectan el rendimiento y eso es visible para el escritor del compilador. Considere, por
ejemplo, un diseño superescalar que puede emitir instrucciones consecutivas en el
mismo ciclo, siempre que una sea una instrucción entera y la otra sea una instrucción
de punto flotante. Si el compilador alterna instrucciones enteras y de punto flotante,
obtendrá un rendimiento notablemente mejor que si no lo hiciera. Así, los detalles de
la operación superescalarestán visible a nivel ISA, por lo que la separación entre las
capas no es tan limpia como podría parecer al principio.
Para algunas arquitecturas, el nivel ISA se especifica mediante un documento de definición
formal, a menudo producido por un consorcio de la industria. Para otros no lo es. Por ejemplo,
ARM v7 (versión 7 ARM ISA) tiene una definición oficial publicada por ARM Ltd. El propósito de un
documento de definición es hacer posible que diferentes implementadores construyan las
máquinas y que todas ejecuten exactamente el mismo software y obtengan exactamente los
mismos resultados.
En el caso de ARM ISA, la idea es permitir que varios proveedores de chips fabriquen
chips ARM que sean funcionalmente idénticos, que solo difieran en rendimiento y precio.
Para que esta idea funcione, los proveedores de chips deben saber qué se supone que debe
hacer un chip ARM (a nivel ISA). Por lo tanto, el documento de definición dice qué es el
modelo de memoria, qué registros están presentes, qué hacen las instrucciones, etc., pero
no cómo es la microarquitectura.
Dichos documentos definitorios contienen normativo secciones, que imponen requisitos, y
informativo secciones, que están destinadas a ayudar al lector pero no forman parte de la
definición formal. Las secciones normativas utilizan constantemente palabras como
debe, no puede, y deberían exigir, prohibir y sugerir aspectos de la arquitectura,
respectivamente. Por ejemplo, una oración como

La ejecución de un código de operación reservado provocará una trampa.

dice que si un programa ejecuta un código de operación que no está definido, debe causar una
trampa y no ser simplemente ignorado. Un enfoque alternativo podría ser dejar esto abierto, en
cuyo caso la oración podría leer

El efecto de ejecutar un código de operación reservado está definido por la implementación.

Esto significa que el escritor del compilador no puede contar con ningún comportamiento en particular,
lo que le da a los diferentes implementadores la libertad de tomar diferentes decisiones. La mayoría de
las especificaciones arquitectónicas van acompañadas de conjuntos de pruebas que comprueban si una
implementación que dice ajustarse a la especificación realmente lo hace.
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 347

Está claro por qué ARM v7 tiene un documento que define su nivel ISA: para que todos
los chips ARM ejecuten el mismo software. Durante muchos años, no hubo un documento
de definición formal para el IA-32 ISA (a veces llamado elx86 ISA) porque Intel no quería
facilitar a otros proveedores la fabricación de chips compatibles con Intel. De hecho, Intel
acudió a los tribunales para intentar evitar que otros proveedores clonaran sus chips,
aunque perdió el caso. Sin embargo, a fines de la década de 1990, Intel finalmente lanzó
una especificación completa del conjunto de instrucciones IA-32. Quizás esto se debió a que
sintieron el error de sus métodos y querían ayudar a sus colegas arquitectos y
programadores, o quizás fue porque Estados Unidos, Japón y Europa estaban investigando
a Intel por posiblemente violar las leyes antimonopolio. Esta referencia de ISA bien
redactada todavía se actualiza hoy en el sitio web para desarrolladores de Intel (http://
developer.intel.com). La versión lanzada con el Core i7 de Intel pesa 4161 páginas,
recordándonos una vez más que el Core i7 es uncomplejo equipo de instrucciones.
Otra propiedad importante del nivel ISA es que en la mayoría de las máquinas hay al menos
dos modos. Modo kernel está diseñado para ejecutar el sistema operativo y permite que se
ejecuten todas las instrucciones. Modo de usuario está destinado a ejecutar programas de
aplicación y no permite que se ejecuten determinadas instrucciones sensibles (como las que
manipulan la caché directamente). En este capítulo nos centraremos principalmente en las
instrucciones y propiedades del modo de usuario.

5.1.2 Modelos de memoria

Todas las computadoras dividen la memoria en celdas que tienen direcciones consecutivas. El
tamaño de celda más común en este momento es de 8 bits, pero en el pasado se han utilizado tamaños
de celda de 1 a 60 bits (consulte la figura 2-10). Una celda de 8 bits se llamabyteo octeto).
La razón para usar bytes de 8 bits es que los caracteres ASCII son de 7 bits, por lo que un carácter ASCII
(más un bit de paridad que se usa con poca frecuencia) cabe en un byte. Otros códigos, como Unicode y
UTF-8, utilizan múltiplos de 8 bits para representar caracteres.
Los bytes generalmente se agrupan en palabras de 4 bytes (32 bits) u 8 bytes (64 bits) con
instrucciones disponibles para manipular palabras completas. Muchas arquitecturas requieren que las
palabras estén alineadas con sus límites naturales. Por ejemplo, una palabra de 4 bytes puede
comenzar en la dirección 0, 4, 8, etc., pero no en la dirección 1 o 2. De manera similar, una palabra de 8
bytes puede comenzar en la dirección 0, 8 o 16, pero no en dirección 4 o 6. La alineación de palabras de
8 bytes se ilustra en la Fig. 5-2.
A menudo se requiere alineación porque las memorias funcionan de manera más eficiente
de esa manera. El Core i7, por ejemplo, obtiene 8 bytes a la vez de la memoria utilizando una
interfaz DDR3 que solo admite accesos alineados de 64 bits. Por lo tanto, el Core i7 ni siquiera
podría hacer una referencia de memoria no alineada si quisiera porque la interfaz de memoria
requiere direcciones. que son múltiplos de 8.
Sin embargo, este requisito de alineación a veces causa problemas. En el Core i7, los
programas pueden hacer referencia a palabras que comienzan en cualquier dirección, una
propiedad que se remonta al 8088, que tenía un bus de datos de 1 byte de ancho (y, por lo tanto,
no se requiere alinear referencias de memoria en límites de 8 bytes). . Si un Core i7
348 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

Dirección Dirección
8 bytes 8 bytes

24 24
dieciséis 19 18 17 16 dieciséis

15 14 13 12 11 10 9 8 8 15 14 13 12 8
0 0

8 bytes alineados 8 bytes no alineados


palabra en la dirección 8 palabra en la dirección 12

(a) (B)

Figura 5-2. Un 8 bytes palabra en una memoria little-endian. (a) Alineado. (b) No
alineado. Algunas máquinas requieren que las palabras de la memoria estén alineadas.

El programa lee una palabra de 4 bytes en la dirección 7, el hardware tiene que hacer una
referencia de memoria para obtener los bytes 0 a 7 y una segunda para obtener los bytes 8 a 15.
Luego, la CPU tiene que extraer los 4 bytes requeridos de los 16 bytes leídos. de la memoria y
ensamblarlos en el orden correcto para formar una palabra de 4 bytes. Hacer esto de forma
regular no conduce a una velocidad deslumbrante.
Tener la capacidad de leer palabras en direcciones arbitrarias requiere una lógica adicional en el
chip, lo que lo hace más grande y más caro. A los ingenieros de diseño les encantaría deshacerse de él y
simplemente requerirían que todos los programas hicieran referencias a la memoria alineadas con
palabras. El problema es que, cada vez que los ingenieros dicen: "¿A quién le importa ejecutar
programas viejos y mohosos 8088 que hacen referencia a la memoria incorrectamente?", La gente de
marketing tiene una respuesta sucinta: "Nuestros clientes".
La mayoría de las máquinas tienen un solo espacio de direcciones lineal en el nivel ISA, que se
extiende desde la dirección 0 hasta un máximo, a menudo 232 - 1 bytes o 264 - 1 bytes. Sin embargo,
algunas máquinas tienen espacios de direcciones separados para instrucciones y datos, por lo que una
búsqueda de instrucciones en la dirección 8 va a un espacio de direcciones diferente al de una
búsqueda de datos en la dirección 8. Este esquema es más complejo que tener un solo espacio de
direcciones, pero tiene dos ventajas. Primero, es posible tener 232 bytes de programa y 232 bytes de
datos utilizando solo direcciones de 32 bits. En segundo lugar, debido a que todas las escrituras van
automáticamente al espacio de datos, es imposible que un programa se sobrescriba accidentalmente a
sí mismo, eliminando así una fuente de errores del programa. La separación de los espacios de
instrucción y de datos también hace que los ataques de malware sean mucho más difíciles de realizar
porque el malware no puede cambiar el programa, ni siquiera puede abordarlo.
Tenga en cuenta que tener espacios de direcciones separados para instrucciones y datos no es lo mismo
que tener un caché de nivel 1 dividido. En el primer caso, la cantidad total de espacio de direcciones se duplica
y las lecturas en cualquier dirección dada producen resultados diferentes, dependiendo de si se está leyendo
una instrucción o una palabra de datos. Con un caché dividido, todavía hay un solo espacio de direcciones, solo
diferentes cachés almacenan diferentes partes del mismo.
Otro aspecto más del modelo de memoria de nivel ISA es la semántica de la memoria. Es
perfectamente natural esperar que unCARGA instrucción que ocurre después de una TIENDA
instrucción y que hace referencia a la misma dirección devolverá el valor recién almacenado.
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 349

Sin embargo, en muchos diseños, como vimos en el Cap. 4, se reordenan las microinstrucciones. Por
tanto, existe un peligro real de que la memoria no tenga el comportamiento esperado. El problema
empeora aún más en un multiprocesador, ya que cada una de las múltiples CPU envía un flujo de
solicitudes de lectura y escritura (posiblemente reordenadas) a la memoria compartida.
Los diseñadores de sistemas pueden abordar este problema con cualquiera de los varios enfoques. En un
extremo, todas las solicitudes de memoria se pueden serializar, de modo que cada una se complete antes de
que se emita la siguiente. Esta estrategia perjudica el rendimiento pero proporciona la semántica de memoria
más simple (todas las operaciones se ejecutan en un orden estricto del programa).
En el otro extremo, no se dan garantías de ningún tipo. Para forzar una orden en la
memoria, el programa debe ejecutar unSINCRONIZAR instrucción, que bloquea la emisión de todas
las nuevas operaciones de memoria hasta que se hayan completado todas las anteriores. Este
diseño supone una gran carga para los compiladores porque tienen que comprender en detalle
cómo funciona la microarquitectura subyacente, pero les da a los diseñadores de hardware la
máxima libertad para optimizar el uso de la memoria.
También son posibles modelos de memoria intermedia, en los que el hardware bloquea
automáticamente la emisión de determinadas referencias de memoria (por ejemplo, aquellas que
implican una dependencia RAW o WAR) pero no otras. Si bien tener todas estas peculiaridades
causadas por la microarquitectura expuestas al nivel ISA es molesto (al menos para los escritores
de compiladores y programadores de lenguaje ensamblador), es en gran medida la tendencia.
Esta tendencia se debe a las implementaciones subyacentes, como el reordenamiento de las
microinstrucciones, las canalizaciones profundas, los niveles de caché múltiples, etc. Veremos
más ejemplos de tales efectos antinaturales más adelante en este capítulo.

5.1.3 Registros

Todas las computadoras tienen algunos registros visibles a nivel ISA. Están ahí para
controlar la ejecución del programa, mantener resultados temporales y servir para otros
propósitos. En general, los registros visibles a nivel de microarquitectura, comoTOS y MAR
en la Fig. 4-1, no son visibles a nivel ISA. Sin embargo, algunos de ellos, como el
contador de programa y el puntero de pila, son visibles en ambos niveles. Por otro
lado, los registros visibles a nivel ISA siempre son visibles a nivel de microarquitectura
ya que es allí donde se implementan.
Los registros de nivel ISA se pueden dividir aproximadamente en dos categorías: registros
de propósito especial y registros de propósito general Los registros de propósito especial
incluyen cosas como el contador de programa y el puntero de pila, así como otros registros con
una función específica. En contraste, los registros de propósito general están ahí para contener
variables locales clave y resultados intermedios de cálculos. Su función principal es proporcionar
un acceso rápido a datos muy utilizados (básicamente, evitando accesos a la memoria). Las
máquinas RISC, con sus CPU rápidas y memorias (relativamente) lentas, generalmente tienen al
menos 32 registros de propósito general, y la tendencia en los nuevos diseños de CPU es tener
aún más.
En algunas máquinas, los registros de propósito general son completamente simétricos e
intercambiables. Cada uno puede hacer cualquier cosa que los demás puedan hacer. Si los registros
350 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

son todos equivalentes, un compilador puede usar R1 para mantener un resultado temporal, pero
también se puede utilizar R25. La elección del registro no importa.
En otras máquinas, sin embargo, algunos de los registros de propósito general pueden ser
algo especiales. Por ejemplo, en el Core i7, hay un registro llamadoEDX que puede usarse como
un registro general, pero que también recibe la mitad del producto en una multiplicación y
retiene la mitad del dividendo en una división.
Incluso cuando los registros de propósito general son completamente intercambiables, es común
que el sistema operativo o los compiladores adopten convenciones sobre cómo se usan. Por ejemplo,
algunos registros pueden contener parámetros para procedimientos llamados y otros pueden usarse
como registros temporales. Si un compilador pone una variable local importante enR1 y luego llama a
un procedimiento de biblioteca que piensa R1 es un registro temporal disponible para él, cuando el
procedimiento de biblioteca regresa, R1 puede contener basura. Si existen convenciones en todo el
sistema sobre cómo se utilizarán los registros, se recomienda a los compiladores y programadores en
lenguaje ensamblador que se adhieran a ellas para evitar problemas.

Además de los registros de nivel ISA visibles para los programas de usuario, siempre
hay una cantidad sustancial de registros de propósito especial disponibles solo en modo
kernel. Estos registros controlan los distintos cachés, memoria, dispositivos de E / S y otras
características de hardware de la máquina. Solo los usa el sistema operativo, por lo que los
compiladores y los usuarios no tienen que conocerlos.
Un registro de control que es una especie de híbrido kernel / usuario es el registro de
banderas o PSW (Palabra de estado del programa). Este registro contiene varios bits
misceláneos que necesita la CPU. Los bits más importantes son loscódigos de condición.
Estos bits se establecen en cada ciclo de ALU y reflejan el estado del resultado de la operación
más reciente. Los bits de código de condición típicos incluyen

N: se establece cuando el resultado fue Negativo. Z:

se establece cuando el resultado es cero.

V— Establecer cuándo el resultado provocó un sobreflujo.

C— Se establece cuando el resultado provocó una ejecución del bit más a la izquierda.

A - Se establece cuando hubo un acarreo del bit 3 (acarreo auxiliar, ver más abajo). P -

Establecer cuando el resultado tuvo paridad uniforme.

Los códigos de condición son importantes porque la comparación y las instrucciones de bifurcación
condicional (también llamadas instrucciones de salto condicional) los utilizan. Por ejemplo, el
CMP La instrucción típicamente resta dos operandos y establece los códigos de condición
basados en la diferencia. Si los operandos son iguales, entonces la diferencia será cero y el
Z bit de código de condición en el PSW Se establecerá el registro. Una subsecuenteBEQ
(Branch EQual) prueba la Z bit y ramas si está configurado.
los PSW contiene más que solo los códigos de condición, pero el contenido completo
varía de una máquina a otra. Los campos adicionales típicos son el modo de máquina
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 351

(por ejemplo, usuario o kernel), bit de seguimiento (utilizado para depurar), nivel de prioridad de la CPU y estado de
habilitación de interrupciones. A menudo elPSW es legible en modo de usuario, pero algunos de los campos sólo se
pueden escribir en modo kernel (por ejemplo, el bit de modo usuario / kernel).

5.1.4 Instrucciones

La característica principal del nivel ISA es su conjunto de instrucciones de máquina.


Estos controlan lo que puede hacer la máquina. Siempre hayCARGA y TIENDA instrucciones
(de una forma u otra) para mover datos entre la memoria y los registros y MOVERSE
instrucciones para copiar datos entre los registros. Las instrucciones aritméticas siempre
están presentes, al igual que las instrucciones booleanas y las instrucciones para comparar
elementos de datos y ramificar los resultados. Ya hemos visto algunas instrucciones ISA
típicas (ver Fig. 4-11) y estudiaremos muchas más en este capítulo.

5.1.5 Descripción general del nivel ISA Core i7

En este capítulo analizaremos tres ISA muy diferentes: el IA-32 de Intel, como se
incorpora en el Core i7, la arquitectura ARM v7, implementada en el sistema en un
chip OMAP4430, y la arquitectura AVR de 8 bits, utilizada por el microcontrolador
ATmega168. La intención no es proporcionar una descripción exhaustiva de ninguna
de las NIA, sino más bien demostrar aspectos importantes de una NIA y mostrar cómo
estos aspectos pueden variar de una NIA a otra. Comencemos con el Core i7.

El procesador Core i7 ha evolucionado a lo largo de muchas generaciones, y su linaje se remonta a


algunos de los primeros microprocesadores jamás construidos, como comentamos en el Cap. 1. Si bien
el ISA básico mantiene el soporte completo para la ejecución de programas escritos para los
procesadores 8086 y 8088 (que tenían el mismo ISA), incluso contiene restos del 8080, un procesador de
8 bits popular en la década de 1970. El 8080, a su vez, estuvo fuertemente influenciado por las
limitaciones de compatibilidad con el 8008 aún anterior, que se basaba en el 4004, un chip de 4 bits que
se usaba cuando los dinosaurios deambulaban por la tierra.
Desde un punto de vista de software puro, el 8086 y el 8088 eran máquinas
sencillas de 16 bits (aunque el 8088 tenía un bus de datos de 8 bits). Su sucesor, el
80286, también era una máquina de 16 bits. Su principal ventaja era un espacio de direcciones más
grande, aunque pocos programas lo usaron porque constaba de 16,384 segmentos de 64 KB en lugar
de un 2 lineal.30-memoria de bytes.
El 80386 fue la primera máquina de 32 bits de la familia Intel. Todas las máquinas
posteriores (80486, Pentium, Pentium Pro, Pentium II, Pentium III, Pentium 4, Celeron,
Xeon, Pentium M, Centrino, Core 2 duo, Core i7, etc.) tienen esencialmente la misma
arquitectura de 32 bits que la 80386, llamadoIA-32, por lo que es esta arquitectura en la
que nos centraremos aquí. El único cambio arquitectónico importante después del 80386
fue la introducción de las instrucciones MMX, SSE y SSE2 en versiones posteriores de la
serie x86. Estas instrucciones son altamente especializadas y están diseñadas para mejorar
el rendimiento en aplicaciones multimedia. Otra extensión importante fue x86 de 64 bits
352 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

(a menudo llamado x86-64), que aumentó los cálculos de números enteros y el tamaño de la dirección
virtual a 64 bits. Si bien la mayoría de las extensiones fueron introducidas por Intel y luego
implementadas por los competidores, este fue un caso en el que AMD introdujo una extensión que Intel
tuvo que adoptar.
El Core i7 tiene tres modos de funcionamiento, dos de los cuales lo hacen actuar como un
8088. En modo real, todas las funciones que se han agregado desde el 8088 están desactivadas y
el Core i7 se comporta como un simple 8088. Si algún programa hace algo mal, toda la máquina
simplemente falla. Si Intel hubiera diseñado seres humanos, habría puesto un poco que los
hubiera hecho volver al modo chimpancé (la mayoría de los cerebrales discapacitados, no habla,
duerme en los árboles, come principalmente plátanos, etc.)
Un paso hacia arriba es modo virtual 8086, lo que hace posible ejecutar programas
antiguos del 8088 de forma protegida. En este modo, un sistema operativo real tiene el control
de toda la máquina. Para ejecutar un programa 8088 antiguo, el sistema operativo crea un
entorno aislado especial que actúa como un 8088, excepto que si su programa falla, se notifica al
sistema operativo en lugar de que la máquina se bloquee. Cuando un usuario de Windows inicia
una ventana de MS-DOS, el programa que se ejecuta allí se inicia en el modo virtual 8086 para
proteger al propio Windows de los programas de MS-DOS que se comportan mal.
El modo final es el modo protegido, en el que el Core i7 actúa como un Core i7 en lugar
de un 8088 muy caro. Hay cuatro niveles de privilegios disponibles y controlados por bits en
el PSW. El nivel 0 corresponde al modo kernel en otras computadoras y tiene acceso
completo a la máquina. Es utilizado por el sistema operativo. El nivel 3 es para programas
de usuario. Bloquea el acceso a ciertas instrucciones críticas y registros de control para
evitar que un programa de usuario fraudulento apague toda la máquina. Los niveles 1 y 2
rara vez se utilizan.
El Core i7 tiene un gran espacio de direcciones, con memoria dividida en 16,384 segmentos, cada
uno de los cuales va de la dirección 0 a la dirección 2.32 - 1. Sin embargo, la mayoría de los sistemas
operativos (incluidos UNIX y todas las versiones de Windows) admiten solo un segmento, por lo que la
mayoría de los programas de aplicación ven efectivamente un espacio de direcciones lineal de 232 bytes
y, a veces, parte de esto está ocupado por el sistema operativo. Cada byte en el espacio de direcciones
tiene su propia dirección, con palabras de 32 bits de longitud. Las palabras se almacenan en formato
little-endian (el byte de orden inferior tiene la dirección más baja).
Los registros del Core i7 se muestran en la figura 5-3. Los primeros cuatro registros,
EAX, EBX, ECX, y EDX, son
registros de 32 bits, más o menos de propósito general, aunque cada
uno tiene sus propias peculiaridades. EAX es el registro aritmético principal; EBX es bueno
para guardar punteros (direcciones de memoria); ECX juega un papel en el bucle; EDX es
necesario para la multiplicación y la división, donde, junto con EAX, contiene dividendos y
productos de 64 bits. Cada uno de estos registros contiene un registro de 16 bits en los 16
bits de orden inferior y un registro de 8 bits en los 8 bits de orden inferior. Estos registros
facilitan la manipulación de cantidades de 16 y 8 bits, respectivamente. El 8088 y el 80286
solo tenían registros de 8 y 16 bits. Los registros de 32 bits se agregaron con el 80386, junto
con elmi prefijo, que significa Extendido.
Los cuatro siguientes también son de propósito algo general, pero con más peculiaridades. losESI y
EDI Los registros están destinados a mantener punteros en la memoria, especialmente para
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 353

Bits dieciséis 8 8
HACHA
AH Alabama EAX
BX
BH licenciado en Derecho EBX
CX
CH CL ECX
DX
DH DL EDX

ESI

EDI

EBP

ESP

CS

SS

DS

ES

FS

GS

EIP

EFLAGS

Figura 5-3. Los registros primarios del Core i7.

las instrucciones de manipulación de cadenas de hardware, donde ESI apunta a la cadena


de origen y EDI apunta a la cadena de destino. losEBP register es también un registro de
puntero. Por lo general, se usa para apuntar a la base del marco de pila actual, al igual que
LV en IJVM. Cuando un registro (comoEBP) se usa para apuntar a la base del marco de pila
local, generalmente se llama puntero de marco. Finalmente, ESP es el puntero de pila.
El siguiente grupo de registros, CS mediante GS, son registros de segmento. Hasta cierto
punto, son trilobites electrónicos, antiguos fósiles que quedaron de la época en que el 8088
intentó abordar 220 bytes de memoria utilizando direcciones de 16 bits. Baste decir que cuando el
Core i7 está configurado para utilizar un único espacio de direcciones lineal de 32 bits, se pueden
ignorar de forma segura. El siguiente esEIP, que es el contador de programa (puntero de
instrucción extendida). Finalmente, llegamos aEFLAGS, Cuál es el PSW.
354 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

5.1.6 Descripción general del nivel ISA ARM OMAP4430

La arquitectura ARM fue introducida por primera vez en 1985 por Acorn Computer. La
arquitectura se inspiró en la investigación realizada en Berkeley en la década de 1980 (Patterson,
1985 y Patterson y Séquin, 1982). La arquitectura ARM original (llamada ARM2) era una
arquitectura de 32 bits que admitía un espacio de direcciones de 26 bits. El OMAP4430
utiliza la microarquitectura ARM Cortex A9, que implementa la versión 7 de la
arquitectura ARM, y esa es la ISA que describiremos en este capítulo. Para mantener la
coherencia con el resto del libro, aquí nos referiremos al OMAP4430, pero a nivel ISA,
todos los diseños basados en el núcleo ARM Cortex A9 implementan el mismo ISA.

La estructura de la memoria OMAP4430 es limpia y simple: la memoria direccionable es una matriz


lineal de 232 bytes. Los procesadores ARM son bi-endian, de modo que pueden acceder a la memoria
con un orden big- o little-endian. El endian se especifica en un bloque de memoria del sistema que se
lee inmediatamente después del reinicio del procesador. Para garantizar que el bloque de memoria del
sistema se lea correctamente, debe estar en formato little-endian, incluso si la máquina se va a
configurar para la operación big-endian.
Es importante que la ISA tenga un límite de espacio de direcciones mayor que el que necesitan las
implementaciones, porque las implementaciones futuras casi con certeza necesitarán aumentar el
tamaño de la memoria a la que puede acceder el procesador. El espacio de direcciones de 32 bits de
ARM ISA está causando dificultades a muchos diseñadores, ya que muchos sistemas basados en ARM,
como los teléfonos inteligentes, ya tienen más de 232 bytes de memoria. Hasta la fecha, los diseñadores
han solucionado estos problemas creando la mayor parte del almacenamiento en la unidad flash de
memoria, al que se accede con una interfaz de disco que admite un espacio de direcciones orientado a
bloques más grande. Para abordar esta limitación potencialmente destructora del mercado, ARM (la
compañía) publicó recientemente la definición de ARM versión 8 ISA, que admite espacios de
direcciones de 64 bits.
Un problema serio encontrado con las arquitecturas exitosas ha sido que su ISA
limitaba la cantidad de memoria direccionable. En ciencias de la computación, el único error
que uno no puede solucionar es que no hay suficientes bits. Un día, sus nietos le
preguntarán cómo las computadoras podían hacer algo en los viejos tiempos con solo
direcciones de 32 bits y solo 4 GB de memoria real cuando el juego promedio necesita 1 TB
solo para arrancar.
El ARM ISA es limpio, aunque la organización de los registros es algo peculiar en un intento
de simplificar algunas codificaciones de instrucciones. La arquitectura mapea el contador del
programa en el archivo de registro de enteros (como registroR15), ya que esto permite crear
ramas con operaciones ALU que tienen R15 como registro de destino. La experiencia ha
demostrado que la organización del registro es más problemática de lo que vale, pero la antigua
regla de compatibilidad retroactiva hacía que fuera casi imposible deshacerse de ella.

El ARM ISA tiene dos grupos de registros. Estos son los registros de propósito
general de 16 32 bits y los registros de coma flotante de 32 bits (si se admite el
coprocesador VFP). Los registros de propósito general se denominanR0 mediante R15,
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 355

aunque se utilizan otros nombres en determinados contextos. Los nombres y funciones alternativos de
los registros se muestran en la figura 5-4.

Registrarse Alt. nombre Función


R0 – R3 A1 – A4 Mantiene parámetros para el procedimiento que se llama

R4 – R11 V1 – V8 Mantiene variables locales para el procedimiento actual Registro

R12 IP de llamadas intraprocedimiento (para llamadas de 32 bits) Puntero

R13 SP de pila

R14 LR Registro de enlace (dirección de retorno para la función actual)

R15 ordenador personal Contador de programa

Figura 5-4. Registros generales de la versión 7 de ARM.

Todos los registros generales tienen 32 bits de ancho y se pueden leer y escribir
mediante una variedad de instrucciones de carga y almacenamiento. Los usos dados en la
figura 5-4 se basan en parte en la convención, pero también en parte en cómo los trata el
hardware. En general, no es aconsejable desviarse de los usos enumerados en la figura a
menos que tenga un cinturón negro en piratería ARM y realmente sepa lo que está
haciendo. Es responsabilidad del compilador o programador asegurarse de que el
programa acceda a los registros correctamente y realice el tipo correcto de aritmética en
ellos. Por ejemplo, es muy fácil cargar números de punto flotante en los registros generales
y luego realizar la suma de enteros en ellos, una operación que producirá un sinsentido
total, pero que la CPU realizará alegremente cuando se le indique.
los Vx Los registros se utilizan para contener constantes, variables y punteros que son necesarios
para los procedimientos, y deben almacenarse y volver a cargarse en las entradas y salidas de los
procedimientos si es necesario. losHacha Los registros se utilizan para pasar parámetros a los
procedimientos para evitar referencias a la memoria. Explicaremos cómo funciona esto a continuación.
Se utilizan cuatro registros dedicados para fines especiales. losIP El registro funciona
alrededor de las limitaciones de la instrucción de llamada funcional ARM (BL) que no puede
abordar completamente todos sus 232 bytes de espacio de direcciones. Si el objetivo de una
llamada está demasiado lejos para que la instrucción se exprese, la instrucción llamará a un
fragmento de código '' barniz '' que usa la dirección en elIP registrarse como destino de la
llamada a la función. losSP register indica la parte superior actual de la pila y fluctúa a medida
que las palabras se introducen en la pila o salen de ella. El tercer registro de fines especiales esLR.
Se utiliza para que las llamadas a procedimientos retengan la dirección de retorno. El cuarto registro de
propósito especial, como se mencionó anteriormente, es el contador de programaORDENADOR PERSONAL.
El almacenamiento de un valor en este registro redirige la búsqueda de instrucciones al recién
depositado ordenador personal Dirección. Otro registro importante en la arquitectura ARM es el registro de
estado del programa (PSR), que mantiene el estado de los cálculos de ALU anteriores, incluidos Cero,
Negativo y Desbordamiento, entre otros bits.
El ARM ISA (cuando se configura con el coprocesador VFP) también tiene 32 registros de coma
flotante de 32 bits. Se puede acceder a estos registros directamente como 32 valores de punto flotante
de precisión simple o como 16 valores de punto flotante de precisión doble de 64 bits.
356 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

valores. El tamaño del registro de punto flotante al que se accede está determinado por la
instrucción; en general, todas las instrucciones de punto flotante ARM vienen en variantes de
precisión simple y doble.
La arquitectura ARM es una arquitectura de carga / almacenamiento. Es decir, las únicas
operaciones que acceden a la memoria directamente son las instrucciones de carga y almacenamiento
para mover datos entre los registros y la memoria. Todos los operandos para instrucciones aritméticas
y lógicas deben provenir de registros o ser suministrados por la instrucción (no en la memoria), y todos
los resultados deben guardarse en un registro (no en la memoria).

5.1.7 Descripción general del nivel ATmega168 AVR ISA

Nuestro tercer ejemplo es el ATmega168. A diferencia del Core i7 (que se usa


principalmente en máquinas de escritorio y granjas de servidores) y el OMAP4430 (que se
usa principalmente en teléfonos, tabletas y otros dispositivos móviles), el ATmega168 se
usa en sistemas integrados de gama baja como semáforos. y radio reloj para controlar el
dispositivo y administrar los botones, las luces y otras partes de la interfaz de usuario. En
esta sección, daremos una breve introducción técnica al ATmega168 AVR ISA.
El ATmega168 tiene un modo y no tiene hardware de protección, ya que nunca ejecuta múltiples
programas propiedad de usuarios potencialmente hostiles. El modelo de memoria es extremadamente
simple. Hay 16 KB de memoria de programa y un segundo 1 KB de memoria de datos. Cada uno tiene
su propio espacio de direcciones distinto, por lo que una dirección particular hará referencia a una
memoria diferente dependiendo de si el acceso es al programa o a la memoria de datos. El programa y
los espacios de datos se dividen para que sea posible implementar el espacio del programa en flash y el
espacio de datos en SRAM.
Son posibles varias implementaciones diferentes de memoria, dependiendo de cuánto
quiera pagar el diseñador por el procesador. En el más simple, el ATmega48, hay una
memoria flash de 4 KB para el programa y una SRAM de 512 bytes para los datos. Tanto la
memoria flash como la RAM están en chip. Para aplicaciones pequeñas, esta cantidad de
memoria suele ser suficiente y tener toda la memoria en el chip de la CPU es una gran
ventaja. El ATmega88 tiene el doble de memoria en chip: 8 KB de ROM y 1 KB de SRAM.

El ATmega168 utiliza una organización de memoria de dos niveles para proporcionar una mejor
seguridad del programa. La memoria flash del programa se divide ensección del cargador de arranque y
sección de aplicación, el tamaño de cada uno está determinado por bits de fusible que se programan
una vez cuando el microcontrolador se enciende por primera vez. Por razones de seguridad, solo el
código que se ejecuta desde la sección del cargador de arranque puede actualizar la memoria flash. Con
esta función, cualquier código se puede colocar en el área de la aplicación (incluidas las aplicaciones
descargadas de terceros) con la confianza de que nunca se mezclará con otro código en el sistema
(porque el código de la aplicación se ejecutará desde el espacio de la aplicación que no puede escribir
memoria flash ). Para realmente atar un sistema, un proveedor puede firmar código digitalmente. Con
el código firmado, el cargador de arranque carga el código en la memoria flash solo si está firmado
digitalmente por un proveedor de software aprobado. Como tal, el sistema
SEGUNDO. 5.1 DESCRIPCIÓN GENERAL DEL NIVEL ISA 357

sólo ejecutará código que haya sido "bendecido" por un proveedor de software de confianza. El enfoque
es bastante flexible en el sentido de que incluso el cargador de arranque se puede reemplazar, si el
nuevo código se ha firmado digitalmente correctamente. Esto es similar a la forma en que Apple y TiVo
se aseguran de que el código que se ejecuta en sus dispositivos esté a salvo de daños.
El ATmega168 contiene 32 registros de uso general de 8 bits, a los que se accede mediante
instrucciones a través de un campo de 5 bits que especifica qué registro utilizar. Los registros se
llamanR0 mediante R31. Una propiedad peculiar de los registros ATmega168 es que también están
presentes en el espacio de la memoria. El byte 0 del espacio de datos es equivalente aR0 del
registro establecido en 0. Cuando una instrucción cambia R0 y luego lee el byte de memoria 0,
encuentra el nuevo valor de R0 allí. Del mismo modo, el byte 1 de memoria
es R1 y así sucesivamente, hasta el byte 31. Esta disposición se muestra en la Fig. 5-5.

Programa de memoria

16383

Memoria de datos

Solicitud
1023
memoria Bloc de notas

95
Memoria de E / S
32
Cargador de arranque 31
0 Registros
0

7 0
Registro de estado (SREG) ITHSVNZC

Elevado Dirección 80

Puntero de pila (SP)


Bajo Dirección 81

Figura 5-5. Registro en chip y organización de la memoria para el ATmega168.

Directamente encima de los 32 registros de propósito general, en las direcciones de memoria 32 a


95, hay 64 bytes de memoria reservados para acceder a los registros de dispositivos de E / S, incluidos
los dispositivos internos del sistema en un chip.
Además de los cuatro conjuntos de ocho registros, el ATmega168 tiene una pequeña cantidad de
registros de propósito especial, los más importantes de los cuales se ilustran en la figura 5-5. losregistro de
estado contiene, de izquierda a derecha, el bit de habilitación de interrupción, el bit de medio acarreo, el bit de
signo, el bit de desbordamiento, el indicador negativo, el indicador de cero y el bit de ejecución. Todos estos
bits de estado, excepto el bit de habilitación de interrupciones, se establecen como resultado de operaciones
aritméticas.
El registro de estado I bit permite que las interrupciones se habiliten o deshabiliten globalmente. Si
elI bit es 0, todas las interrupciones están deshabilitadas. Al borrar este bit, es posible deshabilitar
cualquier otra interrupción en una sola instrucción. La configuración del bit permite que se produzcan
las interrupciones actualmente pendientes, así como las futuras. Cada dispositivo tiene asociado
358 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

con él, un bit de habilitación de interrupciones. Si la habilitación del dispositivo está configurada y la habilitación de interrupción

globalI bit está establecido, el dispositivo puede interrumpir el procesador.

El puntero de pila SP contiene la dirección actual en la memoria de datos donde EMPUJAR


y MÚSICA POP las instrucciones accederán a sus datos, similar a la instrucción con un nombre similar en
Java JVM del Cap. 4. El puntero de la pila se encuentra en la memoria de E / S en la dirección 80. Un solo
byte de memoria de 8 bits es demasiado pequeño para abordar 1024 bytes de memoria de datos, por lo
que el puntero de la pila se compone de dos ubicaciones consecutivas en la memoria, formando un 16
-dirección de bits.

5.2 TIPOS DE DATOS

Todas las computadoras necesitan datos. De hecho, para muchos sistemas informáticos, el
objetivo principal es procesar datos financieros, comerciales, científicos, de ingeniería u otros. Los datos
deben estar representados de alguna forma específica dentro de la computadora. A nivel ISA, se utilizan
una variedad de diferentes tipos de datos. Estos se explicarán a continuación.
Una cuestión clave es si hay soporte de hardware para un tipo de datos en particular. El
soporte de hardware significa que una o más instrucciones esperan datos en un formato
particular, y el usuario no es libre de elegir un formato diferente. Por ejemplo, los contadores
tienen la peculiar costumbre de escribir números negativos con el signo menos a la derecha del
número en lugar de a la izquierda, donde lo ponen los científicos informáticos. Suponga que, en
un esfuerzo por impresionar a su jefe, el director del centro de cómputo de una empresa de
contabilidad cambia todos los números de todas las computadoras para usar el bit más a la
derecha (en lugar del bit más a la izquierda) como bit de signo. Sin duda, esto causaría una gran
impresión en el jefe, porque todo el software dejaría de funcionar correctamente al instante. El
hardware espera un cierto formato para los números enteros y no funciona correctamente
cuando se le da cualquier otra cosa.
Ahora considere otra firma de contabilidad, esta acaba de obtener un contrato para verificar
la deuda federal (cuánto el gobierno de los Estados Unidos les debe a todos). El uso de aritmética
de 32 bits no funcionaría aquí porque los números involucrados son mayores que 232 (alrededor
de 4 mil millones). Una solución es usar dos enteros de 32 bits para representar cada número, lo
que da 64 bits en total. Si la máquina no es compatiblePrecisión doble números, toda la
aritmética en ellos tendrá que hacerse en software, pero las dos partes pueden estar en cualquier
orden ya que al hardware no le importa. Este es un ejemplo de un tipo de datos sin soporte de
hardware y, por lo tanto, sin una representación de hardware requerida. En las siguientes
secciones, veremos los tipos de datos que son compatibles con el hardware y, por lo tanto, para
los que se requieren formatos específicos.

5.2.1 Tipos de datos numéricos

Los tipos de datos se pueden dividir en dos categorías: numéricos y no numéricos. El principal de
los tipos de datos numéricos son los enteros. Vienen en muchas longitudes, normalmente de 8, 16, 32 y
64 bits. Los enteros cuentan cosas (p. Ej., El número de destornilladores
SEGUNDO. 5.2 TIPOS DE DATOS 359

una ferretería tiene en stock), identificar cosas (por ejemplo, números de cuentas bancarias) y mucho
más. La mayoría de las computadoras modernas almacenan números enteros en notación binaria en
complemento a dos, aunque también se han utilizado otros sistemas en el pasado. Los números
binarios se analizan en el Apéndice A.
Algunas computadoras admiten enteros sin firmar y con signo. Para un entero sin signo, no
hay bit de signo y todos los bits contienen datos. Este tipo de datos tiene la ventaja de un bit
adicional, por lo que, por ejemplo, una palabra de 32 bits puede contener un solo entero sin
signo en el rango de 0 a 232 - 1, inclusive. Por el contrario, un entero de 32 bits con signo en
complemento a dos solo puede manejar números hasta 231 - 1, pero, por supuesto, también
puede manejar números negativos.
Para los números que no se pueden expresar como un número entero, como 3.5, se utilizan
números de coma flotante. Estos se analizan en el Apéndice B. Tienen longitudes de 32,
64 o 128 bits. La mayoría de las computadoras tienen instrucciones para realizar operaciones aritméticas en
coma flotante. Muchas computadoras tienen registros separados para almacenar operandos enteros y para
almacenar operandos de coma flotante.
Algunos lenguajes de programación, en particular COBOL, permiten números decimales
como tipo de datos. Las máquinas que desean ser compatibles con COBOL a menudo admiten
números decimales en el hardware, generalmente codificando un dígito decimal en 4 bits y luego
empaquetando dos dígitos decimales por byte (formato decimal de código binario). Sin embargo,
la aritmética binaria no funciona correctamente en números decimales empaquetados, por lo
que se necesitan instrucciones especiales de corrección decimalaritmética. Estas instrucciones
necesitan conocer la ejecución del bit 3. Esta es la razón por la que el código de condición a
menudo contiene un bit de ejecución auxiliar. Aparte, el infame problema Y2K (año 2000) fue
causado por programadores de COBOL que decidieron que podían representar el año en dos
dígitos decimales (8 bits) en lugar de cuatro dígitos decimales (o un número binario de 8 bits),
que puede contienen incluso más valores (256) que dos dígitos decimales (100).

5.2.2 Tipos de datos no numéricos

Aunque la mayoría de las primeras computadoras se ganaban la vida procesando números,


las computadoras modernas se utilizan a menudo para aplicaciones no numéricas, como correo
electrónico, navegación por Internet, fotografía digital y creación y reproducción multimedia.
Para estas aplicaciones, se necesitan otros tipos de datos y con frecuencia son compatibles con
instrucciones de nivel ISA. Los personajes son claramente importantes aquí, aunque no todas las
computadoras brindan soporte de hardware para ellos. Los códigos de caracteres más comunes
son ASCII y Unicode. Estos admiten caracteres de 7 bits y caracteres de 16 bits, respectivamente.
Ambos se analizaron en el cap. 2.
No es raro que el nivel ISA tenga instrucciones especiales destinadas a manejar cadenas de
caracteres, es decir, ejecuciones consecutivas de caracteres. Estas cadenas a veces están
delimitadas por un carácter especial al final. Alternativamente, se puede usar un campo de
longitud de cadena para realizar un seguimiento del final. Las instrucciones pueden realizar
copias, buscar, editar y otras funciones en las cadenas.
360 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

Los valores booleanos también son importantes. Un valor booleano puede tomar uno de dos
valores: verdadero o falso. En teoría, un solo bit puede representar un booleano, con 0 como falso y 1
como verdadero (o viceversa). En la práctica, se utiliza un byte o una palabra por valor booleano porque
los bits individuales de un byte no tienen sus propias direcciones y, por lo tanto, son difíciles de acceder.
Un sistema común usa la convención de que 0 significa falso y todo lo demás significa verdadero.

La única situación en la que un valor booleano se representa normalmente por 1 bit es cuando hay una
matriz completa de ellos, por lo que una palabra de 32 bits puede contener 32 valores booleanos. Esta
estructura de datos se llamamapa de bits y ocurre en muchos contextos. Por ejemplo, se puede utilizar un
mapa de bits para realizar un seguimiento de los bloques libres en un disco. Si el disco tiene
norte bloques, entonces el mapa de bits tiene norte bits.

Nuestro último tipo de datos es el puntero, que es solo una dirección de máquina. Ya hemos
visto punteros repetidamente. En el micrófonoX máquinas, SP, PC, LV, y CPP son todos ejemplos de
punteros. Acceder a una variable a una distancia fija de un puntero, que es la formaYO CARGO
funciona, es extremadamente común en todas las máquinas. Si bien los punteros son útiles,
también son la fuente de una gran cantidad de errores de programación, a menudo con
consecuencias muy graves. Deben usarse con mucho cuidado.

5.2.3 Tipos de datos en el Core i7

El Core i7 admite enteros en complemento a dos con signo, enteros sin signo, números decimales
codificados en binario y números de coma flotante IEEE 754, como se muestra en la figura 5-6. Debido a
sus orígenes como una humilde máquina de 8 bits / 16 bits, maneja enteros de estas longitudes así
como de 32 bits, con numerosas instrucciones para realizar operaciones aritméticas, booleanas y
comparaciones sobre ellos. El procesador se puede ejecutar opcionalmente en modo de 64 bits, que
también admite registros y operaciones de 64 bits. Los operandos hacen
no tiene que estar alineado en la memoria, pero mejor el rendimiento se logra si la palabra
los vestidos son múltiplos de 4 bytes.

Escribe 8 bits 16 bits 32 bits 64 bits


Entero con signo × × × × (64 bits)
Entero sin signo × × × × (64 bits)
Entero decimal codificado en binario ×
Punto flotante × ×

Figura 5-6. Los tipos de datos numéricos del Core i7. Los tipos admitidos están marcados con×.
Los tipos marcados con "64 bits" solo se admiten en el modo de 64 bits.

El Core i7 también es bueno para manipular caracteres ASCII de 8 bits: hay instrucciones
especiales para copiar y buscar cadenas de caracteres. Estas instrucciones se pueden utilizar
tanto con cadenas cuya longitud se conoce de antemano como con cadenas cuyo final está
marcado. A menudo se utilizan en bibliotecas de manipulación de cadenas.
SEGUNDO. 5.2 TIPOS DE DATOS 361

5.2.4 Tipos de datos en la CPU ARM OMAP4430

La CPU ARM OMAP4430 admite una amplia gama de formatos de datos, como se muestra en
la Fig. 5-7. Solo para enteros, puede admitir operandos de 8, 16 y 32 bits, tanto con signo como
sin signo. El manejo de tipos de datos pequeños en el OMAP4430 es un poco más inteligente que
en el Core i7. Internamente, el OMAP4430 es una máquina de 32 bits con rutas de datos e
instrucciones de 32 bits. Para cargas y almacenes, el programa puede especificar el tamaño y el
signo del valor que se cargará (por ejemplo, cargar byte con signo:LDRSB). A continuación, las
instrucciones de carga convierten el valor en un valor comparable de 32 bits. De manera similar,
las tiendas también especifican el tamaño y el signo del valor para escribir en la memoria, y solo
acceden a la parte especificada del registro de entrada.
Los enteros con signo utilizan el complemento a dos. Se incluyen operandos de coma
flotante de 32 y 64 bits que cumplen con el estándar IEEE 754. No se admiten números decimales
codificados en binario. Todos los operandos deben estar alineados en la memoria. Los tipos de
datos de caracteres y cadenas no son compatibles con instrucciones especiales de hardware. Son
manipulado íntegramente en software.

Escribe 8 bits 16 bits 32 bits 64 bits


Entero con signo × × ×
Entero sin signo × × ×
Entero decimal codificado en binario

Punto flotante × ×

Figura 5-7. Los tipos de datos numéricos de la CPU ARM OMAP4430. Los tipos admitidos están
marcados con×.

5.2.5 Tipos de datos en la CPU ATmega168 AVR

El ATmega168 tiene un número muy limitado de tipos de datos. Con una excepción, todos los registros
tienen un ancho de 8 bits, por lo que los enteros también tienen un ancho de 8 bits. Los caracteres también
tienen 8 bits de ancho. En esencia, el único tipo de datos que realmente es compatible con el hardware.
para operaciones aritméticas es el byte de 8 bits, como se muestra en la figura 5-8.

Escribe 8 bits 16 bits 32 bits 64 bits


Entero con signo ×
Entero sin signo × ×
Entero decimal codificado en binario

Punto flotante

Figura 5-8. Los tipos de datos numéricos ATmega168. Tipos admitidos están marcados
con×.
362 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

Para facilitar el acceso a la memoria, el ATmega168 también incluye soporte limitado para
punteros sin firmar de 16 bits. Los punteros de 16 bitsX, Y, y Z, se puede formar a partir de la
concatenación de pares de registros de 8 bits R26 / R27, R28 / R29, y R30 / R31, respectivamente.
Cuando una carga usaX, Y, o Z como operando de dirección, el procesador también incrementará
o reducirá opcionalmente el valor según sea necesario.

5.3 FORMATOS DE INSTRUCCIONES

Una instrucción consta de un código de operación, generalmente junto con información


adicional, como de dónde provienen los operandos y adónde van los resultados. El tema general
de especificar dónde están los operandos (es decir, sus direcciones) se llama
direccionamiento y se discutirá en detalle más adelante en esta sección.
La Figura 5-9 muestra varios formatos posibles para instrucciones de nivel 2. Una instrucción
siempre tiene un código de operación para indicar qué hace la instrucción. Puede haber cero
una, dos o tres direcciones presentes.

OPCODE OPCODE DIRECCIÓN

(a) (B)

OPCODE DIRECCION 1 DIRECCION 2 OPCODE ADDR1 ADDR2 ADDR3

(C) (D)

Figura 5-9. Cuatro formatos de instrucción comunes: (a) Instrucción de dirección cero.
(b) Instrucción de una dirección (c) Instrucción de dos direcciones. (d) Instrucción de tres
direcciones.

En algunas máquinas, todas las instrucciones tienen la misma longitud; en otros, puede
haber muchas longitudes diferentes. Las instrucciones pueden ser más cortas, iguales o más
largas que la longitud de la palabra. Tener todas las instrucciones de la misma longitud es más
simple y facilita la decodificación, pero a menudo desperdicia espacio, ya que todas las
instrucciones deben ser tan largas como la más larga. También son posibles otras
compensaciones. La figura 5-10 muestra algunas posibles relaciones entre la longitud de la
instrucción y la longitud de la palabra.

5.3.1 Criterios de diseño para formatos de instrucción

Cuando un equipo de diseño de computadoras tiene que elegir formatos de


instrucción para su máquina, debe considerar varios factores. La dificultad de esta decisión
no debe subestimarse. La decisión sobre el formato de instrucción debe tomarse al
principio del diseño de una nueva computadora. Si la computadora tiene éxito comercial, el
conjunto de instrucciones puede sobrevivir durante 40 años o más. La capacidad de agregar
SEGUNDO. 5.3 FORMATOS DE INSTRUCCIONES 363

1 palabra 1 palabra 1 palabra

Instrucción Instrucción Instrucción Instrucción


Instrucción Instrucción Instrucción Instrucción Instr. Instr.
Instrucción Instrucción Instrucción
Instrucción
Instrucción Instrucción Instrucción

(a) (B) (C)

Figura 5-10. Algunas posibles relaciones entre la instrucción y la longitud de las palabras.

nuevas instrucciones y aprovechar otras oportunidades que surjan durante un período extendido período
es de gran importancia, pero solo si la arquitectura y la empresa que la construye
sobreviven lo suficiente para que la arquitectura sea un éxito.
La eficiencia de un ISA en particular depende en gran medida de la tecnología con la que se
implementará la computadora. Durante un largo período de tiempo, esta tecnología sufrirá
grandes cambios, y algunas de las opciones de ISA se considerarán (en retrospectiva 20/20) como
desafortunadas. Por ejemplo, si los accesos a la memoria son rápidos, un diseño basado en pila
(como IJVM) es bueno, pero si son lentos, entonces tener muchos registros (como la CPU
OMAP4430 ARM) es el camino a seguir. Se invita a los lectores que piensen que esta elección es
fácil a buscar un trozo de papel y escribir sus predicciones para (1) una velocidad de reloj de CPU
típica y (2) un tiempo de acceso a RAM típico para computadoras de 20 años en el futuro. Doble
este papel con cuidado y guárdelo durante 20 años. Luego desdóblalo y léelo. Los desafiados por
la humildad pueden olvidar el papelito y publicar sus predicciones en Internet ahora mismo.

Por supuesto, incluso los diseñadores con visión de futuro pueden no ser capaces de tomar
todas las decisiones correctas. E incluso si pudieran, también tienen que lidiar con el corto plazo.
Si este elegante ISA es un poco más caro que sus feos competidores actuales, es posible que la
empresa no sobreviva lo suficiente para que el mundo pueda apreciar la elegancia del ISA.

En igualdad de condiciones, las instrucciones breves son mejores que las largas. Un programa que
consta denorte Las instrucciones de 16 bits ocupan solo la mitad del espacio de memoria que norte
Instrucciones de 32 bits. Con los precios de la memoria en constante descenso, este factor podría ser menos importante
en el futuro, si no fuera por el hecho de que el software está haciendo metástasis incluso más rápido de lo que están
cayendo los precios de la memoria.
Además, minimizar el tamaño de las instrucciones puede hacer que sean más difíciles
de decodificar o de superponerlas. Por lo tanto, el logro del tamaño mínimo de instrucción
debe sopesarse con el tiempo requerido para decodificar y ejecutar las instrucciones.
Otra razón para minimizar la longitud de las instrucciones ya es importante y cada vez lo es más
con procesadores más rápidos: el ancho de banda de la memoria (la cantidad de bits / seg que la
memoria es capaz de suministrar). El impresionante crecimiento de las velocidades del procesador
durante las últimas décadas no ha ido acompañado de aumentos iguales en el ancho de banda de la
memoria. Una restricción cada vez más común en los procesadores proviene de la incapacidad del
sistema de memoria para proporcionar instrucciones y operandos tan rápido como
364 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

el procesador puede consumirlos. Cada memoria tiene un ancho de banda que está determinado
por su tecnología y diseño de ingeniería. El cuello de botella del ancho de banda se aplica no solo
a la memoria principal sino también a todas las cachés.
Si el ancho de banda de una caché de instrucciones es t bps y la duración media de la instrucción
es r bits, la caché puede entregar como máximo t / r instrucciones por segundo. Tenga en cuenta que
este es unlimite superior sobre la velocidad a la que el procesador puede ejecutar instrucciones, aunque
existen esfuerzos de investigación actuales para romper incluso esta barrera aparentemente
insuperable. Claramente, la velocidad a la que se pueden ejecutar las instrucciones (es decir, la
velocidad del procesador) puede estar limitada por la longitud de la instrucción. Instrucciones más
cortas significan un procesador más rápido. Dado que los procesadores modernos pueden ejecutar
varias instrucciones en cada ciclo de reloj, es imperativo obtener varias instrucciones por ciclo de reloj.
Este aspecto de la caché de instrucciones hace que el tamaño de las instrucciones sea un criterio de
diseño importante que tiene importantes implicaciones para el rendimiento.
Un segundo criterio de diseño es espacio suficiente en el formato de instrucción para expresar
todas las operaciones deseadas. Una maquina con 2norte operaciones con todas las instrucciones
menores que norte bits es imposible. Simplemente no habrá suficiente espacio en el código de
operación para indicar qué instrucción se necesita. Y la historia ha demostrado una y otra vez la locura
de no dejar una cantidad sustancial de códigos de operación libres para futuras adiciones al conjunto
de instrucciones.
Un tercer criterio se refiere al número de bits en un campo de dirección. Considere el diseño
de una máquina con un carácter de 8 bits y una memoria principal que debe contener 232

caracteres. Los diseñadores pueden optar por asignar direcciones consecutivas a unidades de
8, 16, 24 o 32 bits, entre otras posibilidades.
Imagínese lo que sucedería si el equipo de diseño degenerara en dos facciones en
guerra, una abogando por hacer del byte de 8 bits la unidad básica de memoria y la
otra abogando por la palabra de 32 bits. El primer grupo propondría un recuerdo de 2
32 bytes, numerados 0, 1, 2, 3, ..., 4.294.967.295. Este último grupo propondría una

memoria de 230 palabras numeradas 0, 1, 2, 3, ..., 1.073.741.823.


El primer grupo señalaría que para comparar dos caracteres en la organización de
palabras de 32 bits, el programa no solo tendría que buscar las palabras que
contienen los caracteres, sino que también tendría que extraer cada carácter de su
palabra para compararlos. . Hacerlo cuesta instrucciones adicionales y, por lo tanto,
desperdicia espacio. La organización de 8 bits, por otro lado, proporciona una
dirección para cada carácter, lo que facilita mucho la comparación.
Los partidarios de la palabra de 32 bits tomarían represalias señalando que su propuesta solo
requiere 230 direcciones separadas, dando una longitud de dirección de sólo 30 bits, mientras que la
propuesta de bytes de 8 bits requiere 32 bits para direccionar la misma memoria. Una dirección más
corta significa una instrucción más corta, que no solo ocupa menos espacio, sino que también requiere
menos tiempo para buscar. Alternativamente, podrían retener la dirección de 32 bits para hacer
referencia a una memoria de 16 GB en lugar de una memoria insignificante de 4 GB.
Este ejemplo demuestra que para obtener una resolución de memoria más fina, se debe
pagar el precio de direcciones más largas y, por lo tanto, de instrucciones más largas. Lo último
en resolución es una organización de memoria en la que cada bit es directamente direccionable
SEGUNDO. 5.3 FORMATOS DE INSTRUCCIONES 365

(por ejemplo, el Burroughs B1700). En el otro extremo hay una memoria que consta de palabras muy
largas (por ejemplo, la serie CDC Cyber tenía palabras de 60 bits).
Los sistemas informáticos modernos han llegado a un compromiso que, en cierto sentido,
captura lo peor de ambos. Requieren todos los bits necesarios para direccionar bytes
individuales, pero los accesos a la memoria leen una, dos o, a veces, cuatro palabras a la vez. La
lectura de 1 byte de la memoria en el Core i7, por ejemplo, trae un mínimo de 8 bytes y
probablemente una línea completa de caché de 64 bytes.

5.3.2 Expandir códigos de operación

En la sección anterior vimos cómo las direcciones cortas y la buena resolución de memoria
pueden intercambiarse entre sí. En esta sección examinaremos nuevas compensaciones, que
involucran tanto códigos de operación como direcciones. Considere un (n + k) instrucción de bits
con un k-bit opcode y un solo norte-dirección de bit. Esta instrucción permite 2k diferentes
operaciones y 2norte celdas de memoria direccionables. Alternativamente, el mismon + k
los bits se pueden dividir en un (k - 1) código de operación de bits y un (n + 1) dirección de bit, lo
que significa solo la mitad de instrucciones pero el doble de memoria direccionable, o la misma
cantidad de memoria pero con el doble de resolución. A (k + 1) código de operación de bits y un (
norte - 1) la dirección de bit proporciona más operaciones, pero el precio es un número menor de
celdas direccionables o una resolución más pobre y la misma cantidad de memoria direccionable.
Son posibles compensaciones bastante sofisticadas entre los bits de código de operación y los
bits de dirección, así como los más simples que se acaban de describir. El esquema discutido en
los siguientes párrafos se llamaexpandiendo opcode.
El concepto de un código de operación en expansión se puede ver más claramente con un ejemplo
simple. Considere una máquina en la que las instrucciones tienen una longitud de 16 bits y las direcciones de 4
bits, como se muestra en la figura 5-11. Esta situación podría ser razonable para una máquina que tiene 16
registros (por lo tanto, una dirección de registro de 4 bits) en la que toda la aritmética
tienen lugar las operaciones. Un diseño sería un código de operación de 4 bits y tres direcciones en
cada instrucción, dando 16 instrucciones de tres direcciones.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Código de operación Dirección 1 Dirección 2 Direccion 3

Figura 5-11. Una instrucción con un 4 bits código de operación y tres campos de dirección de 4 bits.

Sin embargo, si los diseñadores necesitan 15 instrucciones de tres direcciones, 14 instrucciones de dos
direcciones, 31 instrucciones de una dirección y 16 instrucciones sin dirección en absoluto, pueden usar los códigos de
operación 0 a 14 como instrucciones de tres direcciones pero interpretar el código de operación 15 de manera
diferente. (vea la Fig. 5-12).
El código de operación 15 significa que el código de operación está contenido en los bits 8 a 15 en lugar
de los bits 12 a 15. Los bits 0 a 3 y 4 a 7 forman dos direcciones, como es habitual. Las 14 dos direcciones
366 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

16 bits

4 bits 0000 xxxx yyyy zzzz 0001 15 3 direcciones


código de operación xxxx yyyy zzzz 0010 xxxx
instrucciones
yyyy zzzz

...
1100 xxxx yyyy zzzz 1101
xxxx yyyy zzzz 1110 xxxx
yyyy zzzz

8 bits 1111 0000 yyyy zzzz 1111 14 2 direcciones


código de operación 0001 yyyy zzzz 1111 0010
instrucciones
yyyy zzzz
...

1111 1011 yyyy zzzz 1111


1100 yyyy zzzz 1111 1101
yyyy zzzz

12 bits 1111 1110 0000 zzzz 1111 31 1 dirección


código de operación 1110 0001 zzzz
instrucciones
...

1111 1110 1110 zzzz 1111


1110 1111 zzzz 1111 1111
0000 zzzz 1111 1111 0001
zzzz
...

1111 1111 1101 zzzz 1111


1111 1110 zzzz

16 bits 1111 1111 1111 0000 16 dirección 0


código de operación 1111 1111 1111 0001
instrucciones
1111 1111 1111 0010
...

1111 1111 1111 1101


1111 1111 1111 1110
1111 1111 1111 1111

15 12 11 8 7 4 3 0
Número de bits

Figura 5-12. Un código de operación en expansión que permite 15 instrucciones de tres direcciones, 14
instrucciones de dos direcciones, 31 instrucciones de una dirección y 16 instrucciones de dirección cero.
Los campos marcadosxxxx, aaaa, y zzzz son campos de dirección de 4 bits.
SEGUNDO. 5.3 FORMATOS DE INSTRUCCIONES 367

todas las instrucciones tienen 1111 en los 4 bits más a la izquierda y números de 0000 a 1101 en los bits
8 a 11. Las instrucciones con 1111 en los 4 bits más a la izquierda y 1110 o 1111 en los bits 8 a 11 serán
tratadas especialmente. Se tratarán como si sus códigos de operación estuvieran en los bits 4 a 15. El
resultado son 32 códigos de operación nuevos. Debido a que solo se necesitan 31, el código de
operación 111111111111 se interpreta en el sentido de que el código de operación real está en los bits
0 a 15, dando 16 instrucciones sin dirección.
A medida que avanzábamos en esta discusión, el código de operación se hizo más y más largo: las instrucciones
de tres direcciones tienen un código de operación de 4 bits, las instrucciones de dos direcciones tienen un código de
operación de 8 bits, las instrucciones de una dirección tienen un código de operación de 12 bits, y las instrucciones de
dirección cero tienen un código de operación de 16 bits.
La idea de expandir los códigos de operación demuestra una compensación entre el espacio para los
códigos de operación y el espacio para otra información. En la práctica, expandir los códigos de operación no
es tan limpio y regular como en nuestro ejemplo. De hecho, la capacidad de utilizar tamaños variables de
códigos de operación se puede aprovechar de dos formas. Primero, todas las instrucciones pueden
mantenerse con la misma longitud, asignando los códigos de operación más cortos a las instrucciones que
necesitan más bits para especificar otras cosas. En segundo lugar, el tamaño delpromedio la instrucción se
puede minimizar eligiendo códigos de operación que sean más cortos para instrucciones comunes y más
largos para instrucciones raras.
Llevando la idea de códigos de operación de longitud variable al extremo, es posible
minimizar la longitud promedio de instrucción codificando cada instrucción para minimizar el
número de bits necesarios. Desafortunadamente, esto daría como resultado instrucciones de
varios tamaños que ni siquiera se alinean en los límites de bytes. Si bien ha habido ISA que tenían
esta propiedad (por ejemplo, el desafortunado Intel 432), la importancia de la alineación es tan
grande para la rápida decodificación de instrucciones que este grado de optimización es casi con
certeza contraproducente.

5.3.3 Los formatos de instrucción de Core i7

Los formatos de instrucción del Core i7 son muy complejos e irregulares, y tienen hasta seis
campos de longitud variable, de los cuales cinco son opcionales. El patrón general se muestra en la
figura 5-13. Esta situación se produjo porque la arquitectura evolucionó a lo largo de muchas
generaciones e incluyó algunas malas elecciones desde el principio. En nombre de la compatibilidad con
versiones anteriores, estas primeras decisiones no se pudieron revertir más adelante. En general, para
las instrucciones de dos operandos, si un operando está en la memoria, es posible que el otro no esté
en la memoria. Por lo tanto, existen instrucciones para agregar dos registros, agregar un registro a la
memoria y agregar memoria a un registro, pero no para agregar una palabra de memoria a otra
palabra de memoria.
En arquitecturas Intel anteriores, todos los códigos de operación eran de 1 byte, aunque el concepto de
byte de prefijo se usaba ampliamente para modificar algunas instrucciones. Abyte de prefijo es un código de
operación adicional pegado al frente de una instrucción para cambiar su acción. los
AMPLIO La instrucción en IJVM es un ejemplo de un byte de prefijo. Desafortunadamente, en algún momento
durante la evolución, Intel se quedó sin códigos de operación, por lo que un código de operación, 0xFF, fue
designado comocódigo de escape para permitir un segundo byte de instrucción.
368 LOS CONJUNTO DE INSTRUCCIONES NIVEL DE ARQUITECTURA CAP. 5

Bytes 0-5 1-2 0-1 0-1 0-4 0-4


PREFIJO OPCODE MODO HERMANO DESPLAZAMIENTO INMEDIATO

Bits 6 11 Bits 2 3 3
INSTRUCCIÓN ÍNDICE DE ESCALA BASE

¿Qué operando es la fuente?

Byte / palabra

Bits 2 3 3
MODIFICACIÓN REG R/M

Figura 5-13. Los formatos de instrucción Core i7.

Los bits individuales en los códigos de operación del Core i7 no brindan mucha información
sobre la instrucción. La única estructura en el campo del código de operación es el uso del bit de
orden inferior en algunas instrucciones para indicar byte / palabra, y el uso del bit contiguo para
indicar si la dirección de memoria (si está presente) es el origen o el destino. . Por lo tanto, en
general, el código de operación debe decodificarse completamente para determinar qué clase de
operación se va a realizar y, por lo tanto, cuánto tiempo dura la instrucción. Esto dificulta las
implementaciones de alto rendimiento, ya que es necesaria una decodificación extensa antes de
que se pueda determinar dónde comienza la siguiente instrucción.
Después del byte del código de operación en la mayoría de las instrucciones que hacen referencia
a un operando en la memoria hay un segundo byte que dice todo sobre el operando. Estos 8 bits se
dividen en 2 bits.MODIFICACIÓN campo y dos campos de registro de 3 bits, REG y R / M. A veces, los primeros
3 bits de este byte se utilizan como una extensión para el código de operación, dando un total de 11 bits
para el código de operación. Sin embargo, el campo de modo de 2 bits significa que solo hay cuatro
formas de direccionar operandos y uno de los operandos siempre debe ser un registro. Lógicamente,
cualquiera deEAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP debe ser especificable como cualquiera de los registros,
pero las reglas de codificación prohíben algunas combinaciones y las usan para casos especiales.
Algunos modos requieren un byte adicional, llamadoSIB (escala, índice, base), dando una
especificación adicional. Este esquema no es ideal, sino un compromiso dadas las demandas
competitivas de compatibilidad con versiones anteriores y el deseo de agregar nuevas características no
previstas originalmente.
Además de todo esto, algunas instrucciones tienen 1, 2 o 4 bytes más que especifican una
dirección de memoria (desplazamiento) y posiblemente otros 1, 2 o 4 bytes que contienen una
constante (operando inmediato).

5.3.4 Formatos de instrucción de la CPU ARM OMAP4430

El OMAP4430 ARM ISA consta de instrucciones de 16 y 32 bits, alineadas en la


memoria. Las instrucciones son generalmente simples y especifican una sola acción.
Una instrucción aritmética típica especifica dos registros para suministrar la fuente
SEGUNDO. 5.3 FORMATOS DE INSTRUCCIONES 369

operandos y un registro de destino único. Las instrucciones de 16 bits son versiones reducidas de
la instrucción de 32 bits. Realizan las mismas operaciones, pero permiten solo dos operandos de
registro (es decir, el registro de destino debe ser el mismo que una de las entradas) y solo los
primeros ocho registros pueden especificarse como entradas. Los arquitectos de ARM llamaron a
esta versión más pequeña de ARM ISA el Thumb ISA.
Las variantes adicionales permiten que las instrucciones proporcionen una constante sin
signo de 3, 8, 12, 16 o 24 bits en lugar de uno de los registros. Para una instrucción de carga, se
suman dos registros (o un registro y una constante con signo de 8 bits) para especificar la
dirección de memoria a leer. Los datos se escriben en el otro registro especificado.
El formato de las instrucciones ARM de 32 bits se ilustra en la figura 5-14. El lector
atento notará que algunos de los formatos tienen los mismos campos (por ejemplo,
MULTIPLICACIÓN LARGA y INTERCAMBIO). En el caso de la INTERCAMBIO instrucción, el decodificador sabe
que la instrucción es una INTERCAMBIO sólo cuando ve que la combinación de valores de campo
para el MUL es ilegal. Se han agregado formatos adicionales para extensiones de instrucciones y
Thumb ISA. En el momento de escribir este artículo, la cantidad de formatos de instrucción era 21
y estaba aumentando. (¿Puede pasar mucho tiempo antes de que veamos a alguna empresa
anunciando la "máquina RISC más compleja del mundo"?)
ciones, sin embargo, todavía utilizan los formatos que se muestran en la figura.

31 2827 1615 87 0 Tipo de instrucción


Cond 0 0 I Código de operación S Rn Rd Operand2 Procesamiento de datos / Transferencia PSR

Cond 0 0 0 0 0 0 AS Rd Rn RS 1001 Rm Multiplicar

Cond 0 0 0 0 1 UAS RdHi RdLo RS 1001 Rm Multiplicar largo

Cond 00010B00 Rn Rd 00001001 Rm Intercambio

Cond 0 1 IPUBWL Rn Rd Compensar Cargar / Almacenar Byte / Palabra

Cond 1 0 0 PUSWL Rn Lista de registro Cargar / almacenar varios

Cond 0 0 0 PU 1 WL Rn Rd Desplazamiento1 1 SH 1 Desplazamiento2 Transferencia de media palabra: compensación inmediata

Cond 0 0 0 PU 0 WL Rn Rd 0 0 0 0 1 SH 1 Rm Transferencia de media palabra: compensación de registro

Cond 101L Compensar Rama

Cond 00010010111111 1 1 1 1 1 1 1 1 0 0 0 1 CRd Rn Intercambio de sucursales

Cond 0 PUNWL Rn CPNum Offset Transferencia de datos del coprocesador

Cond 1 1 1 0 Op1 CRn CRd CPNum Op2 0 CRm Rd Operación de datos del coprocesador

Cond 1 1 1 0 Op1 L CRn 1 1 1 CPNum Op2 1 CRm Transferencia de registro del coprocesador

Cond 1 Número SWI Interrupción de software

Figura 5-14. Los formatos de instrucción ARM de 32 bits.

Los bits 26 y 27 de cada instrucción son la primera parada para determinar el formato de
instrucción y le dicen al hardware dónde encontrar el resto del código de operación, si hay más.
Por ejemplo, si los bits 26 y 27 son ambos cero, y el bit 25 es cero (el operando no es inmediato) y
el desplazamiento del operando de entrada no es ilegal (lo que indica que la instrucción es un
intercambio de multiplicación o bifurcación), entonces ambas fuentes son registros. Si el bit 25 es
uno, entonces una fuente es un registro y la otra es una constante en el rango de 0 a 4095.
370 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

En ambos casos, el destino es siempre un registro. Se proporciona suficiente espacio de codificación


para hasta 16 instrucciones, todas las cuales se utilizan actualmente.
Con instrucciones de 32 bits, no es posible incluir una constante de 32 bits en la instrucción.
losMOVT La instrucción establece los 16 bits superiores de un registro de 32 bits, dejando espacio
para que otra instrucción establezca los 16 bits inferiores restantes. Es la única instrucción para
usar este formato.
Cada instrucción de 32 bits tiene el mismo campo de 4 bits en los bits más significativos (bits
28 a 31). Este es el campo de condición, que hace que cualquier instrucción sea uninstrucción
predispuesta. Una instrucción predicada se ejecuta normalmente en el procesador, pero antes
de escribir su resultado en un registro (o memoria), primero verifica la condición de la
instrucción. Para las instrucciones ARM, la condición se basa en el estado del registro de estado
del procesador (PSR). Este registro contiene las propiedades aritméticas de la última operación
aritmética (por ejemplo, cero, negativo, desbordado, etc.). Si no se cumple la condición, se
descarta el resultado de la instrucción condicional.
El formato de instrucción de bifurcación codifica el valor inmediato más grande, utilizado para
calcular una dirección de destino para bifurcaciones y llamadas a procedimientos de función. Esta
instrucción es especial, porque es la única en la que se necesitan 24 bits de datos para especificar una
dirección. Para esta instrucción, hay un código de operación único de 3 bits. La dirección es la dirección
de destino dividida por cuatro, lo que hace que el rango sea aproximadamente±225 bytes relativos a la
instrucción actual.
Claramente, los diseñadores de ARM ISA querían utilizar completamente cada combinación de bits,
incluidas las combinaciones de operandos que de otro modo serían ilegales, para especificar
instrucciones. El enfoque genera una lógica de decodificación extremadamente complicada, pero al
mismo tiempo, permite codificar el número máximo de operaciones en una instrucción de longitud fija
de 16 o 32 bits.

5.3.5 Formatos de instrucción ATmega168 AVR

El ATmega168 tiene seis formatos de instrucciones simples, como se ilustra en la figura 5-15. Las
instrucciones tienen una longitud de 2 o 4 bytes. El formato uno consta de un código de operación y dos
operandos de registro, los cuales son entradas y el otro también es la salida de la instrucción. los
AGREGAR instrucción para registros utiliza este formato, por ejemplo.
El formato 2 también es de 16 bits, que consta de 16 códigos de operación adicionales y un
número de registro de 5 bits. Este formato aumenta el número de operaciones codificadas en el
ISA a costa de reducir el número de operandos de instrucción a uno. Las instrucciones que
utilizan este formato realizan una operación unaria, tomando una única entrada de registro y
escribiendo la salida de la operación en el mismo registro. Ejemplos de este tipo de instrucción
incluyen "negar" e "incrementar".
El formato 3 tiene un operando inmediato sin firmar de 8 bits. Para acomodar un valor
inmediato tan grande en una instrucción de 16 bits, las instrucciones que usan esta
codificación pueden tener solo un operando de registro (usado como entrada y salida) y el
registro solo puede serR16-R31 (que limita la codificación del operando a 4 bits). También el
SEGUNDO. 5.3 FORMATOS DE INSTRUCCIONES 371

Formato
15 0

1 00cc ccrd dddd rrrr ALU: Código de operación (c) Rd, Rr

2 1001 010d dddd cccc ALU extendido: código de operación (c) Rd

3 01cc KKKK dddd KKKK ALU + Imm: Código de operación (c) Rd, #K

4 10T0 QQcd dddd cQQQ Cargar / almacenar: Id / st (c) X / Y / Z + Q, Rd

5 11cc KKKK KKKK KKKK Rama: br (c) PC + K

31 0

6 1001 010K KKKK 11cK KKKK KKKK KKKK KKKK

Llamar / jmp: llamar / jmp (c) #K

Figura 5-15. Los formatos de instrucción ATmega168 AVR.

número de Los bits de código de operación se reducen a la mitad, lo que permite que solo cuatro instrucciones utilicen este formato.
esteraSUBCI, SUBI, ORI, y Y YO).
El formato 4 codifica la instrucción de carga y almacenamiento, que incluye un operando inmediato sin
firmar de 6 bits. El registro base es un registro fijo no especificado en la codificación de instrucciones porque
está implícito en el código de operación de carga / almacenamiento.
Los formatos 5 y 6 se utilizan para saltos y llamadas a procedimientos. El primer formato
incluye un valor inmediato con signo de 12 bits que se agrega al valor de PC de la instrucción
para calcular el objetivo de la instrucción. El último formato expande el desplazamiento a 22 bits,
haciendo que la instrucción AVR tenga un tamaño de 32 bits.

5.4 DIRECCIÓN
La mayoría de las instrucciones tienen operandos, por lo que se necesita alguna forma de
especificar dónde están. Este tema, que discutiremos ahora, se llamadireccionamiento.

5.4.1 Modos de direccionamiento

Hasta ahora, hemos prestado poca atención a cómo se interpretan los bits de un campo de
dirección para encontrar el operando. Ha llegado el momento de investigar este tema, llamado
modos de dirección. Como veremos, hay muchas formas de hacerlo.
372 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

5.4.2 Direccionamiento inmediato

La forma más sencilla de que una instrucción especifique un operando es que la parte
de dirección de la instrucción contenga realmente el operando en sí en lugar de una
dirección u otra información que describa dónde está el operando. Tal operando se llama
operando inmediato porque se recupera automáticamente de la memoria al mismo tiempo que
se recupera la instrucción misma; por lo tanto, está inmediatamente disponible para su uso. Una
posible instrucción inmediata para cargar el registro.R1 con la constante 4 se muestra en la figura
5-16.
MOV R1 4

Figura 5-16. Una instrucción inmediata para cargar 4 en el registro 1.

El direccionamiento inmediato tiene la virtud de no requerir una referencia de memoria adicional


para buscar el operando. Tiene la desventaja de que de esta forma sólo se puede suministrar una
constante. Además, el número de valores está limitado por el tamaño del campo. Aún así, muchas
arquitecturas usan esta técnica para especificar pequeñas constantes enteras.

5.4.3 Direccionamiento directo

Un método para especificar un operando en la memoria es simplemente dar su dirección


completa. Este modo se llamadireccionamiento directo. Al igual que el direccionamiento inmediato, el
direccionamiento directo tiene un uso restringido: la instrucción siempre accederá exactamente a la
misma ubicación de memoria. Entonces, si bien el valor puede cambiar, la ubicación no. Por lo tanto, el
direccionamiento directo solo se puede utilizar para acceder a variables globales cuya dirección se
conoce en el momento de la compilación. Sin embargo, muchos programas tienen variables globales,
por lo que este modo se usa ampliamente. Los detalles de cómo la computadora sabe qué direcciones
son inmediatas y cuáles son directas se discutirán más adelante.

5.4.4 Registrar direccionamiento

El direccionamiento de registros es conceptualmente lo mismo que el direccionamiento directo, pero


especifica un registro en lugar de una ubicación de memoria. Debido a que los registros son tan importantes
(debido al acceso rápido y las direcciones cortas), este modo de direccionamiento es el más común en la
mayoría de las computadoras. Muchos compiladores hacen todo lo posible para determinar a qué variables se
accederá con mayor frecuencia (por ejemplo, un índice de bucle) y colocan estas variables en registros.

Este modo de direccionamiento se conoce simplemente como modo de registro. En


arquitecturas de carga / almacenamiento como la arquitectura ARM OMAP4420, casi todas las
instrucciones utilizan este modo de direccionamiento exclusivamente. La única vez que no se usa
este modo de direccionamiento es cuando un operando se mueve de la memoria a un registro (
LDR instrucción) o de un registro a la memoria (STR instrucción). Incluso para esas instrucciones,
uno de los operandos es un registro, de donde viene o va la palabra de memoria.
SEGUNDO. 5.4 DIRECCIONAMIENTO 373

5.4.5 Registrar direccionamiento indirecto

En este modo, el operando que se especifica proviene de la memoria o va a la memoria,


pero su dirección no está cableada en la instrucción, como en el direccionamiento directo. En
cambio, la dirección está contenida en un registro. Una dirección utilizada de esta manera se
llamapuntero. Una gran ventaja del direccionamiento indirecto de registros es que puede hacer
referencia a la memoria sin pagar el precio de tener una dirección de memoria completa en la
instrucción. También puede utilizar diferentes palabras de memoria en diferentes ejecuciones de
la instrucción.
Para ver por qué puede ser útil usar una palabra diferente en cada ejecución, imagine un
bucle que recorre los elementos de una matriz de enteros unidimensionales de 1024 elementos
para calcular la suma de los elementos en el registro R1. Fuera del bucle, algún otro registro,
digamos, R2, se puede configurar para que apunte al primer elemento de la matriz y a otro
registro, digamos, R3, se puede configurar para que apunte a la primera dirección más allá de la
matriz. Con 1024 enteros de 4 bytes cada uno, si la matriz comienza enA, la primera dirección
más allá de la matriz será A + 4096. El código de ensamblaje típico para realizar este cálculo se
muestra en la figura 5-17 para una máquina de dos direcciones.

MOV R1, # 0 ; acumular la suma en R1, inicialmente 0;


MOV R2, # A R2 = dirección de la matriz A
MOV R3, # A + 4096 ; R3 = dirección de la primera palabra más allá de A
BUCLE: AÑADIR R1, (R2) ; registrarse indirectamente a través de R2 para obtener el

AGREGAR R2, # 4 operando; incrementar R2 en una palabra (4 bytes); ¿Ya

CMP R2, R3 terminamos?

BUCLE BLT ; si R2 <R3, no hemos terminado, así que continúa

Figura 5-17. Un programa de ensamblaje genérico para calcular la suma de los


elementos de una matriz.

En este pequeño programa, usamos varios modos de direccionamiento. Las primeras tres
instrucciones usan el modo de registro para el primer operando (el destino) y el modo inmediato
para el segundo operando (una constante indicada por el signo #). La segunda instrucción pone el
Dirección de A en R2, no el contenido. Eso es lo que le dice el signo # al ensamblador. De manera
similar, la tercera instrucción coloca la dirección de la primera palabra más allá de la matriz enR3.

Lo que es interesante notar es que el cuerpo del bucle en sí no contiene ninguna dirección
de memoria. Utiliza registrar y registrar el modo indirecto en la cuarta instrucción. Utiliza el modo
de registro e inmediato en la quinta instrucción y el modo de registro dos veces en la sexta
instrucción. losBLT podría usar una dirección de memoria, pero lo más probable es que
especifique la dirección a la que se bifurcará con un desplazamiento de 8 bits en relación con el
BLT instrucción
en sí. Al evitar por completo el uso de direcciones de memoria, hemos
producido un ciclo corto y rápido. Además, este programa es realmente para el Core i7,
excepto que hemos cambiado el nombre de las instrucciones y los registros y cambiado el
374 EL NIVEL DE ARQUITECTURA DEL CONJUNTO DE INSTRUCCIONES CAP. 5

notación para que sea fácil de leer porque la sintaxis estándar en lenguaje ensamblador (MASM)
del Core i7 raya en lo extraño, un remanente de la vida anterior de la máquina como un
8088.
Vale la pena señalar que, en teoría, hay otra forma de realizar este cálculo sin utilizar el
direccionamiento indirecto de registros. El bucle podría haber contenido una instrucción
para agregarA para R1, tal como

AÑADIR R1, A

Luego, en cada iteración del ciclo, la instrucción en sí podría incrementarse en 4, de modo


que después de una iteración lea

AÑADIR R1, A + 4

y así sucesivamente hasta que esté listo.

Un programa que se modifica a sí mismo de esta manera se llama auto modificable


programa. La idea fue pensada nada menos que por John von Neumann y tenía sentido en las
primeras computadoras, que no tenían registro de direccionamiento indirecto. Hoy en día, los
programas que se modifican automáticamente se consideran de estilo horrible y difíciles de
entender. Tampoco se pueden compartir entre varios procesos al mismo tiempo. Además, ni
siquiera funcionarán correctamente en máquinas con una caché de nivel 1 dividida si la caché I
no tiene circuitos para realizar escrituras (porque los diseñadores asumieron que los programas
no se modifican a sí mismos). Por último, los programas que se modifican automáticamente
también fallarán en máquinas con espacios de datos e instrucciones separados. Con todo, esta es
una idea que ha venido y (afortunadamente) se ha ido.

5.4.6 Direccionamiento indexado

Con frecuencia es útil poder hacer referencia a palabras de memoria en un desplazamiento


conocido de un registro. Vimos algunos ejemplos con IJVM donde se hace referencia a las variables
locales dando su desplazamiento deLV. Direccionar la memoria dando un registro (explícito o implícito)
más un desplazamiento constante se llama direccionamiento indexado.
El acceso a la variable local en IJVM usa un puntero en la memoria (LV) en un registro más un
pequeño desplazamiento en la propia instrucción, como se muestra en la figura 4-19 (a). Sin
embargo, también es posible hacerlo al revés: el puntero de memoria en la instrucción y el
pequeño desplazamiento en el registro. Para ver cómo funciona, considere el siguiente cálculo.
Tenemos dos matrices unidimensionales de 1024 palabras cada una,A y B, y nosotros
deseo calcular AI Y BI para todos los pares y luego O estos 1024 productos booleanos
juntos para ver si hay al menos un par distinto de cero en el conjunto. Uno ap-
Proach sería poner la dirección de A en un registro, la dirección de B en un segundo
registro, y luego recorrerlos juntos al mismo tiempo, de manera análoga a lo que hicimos
en la figura 5-17. Esta forma de hacerlo funcionaría ciertamente, pero se puede hacer de
una manera mejor y más general, como se ilustra en la figura 5-18.
SEGUNDO. 5.4 DIRECCIONAMIENTO 375

MOV R1, # 0 ; acumula el OR en R1, inicialmente 0


MOV R2, # 0 ; R2 = índice, i, del producto corriente: A [i] Y B [i]; R3 =
MOV R3, # 4096 primer valor de índice que no se debe usar
BUCLE: MOV R4, A (R2) ; R4 = A [i]
Y R4, B (R2) ; R4 = A [i] Y B [i]
O R1, R4 ; O todos los productos booleanos en R1
AGREGAR R2, # 4 ; i = i + 4 (paso en unidades de 1 palabra = 4 bytes);
CMP R2, R3 ¿Ya terminamos?
BUCLE BLT ; si R2 <R3, no hemos terminado, así que continúa

Figura 5-18. Un programa de ensamblaje genérico para calcular el OR de AI Y BI


para dos matrices de 1024 elementos.

El funcionamiento de este programa es sencillo. Necesitamos cuatro registros aquí:

1. R1 : Contiene el OR acumulado de los términos del producto booleano.

2. R2 - El índice, I, que se utiliza para recorrer las matrices.


3. R3 - La constante 4096, que es el valor más bajo de I no usar.
4. R4 —Un registro de cero para sujetar cada producto a medida que se forma.

Después de inicializar los registros, ingresamos al ciclo de seis instrucciones. La instrucción en


CÍRCULO busca AI dentro R4. El cálculo de la fuente aquí utiliza el modo indexado. Un
registro,R2, y una constante, la dirección de A, se suman y se utilizan para ref-
erence memoria. La suma de estas dos cantidades va a la memoria pero no se almacena en
ningún registro visible para el usuario. La notación

MOV R4, A (R2)

significa que el destino usa el modo de registro con R4 ya que el registro y la fuente utilizan el
modo indexado, con A como el desplazamiento y R2 como el registro. SiA tiene el valor,
digamos, 124300, la instrucción real de la máquina para esto es es probable que se parezca a
la que se muestra en la Fig. 5-19.

MOV R4 R2 124300

Figura 5-19. Una posible representación de MOV R4, A (R2).

La primera vez a través del bucle, R2 es 0 (debido a que se inicializa de esa manera), por lo que
la palabra de memoria direccionada es A0, en la dirección 124300. Esta palabra se carga en R4.
La próxima vez a través del bucle, R2 es 4, por lo que la palabra de memoria direccionada es A1, a
124304 y así sucesivamente.

Como prometimos anteriormente, aquí el desplazamiento en la instrucción en sí es el


puntero de memoria y el valor en el registro es un pequeño entero que se incrementa durante el

También podría gustarte