por Dario Alejandro Alpern
Hola. Mi nombre es Dario Alpern y hoy vamos a ver los bloques que componen el procesador 80386.
Aquí podemos ver el diagrama en bloques del microprocesador 80386. Vamos a analizar las distintas unidades desde el bloque inferior derecho en sentido horario, hacia la izquierda y luego en la parte superior del diagrama, hacia la derecha. Las tres unidades que se encuentran en la parte superior, las de segmentación, paginación y de control del bus se activan cuando hay acceso a memoria.
El procesador interactúa con el bus para leer y escribir datos y para leer instrucciones que debe ejecutar el procesador. En inglés esto último se llama fetch. El fetch se realiza con menor prioridad que la lectura o escritura de datos.
Ahora vamos a ver como funciona la predecodificación de instrucciones.
La unidad de prebúsqueda tiene una cola de 16 bytes donde almacena los bytes que obtiene durante la lectura de instrucciones en los momentos que no hay lectura o escritura de datos. Si bien la cola es de bytes, el microprocesador puede leer hasta cuatro bytes por vez ya que su bus de datos es de 32 bits.
A continuación se encuentra la unidad de predecodificación que incluye una cola de 3 instrucciones predecodificadas. Esto significa que el procesador puede separar las instrucciones del flujo de bytes antes de ejecutarlas.
Para ello, en el código de máquina, que es lo que se encuentra en la cola de bytes de código, algunos bytes se interpretan como prefijos, que se ven en celeste en el diagrama, como 66 hexa, que es el prefijo de tamaño de operandos, que indica si el o los operandos de la instrucción tienen 16 o 32 bits. Otro prefijo es 67 hexa, que indica si el direccionamiento a memoria que posee la instrucción es de 16 o de 32 bits. También hay prefijos de registro de segmento, que indican cuál de los 6 registros de segmento usa la instrucción que accede a memoria. Por ejemplo, el prefijo 2E hexa se refiere a CS, el prefijo 3E hexa se refiere a DS, etc. Por último hay un prefijo LOCK, que es el F0 hexa, que cuando se usa con una instrucción que modifica memoria, activa la pata LOCK negado del bus de control del procesador para forzar una modificación atómica, sin que lo interrumpa otro hardware.
Cuando los bytes no se interpretan como prefijos, lo hacen como códigos de operación, que están en rojo en este diagrama. Los códigos de operación tienen uno o dos bytes (en procesadores actuales pueden tener tres bytes). Estos códigos de operación tienen campos que dicen qué registros se usan y en el caso de acceso a memoria, qué registros se usan para el direccionamiento indirecto. También se puede saber cuántos bytes se usan para el caso de direccionamiento inmediato, que es cuando el dato se encuentra en la propia instrucción, como ADD EAX,5. De esta manera es relativamente fácil en base al código de operación y los prefijos 66 hexa y 67 hexa saber cuántos bytes ocupa la instrucción completa.
Esto permite ir llenando la cola de instrucciones, lo que vacía parcialmente la cola de bytes de código y esto hace que el procesador vaya a buscar más bytes de instrucciones de memoria.
La unidad de control toma la instrucción más antigua que esté en la cola de instrucciones y en base a su código de operación determina si es una instrucción sencilla o compleja. Si la instrucción es sencilla, como las artiméticas y lógicas, la unidad de control genera los comandos para que la unidad aritmética y lógica ejecute la instrucción. En cambio si la instrucción es compleja, la unidad de control genera microinstrucciones que obtiene de la ROM de control para su posterior ejecución en la unidad aritmética y lógica y debido a ello, la instrucción se ejecuta en varios ciclos de reloj.
Como entrada de la unidad de control, también se encuentran los flags, ya que los saltos condicionales dependen de los valores de algunos de sus bits, como el indicador de cero, de acarreo, de signo, de sobrepasamiento o de paridad, para saber si tiene que saltar o no.
La unidad aritmética y lógica contiene los 8 registros de uso general de 32 bits, que se pueden subdividir en registros de 16 y de 8 bits. También incluye el registro EFLAGS que tiene diversos indicadores, como los cinco recién mencionados, la habilitación de interrupciones, el indicador de dirección para instrucciones de cadena. La explicación completa de este registro se encuentra en el video de la parte 1 de introducción a Assembler.
Aparte de los registros recién mencionados, la unidad aritmética y lógica tiene hardware para realizar sumas, restas, multiplicaciones y divisiones, y un barrel shifter para hacer rotaciones y desplazamientos de registros o celdas de memoria.
El barrel shifter es un módulo que permite desplazar hacia la izquierda o la derecha de 0 a 31 bits un número de 64 bits en un único ciclo de clock, sin utilizar flip flops. De esta manera, cualquier desplazamiento o rotación de un registro requiere 3 ciclos de reloj independientemente de la cantidad de bits a mover. El barrel shifter de 64 bits se puede implementar mediante 384 multiplexores.
Se puede observar como inicializar el barrel shifter y de dónde tomar el resultado para diferentes instrucciones: shift left (SHL), shift right (SHR), rotate left (ROL),rotate right (ROR), shift left double (SHLD) y shift right double (SHRD). También se puede utilizar el barrel shifter para rotate through carry left (RCL) y rotate through carry right (RCR), donde se usan 33 bits para la rotación, donde uno de los bits es el indicador de acarreo.
Cuando la instrucción requiere acceso a memoria, entonces se usa esta unidad y las que le siguen.
La unidad de segmentación tiene 6 registros de segmento y 4 registros de direcciones del sistema.
Los registros de segmento tienen un campo visible para el programador y tres campos usados por el procesador. El campo base indica donde comienza el segmento, el campo límite indica el offset máximo que se puede utilizar para el segmento, es decir que la longitud del segmento es el límite más 1. Los atributos indican permisos y otras configuraciones del segmento, por ejemplo indican si el segmento es de 16 o 32 bits.
La carga de los registros de segmento depende del modo de operación del microprocesador: en el modo real, el campo base se carga con el valores del selector multiplicado por 10 hexa, mientras que los otros dos campos no se modifican. En el modo protegido, los valores de los tres campos se obtienen de tablas de descriptores que se encuentran en memoria, apuntados por los registros GDTR y LDTR. El elemento de la tabla y si se usa GDTR o LDTR, depende del contenido del selector que estamos cargando en el registro de segmento.
Una vez cargado el registro de segmento correspondiente, no hay diferencia entre los modos real o protegido. El procesador se limita a leer los tres campos que fueron cargados previamente.
El registro IDTR indica la dirección lineal dónde comienza la tabla de vectores de interrupción.
El registro TR se utiliza para implementar multitarea.
La unidad de paginación solo funciona en modo protegido y se puede habilitarse o no, según convenga. Esta unidad traduce de direcciones lineales a direcciones físicas, que son las que usa el bus externo del procesador. Su uso principal es en sistemas operativos multitarea, donde los programas generalmente arrancan en la misma dirección lineal, pero deben estar ubicados en diferente espacio de RAM.
Otra posibildad de uso ocurre cuando el procesador opera en el modo virtual 8086. En este modo la carga de registros de segmento opera igual que en modo real. Pero como existe la posibilidad de tener varias tareas en el modo virtual 8086, y todas generan direcciones lineales de 0 a 10FFEF hexa, entonces la unidad de paginación se usa para que cada sesión se encuentre en una ubicación diferente de RAM. De esta manera corren las programas de DOS en sistemas operativos Windows de 32 bits.
Por último, en aplicaciones embebidas sin sistema operativo, se puede usar la unidad de paginación para detectar punteros nulos, deshabilitando la traducción de direcciones lineales a direcciones físicas para los primeros 4 MB, por ejemplo.
La traducción se basa en tablas que se encuentran en memoria apuntadas por el registro CR3. El registro CR2 lo carga el procesador con la dirección lineal que quiso convertir y no pudo debido a algún problema.
La tabla que se encuentra abajo que se llama TLB almacena las últimas traducciones hechas para no tener que acceder todo el tiempo a las tablas de paginación.
Aquí se pueden ver las diferentes direcciones que maneja el microprocesador: las direcciones lógicas, que están compuestas de selector y offset, son las que maneja el programador. La unidad de segmentación genera las direcciones lineales, que son la suma de la base del segmento y el offset. Por último, la unidad de paginación, si está habilitada, genera la dirección física, que va a la unidad de control del bus. Si la unidad de paginación no está habilitada, entonces la dirección física coincide con la lineal.
La unidad de control del bus genera las señales de control y opera el bus de direcciones y el de datos. El 80386 tiene 32 bits de direcciones y 32 de datos. El bus de direcciones tiene las líneas A31 hasta A2 y bank enable 0 a 3 negados. Como el proceador puede acceder indistintamente a 1, 2 o 4 bytes, entonces las memorias deben poder ser direccionadas por bytes. En el caso que vemos, hay cuatro memorias (pueden ser RAM o de sólo lectura) de 2n bytes.
El decodificador, que es el que sirve para habilitar estas memorias, debe asegurarse que las líneas de control memory/input output negado y data/control negado estén a 1 y los bits más altos del bus de direcciones tengan el valor apropiado según el mapa de memoria del hardware.
La salida del decodificador es cero cuando queremos habilitar las memorias. Sin embargo, no queremos que se habiliten todas juntas. Entonces, el procesador posee cuatro pines bank enable negados para determinar cuáles de las cuatro memorias se van a habilitar para lectura o escritura. El bank enable 0 negado se activa con valor 0 cuando la dirección es múltiplo de 4. El bank enable 1 negado se activa con valor 0 cuando la dirección es múltiplo de 4 más 1. Y así sucesivamente.
En el caso de usar memoria RAM, tendremos que conectar el pin de control write/ read negado a las cuatro patas de output enable de las memorias.
En este caso vamos a escribir un word en la dirección física 5. Esto significa que el byte bajo del word va a la dirección 5 y el byte alto del word va a la dirección 6.
Como 5 es igual a 0101 binario, entonces el valor que entrega el procesador a An+1 hasta A2 del bus de address es 1 hexadecimal. El procesador escribe en el bus de datos la información a grabar en D23 a D8. La entrada output enable negado de las memorias sigue al pin de control write/read negado, por lo que va a escribir en las memorias que se habiliten. Para habilitar la dirección física 5, el pin bank enable 1 negado se pone a cero. Para habilitar la dirección física 6, el pin bank enable 2 negado se pone a cero. Los otros bank enable negados quedan a uno para no habilitar las memorias que están en los extremos izquierdo y derecho.
En este caso vamos a escribir un double word en la dirección física 5. Esto quiere decir que hay que escribir en las direcciones 5, 6, 7 y 8. Como la dirección 8 tiene un valor diferente para el pin A2 del bus de direcciones, la unidad de control del bus automáticamente genera dos ciclos de memoria.
En la primera habilita las tres memorias de la izquierda, en forma similar al ejemplo anterior.
Ahora el bus de direcciones cambia de 1 hexa a 2 hexa y se habilita únicamente la memoria del extremo derecho poniendo bank enable 0 negado a cero.
Espero que esta explicación les haya servido. Hasta luego.