lunes, 19 de noviembre de 2007

Suplemento # 3: Cómo trabaja el microprocesador




En el Suplemento # 2 se dió una introducción somera a la evolución del microprocesador creado por la empresa Intel con el cual dió inicio la revolución informática que estamos viviendo en estos momentos.

Tradicionalmente, antes del advenimiento del microprocesador, se podían identificar cuatro unidades independientes en la arquitectura de cualquier computadora:

(1) La unidad de control.

(2) La unidad aritmética.

(3) La unidad de almacenamiento o memoria.

(4) La unidad de entrada-salida.

El esquema general bajo esta concepción era el siguiente:


Sin embargo, con la llegada de la microelectrónica, no se llevó mucho tiempo para que los ingenieros se dieran cuenta de que dentro de un mismo circuito integrado se podían incorporar en un mismo substrato la unidad de control y la unidad aritmética:


Así, estas dos unidades independientes que funcionalmente (no físicamente) juntas eran conocidas como la unidad de procesamiento central (Central Processing Unit o CPU) fueron integradas en una sola unidad bautizada apropiadamente con el nombre de microprocesador.

Para que un microprocesador (y de hecho cualquier computadora digital) pueda trabajar es necesario proporcionarle instrucciones para que pueda llevar a cabo las tareas que le estamos encomendando, pudiendo haber otro tipo de información como números o nombres que presuntamente también se requiere. Por el momento pasaremos por alto el hecho de que las computadoras contemporáneas aceptan entradas en una variedad de códigos diferentes dependiendo del modelo y del fabricante, siguiendo instrucciones que han sido grabadas permanentemente en sus memorias de tipo ROM, ejecutando dichas instrucciones a su propio lenguaje de máquina, el lenguaje de "unos" y "ceros". Para nuestros propósitos educativos, imaginemos una pequeña computadora en la que la extensión de las palabras es de 9 bits, lo cual es mucho más corto que cualquier máquina que pueda ser de utilidad alguna pero que para nosotros será más fácil de seguir. La unidad de la memoria RAM será capaz de almacenar 64 palabras de este tipo, siguiendo un orden secuencial. Siempre que una secuencia binaria sea una instrucción los primeros tres bits que llamaremos código de operaciones ó op-code indicarán el tipo de instrucción que será ejecutada, mientras que los últimos seis valores que llamaremos el operando darán el número, o mejor dicho, el domicilio, de la memoria RAM que será requerido para poder ejecutar la instrucción:

XXX_ XXXXXX

Con tres valores disponibles para designar al opcode, son posibles ocho instrucciones diferentes. Si hubieran cuatro bits disponibles para designar al opcode, podríamos contar con 16 instrucciones diferentes. Y con ocho bits disponibles, nuestro conjunto de instrucciones (instruction set) tendría capacidad para 256 instrucciones diferentes. En cualquier máquina, entre mayor sea la cantidad de bits disponibles para el opcode mayor será la cantidad de instrucciones que puedan ser ejecutadas por la máquina. Ahora aprenderemos lo que cada combinación podría significar en lenguaje de máquina para nuestra pequeña computadora ficticia. La siguiente lista proporciona primero el lenguaje de máquina y tras esto una descripción de la instrucción, con el símbolo X pudiendo representar un "1" ó un "0".

000XXXXXX
Alto. En esta instrucción los últimos seis valores no son importantes.

001XXXXXX
Copiar la secuencia en el domicilio XXXXXX de la memoria RAM al acumulador, borrando primero los contenidos previos del acumulador. La palabra almacenada en el domicilio XXXXXX de la memoria RAM deberá estar en ambas partes después de que se ha ejecutado esta instrucción.

010XXXXXX
Sumar el número puesto en el domicilio XXXXXX de la memoria RAM al número que se encuentra en el acumulador, dejando el contenido en el domicilio XXXXXX sin cambio alguno. La suma será dejada en el acumulador. Esta será una suma binaria, no una suma Boleana.

011XXXXXX
Copiar la secuencia que se encuentra en el acumulador hacia el domicilio XXXXXX de la memoria RAM borrando primero cualquier contenido previo que haya sido puesto en XXXXXX. La palabra inicialmente en el acumulador deberá estar en ambas partes después de que se haya ejecutado esta instrucción.

100XXXXXX
Imprimir la secuencia que se encuentra en el domicilio XXXXXX de la memoria RAM, dejando la secuencia en dicho domicilio para su uso posterior.

101XXXXXX
Saltar hacia la instrucción puesta en el domicilio XXXXXX de la memoria RAM. Esto rompe con la rutina normal en la que las instrucciones son tomadas de domicilios contiguos, y es conocida como un salto incondicional (en inglés, jump).

110XXXXXX
Saltar de modo condicionado, tomando la siguiente instrucción del domicilio XXXXXX de la memoria RAM si y solo si el número que se encuentra actualmente en el acumulador es un número binario negativo. En caso contrario, continuar con la secuencia normal.

111XXXXXX
Restar la secuencia que se encuentra en el domicilio XXXXXX de la memoria RAM de la secuencia puesta en el acumulador, dejando los contenidos puestos en el domicilio XXXXXX de la memoria RAM inalterados.
Ahora preguntémonos nosotros mismos cuál será el resultado de la ejecución de las siguientes instrucciones en nuestra máquina ficticia, siguiendo el orden en el que están dadas empezando con la primera instrucción que está puesta arriba:

001010000
010010011
011010000
000101101

Para saber lo que ocurre, tenemos que hacer lo mismo que lo que haría la máquina, analizar cada instrucción. Esto se puede visualizar más fácilmente viendo las instrucciones de nuestro "programa" de la siguiente manera:

001010000

010010011

011010000

000101101

Puesto que los primeros tres bits indican el tipo de instrucción, empezamos por referirnos a nuestra lista anterior, leyendo 001XXXXXX que dice "copiar la secuencia en el domicilio XXXXXX de la memoria RAM al acumulador". En este caso, se trata del domicilio 010000, que nosotros preferimos llamar en nuestro lenguaje humano como el domicilio 16. La primera instrucción ordena que los contenidos en el domicilio 16 de la memoria RAM sean copiados al acumulador. La seguda instrucción dice "sumar el número puesto en el domicilio 010011 de la memoria RAM (el domicilio 19) al número que se encuentra en el acumulador", dejando la suma en el acumulador". De modo semejante, la tercera instrucción se traduce como "copiar la secuencia que se encuentra en el acumulador hacia el domicilio 010000 (el domicilio 16) de la memoria RAM". Mirando hacia atrás vemos que el número puesto en el domicilio 16 de la memoria RAM ha sido alterado, después de haberlo sumado al número puesto en el domicilio 16 de la RAM. Para lograr esto, los números fueron primero sacados de la memoria RAM y llevados a la unidad aritmética, en donde se computó la suma de ambos, con el resultado puesto en la RAM. Y por último, la cuarta instrucción 000XXXXXX le indica a la máquina que la ejecución del pequeño programa ha terminado. Este es un patrón convencional del flujo de información en cualquier microprocesador (o computadora). Los registros en la unidad aritmética, construídos a base de flip-flops, son rápidos y versátiles, y los datos deben ser sacados de la RAM y llevados a los mismos para su procesamiento. Pero por esto mismo estos registros están extremadamente ocupados la mayor parte del tiempo y por ello los resultados intermedios deben ser guardados en la memoria RAM dejando libres dichos registros para otras tareas.

Lo que hemos visto nos indica claramente que es necesario proveerle al microprocesador de circuitos de control que le permitan analizar las instrucciones y ejecutarlas de una manera similar a la forma en la cual se llevó a cabo en nuestro pequeño programa escrito en lenguaje de máquina. Suponiendo que todas las instrucciones necesarias para la ejecución de un programa se encuentran almacenadas en alguna parte de la memoria, la Unidad de Control (Control Unit) debe poder:

(1) Seleccionar cada instrucción siguiendo una secuencia incremental ordenada y copiarla hacia los registros internos del microprocesador para su análisis.

(2) Llevar a cabo el análisis y tomar las acciones necesarias para la ejecución de la instrucción.

Una forma de implementar estas acciones se muestra en el siguiente esquema:




Todo comienza con un humilde flip-flop del tipo R-S, del tipo que vimos en el capítulo quinto de este libro. Este flip-flop divide la acción las en dos partes alternantes bosquejadas arriba: secuenciamiento (búsqueda) y ejecución. Al comienzo, la salida del flip-flop (que puede ser la salida Q) para el ciclo de búsqueda (ciclo fetch) tiene un "1" y permite que el domicilio de la memoria RAM en el cual se encuentra puesta la primera instrucción, posiblemente el domicilio 000001, ingrese al decodificador de domicilios propio de la memoria RAM, que con la acción de "leer" (read) envía el contenido de este domicilio al registro de instrucciones para su análisis posterior. Cuando este paso se ha llevado a cabo, un pulso de un "1" es enviado para "resetear" el flip-flop, activando su otra salida (que en este caso sería su salida complementaria Q) para que se pueda llevar a cabo la ejecución (ciclo execute) de la instrucción. Esto abre otras compuertas, permitiendo que el contenido del registro de instrucciones entre en el decodificador de instrucciones mostrado en la figura, mientras que la porción correspondiente al domicilio va al decodificador de domicilios de modo tal que el domicilio requerido de la memoria RAM pueda ser encontrado. Entre estos dos decodificadores es necesario activar suficientes canales para que se pueda ejecutar la instrucción, tal vez enviando una palabra binaria a la unidad aritmética y empezando una operación de suma binaria en caso de ser la instrucción 010 en nuestra máquina hipotética. En otras palabras, la ejecución de una instrucción dada por el programador al microprocesador lleva implícitas la ejecución de varias instrucciones que podemos llamar microinstrucciones. La ejecución de todas estas microinstrucciones internas son precisamente la labor que lleva a cabo la Unidad de Control mencionada previamente, e inclusive la "microprogramación" de la Unidad de Control (definida como la técnica que consiste en descomponer las instrucciones definidas en el lenguaje de máquina en operaciones elementales llamadas micro instrucciones implementándolas en el "hardware" partiendo de una base común en donde todas las instrucciones utilizan los mismos circuitos, con el microprograma residiendo permanentemente de modo inalterable en el mismo corazón del microprocesador) es una de las técnicas refinadas disponibles para aumentar la eficiencia de cualquier computadora. El siguiente diagrama muestra cómo está integrado esto dentro del núcleo mismo de la computadora:


Mientras ocurren todas las acciones ya señaladas arriba en nuestra máquina hipotética el domicilio en el que se encuentra la siguiente instrucción debe ser incrementado en uno, de modo tal que cuando ocurra la siguiente búsqueda la siguiente instrucción en la secuencia del programa pueda ser localizada, a menos de que la instrucción que se está ejecutando sea del tipo que requiere de un "salto" incondicional hacia otro domicilio de la memoria fuera de la secuencia normal. Es aquí de donde viene el término de "acceso al azar" para la memoria RAM. Una vez que se ha completado la ejecución de la instrucción, la salida del flip-flop (que puede ser la salida Q) es puesta nuevamente a "1" de modo tal que un nuevo ciclo de búsqueda pueda comenzar.

Aunque las múltiples "microinstrucciones" no accesibles al programador que el microprocesador ejecuta cuando decodifica alguna instrucción que le haya sido dada en lenguaje de máquina sean invisibles y dependientes del modelo de microprocesador y del fabricante, en prácticamente cualquier computadora que procesa instrucciones almacenadas en su memoria RAM podemos identificar claramente tres ciclos distintos: búsqueda, decodificación y ejecución. Cada instrucción es buscada primero en la memoria RAM y transferida a un registro especial de la unidad de control del microprocesador llamado el registro de instrucción (conocido en inglés como Instruction Register ó simplemente con el acrónimo IR). Una vez allí, la instrucción es decodificada por un decodificador pasando a manos de la Unidad de Control en donde finalmente la instrucción será ejecutada.

Veamos a continuación un segundo programa hipotético elaborado directamente en lenguaje de máquina para nuestra pequeña computadora ficticia, desde la perspectiva de un programa típico almacenado en la memoria RAM de la computadora:


Al empezar la ejecución de este programa almacenado en la memoria RAM, la unidad de control tomando el domicilio 0 indicado por el Contador del Programa (PC) busca (ciclo fetch) la primera instrucción precisamente en el domicilio "0" de la memoria. Esta es una instrucción del tipo 001XXXXXX que ordena copiar la secuencia binaria almacenada en el domicilio 5 (XXXXXX=000101) al acumulador. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta instrucción es puesta en el Registro de Instrucción (IR) para ser decodificada. Y como el número binario almacenado en el domicilio 5 es el número binario "010000111", o sea el número 135, mediante la ejecución de varias microinstrucciones adicionales que abren y cierran varias compuertas y mueven los datos de un lado a otro (ciclo execute) este número es puesto en el acumulador.

Hecho esto, el Contador del Programa aumenta en conteo binario ascendente a 1, y en vez de apuntar al domicilio 0 ahora apunta al domicilio 1, en donde la segunda instrucción del programa debe ser buscada (ciclo fetch). Esta es una instrucción del tipo 010XXXXXX que ordena sumar la secuencia binaria almacenada en el domicilio 6 (XXXXXX=000110) al número que se encuentra en el acumulador. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número binario almacenado en el domicilio 6 es el número binario "000100110", o sea el número 38, mediante la ejecución de varias microinstrucciones adicionales que abren y cierran varias compuertas y mueven los datos de un lado a otro (ciclo execute) este número binario es sumado al número que se encuentra en el acumulador, dejando el resultado 173 ("010101101") en el acumulador.

Nuevamente, el Contador del Programa aumenta en conteo binario ascendente, y en vez de apuntar al domicilio 1 ahora apunta al domicilio 2, en donde la tercera instrucción del programa debe ser buscada (ciclo fetch). Esta es una instrucción del tipo 011XXXXXX que ordena copiar la secuencia binaria que se encuentra en el acumulador al domicilio 7 (XXXXXX=000111) de la memoria, el cual anteriormente estaba vacío. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta tercera instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número que se encuentra en el acumulador es el número binario "010101101", o sea el número 173 (el resultado de la suma), este número es almacenado en el domicilio 7 de la memoria.

Ahora el Contador del Programa aumentando en conteo binario ascendente en vez de apuntar al domicilio 2 apunta al domicilio 3, en donde la cuarta instrucción del programa debe ser buscada. Esta es una instrucción del tipo 100XXXXXX que ordena imprimir la secuencia binaria que se encuentra en el domicilio 7 (XXXXXX=000111) de la memoria. Con la ejecución de varias microinstrucciones abriendo y cerrando compuertas, esta tercera instrucción es puesta en el Registro de Instrucciones para ser decodificada. Y como el número que se encuentra en el domicilio 7 de la memoria es el número binario "010101101", o sea el número 173 (el resultado de la suma), este es el resultado aritmético que es impreso en alguna impresora.

El Contador del Programa aumentando en conteo binario ascendente en vez de apuntar al domicilio 3 apunta ahora al domicilio 4, en donde la quinta instrucción del programa debe ser buscada. Esta es una instrucción del tipo 101XXXXXX que ordena un salto incondicional al domicilio 8 de la memoria (ya que XXXXXX=001000, que equivale al 8 decimal). Esto significa que el contenido del Contador del Programa es borrado por completo y el Contador del Programa que a fin de cuentas no es más que un contador binario ascendente cuyo contenido puede ser "cargado" con cualquier otro número será puesto en el estado "101001000". Aquí es en donde la sexta instrucción deberá ser buscada. Y como la sexta instrucción almacenada en el domicilio 8 de la memoria es una instrucción del tipo 000XXXXXX, o sea una instrucción de ALTO, hemos llegado a la conclusión del programa. Obsérvese que la porción XXXXXX es ignorada, ya que en una instrucción de ALTO resulta completamente irrelevante.

Este segundo programa fue lo suficientemente pequeño para caber en la memoria RAM de nuestra máquina hipotética. El tamaño de la memoria RAM disponible desde siempre ha sido una preocupación y una limitante fundamental para todas las tareas que se quieran llevar a cabo en una computadora, razón por la cual una de las principales prioridades ha sido desde siempre el tratar de aumentar el tamaño de las memorias RAM recurriendo a todas las tecnologías disponibles.

Todos los programas de cómputo trabajan de la misma manera al nivel del lenguaje de máquina a los programas pequeños que acabamos de ver, desde los más sencillos hasta los más complejos.

Refiriéndonos al diagrama de arriba de nuestra pequeña computadora hipotética, repasemos nuevamente con mayor detalle las acciones que se llevan a cabo en nuestra pequeña máquina hipotética conforme se ejecuta el primer programa de cuatro instrucciones dado al principio de este suplemento en lenguaje de máquina. Supóngase que estas cuatro instrucciones han sido puestas en los domicilios de la memoria RAM 1, 2, 3 y 4, respectivamente (en notación binaria, serían los domicilios 000001, 000010, 000011, y 000100). Entonces:

(1) Al iniciar, el número 000001 debe estar puesto en el registro que guarda el "domicilio de la siguiente instrucción", y el flip-flop tiene que estar en el estado "set" con un "1" puesto en la terminal S.

(2) El domicilio 000001 pasa a través de la compuerta hacia el decodificador de domicilios de la memoria RAM, que a su vez encuentra el contenido 001010000 bajo tal domicilio y se prepara para enviar la primera instrucción al registro de instrucciones.

(3) El flip-flop es "reseteado" con un "1" puesto en la terminal R. Los primeros tres bits de la primera instrucción bajada de la memoria RAM, 001, pasan al decodificador de instrucciones que activa la terminal de salida que corresponde a tal instrucción en preparación para un copiado de la memoria. Al mismo tiempo, los últimos seis bits de la instrucción pasan al decodificador de domicilios. Puesto que estos bits representan el número binario 010000, el contenido en el domicilio 16 de la memoria RAM es localizado. La ejecución a partir de este punto es controlada por las microinstrucciones -no visibles al programador- activadas por la instrucción 001, las cuales deben llevar a cabo todas las acciones necesarias para copiar de tal domicilio de la memoria RAM sus contenidos hacia el acumulador. Después de que esto ocurre se debe producir una señal de "ejecución terminada", aumentándose el registro del "domicilio de la siguiente instrucción" en 1, o sea a 000010.

(4) El flip-flop es ajustado de nuevo al estado "set" con un "1" puesto en la terminal S. El domicilio 000010 (domicilio 2) pasa al decodificador de domicilios de la memoria RAM, que a su vez encuentra el contenido 010010011 bajo tal domicilio y se prepara para enviar la segunda instrucción al registro de instrucciones.

(5) El flip-flop es "reseteado" con un "1" puesto en la terminal R. Los primeros tres bits de la segunda instrucción bajada de la memoria RAM, 010, pasan al decodificador de instrucciones que activa la terminal de salida que corresponde a tal instrucción en preparación para una suma de números en el acumulador.

(6) El mismo proceso se repite una y otra vez, en forma casi monótona.

El ejemplo que se acaba de dar podría parecer risible para una aplicación práctica, y puede dar una idea equivocada de que los microprocesadores en realidad son máquinas muy primitivas capaces de hacer muy pocas cosas. Pero con un conjunto ampliado de instrucciones y con la capacidad de poder ejecutar cientos de miles de instrucciones por segundo con la ayuda de una memoria RAM de gran capacidad el microprocesador adquiere entonces su verdadera estatura de un gigante pequeño, capaz de competir en torneos de ajedrez con buenas probabilidades de éxito o capaz de pilotear por sí solo las misiones espaciales tripuladas y no-tripuladas.

Un programa contiene todas las instrucciones y todos los datos necesarios para que un microprocesador pueda llevar a cabo todas las tareas que se le están encomendando. El proceso de elaborar un programa para un microprocesador (o cualquier computadora) se llama programación. Esto es de lo que trata el software, los programas de la máquina, a diferencia del hardware que consiste en la electrónica de la máquina implementada mediante sus circuitos lógicos. Cuando un programa ha sido elaborado y terminado, es puesto dentro de la memoria RAM de la máquina, y de ésta manera puede ser ejecutado por el microprocesador a su propia velocidad sin ser retardado por intervención humana alguna.

Aunque en principio podemos "programar" cualquier microprocesador directamente en su lenguaje de máquina, usando "unos" y "ceros" la desventaja de hacer tal cosa es que en un programa grande que contenga miles de instrucciones es muy fácil para un humano equivocarse escribiendo un "1" en lugar de un "0" o viceversa. Y basta con que en un programa grande haya un sólo "1" en lugar de un "0", ó un solo "0" en lugar de un "1", para "reventar" la ejecución del programa. En este sentido, los microprocesadores y las computadoras son completamente intolerantes; no admiten un solo error. Es por ello que en lugar de las instrucciones

001010000
010010011
011010000
000101101

sería mucho más cómodo y menos susceptible de errores para nosotros los humanos recurrir a la ayuda de algún lenguaje simbólico en el cual escribiríamos algo como lo siguiente:
copiar x

sumar y

guardar x

alto
En tal caso, el microprocesador, o mejor dicho, el programa para "ensamblar" una tras otra las instrucciones en el lenguaje binario de "unos" y "ceros" que puede ser entendido por la máquina, el programa ensamblador (assembler) ejecutándose por el mismo microprocesador tendría que ir traduciendo estos caracteres alfabéticos al lenguaje de "unos" y "ceros" de la máquina. Esto requeriría que la máquina tuviera a su disposición en su memoria RAM una "tabla" de conversión como la siguiente:

000 - alto

001 - copiar

010 - sumar

011 - guardar

100 - imprimir

101 - saltar

110 - comparar

111 - restar
Pero estos son ya detalles "extra" que conciernen a la programación del microprocesador. La arquitectura básica del microprocesador sigue siendo exactamente la misma, no ha cambiado en nada. Corresponde al progrmador saber lo que quiere y puede hacer con el conjunto de instrucciones que la máquina le pone a su disposición. (Véase el "Suplemento 4a: El conjunto de instrucciones del microprocesador 8086".)

Abandonaremos ahora nuestra pequeña máquina hipotética para adentrarnos en los "intestinos" del microprocesador real. La explicación que se dió nos dará una mejor idea del por qué algunas cosas son requeridas del modo que son requeridas.

Cuando se enciende una computadora (¡cualquier computadora!) que había estado apagada por algún tiempo, todos los registros internos de su microprocesador así como todas las localidades de su memoria RAM contienen una combinación completamente al azar de "unos" y "ceros". No hay absolutamente nada con qué empezar a trabajar, ni instrucciones ni datos. Por ello, la primera prioridad del día es "limpiar" todos los registros internos del microprocesador rellenándolos con "ceros" a través de su terminal de "reajuste" R (reset). Este es precisamente el objetivo de la terminal RESET en todo microprocesador, la cual recibe un "1" sostenido por breve tiempo durante el encendido de la máquina, ya sea de un componente especializado como el circuito integrado MAX809:


o de alguna combinación de circuitos eléctricos implementada por el diseñista. Un registro especial que durante el proceso de inicialización de la máquina puede ser "limpiado" cargándolo con "ceros" o con cierta combinación especial de "unos" y "ceros" es el puntero de instrucciones (instruction pointer ó IP) de la máquina. Esta combinación especial de "unos" y "ceros" es el domicilio en la memoria en donde el microprocesador buscará precisamente la primera instrucción que deberá ser ejecutada, la cual puede estar situada al principio de la memoria (al principio de la memoria RAM en un domicilio como "0000000000000000" ó inclusive hasta el final de la memoria en un domicilio como "FFFF" hexadecimal). Esta primera instrucción le indicará al microprocesador las acciones que deberán llevarse a cabo tras el encendido durante el proceso de inicialización.

Así pues, un microprocesador, por sí solo, no es capaz de hacer absolutamente nada. Se necesita alguna manera de poder meterle, paso a paso, las combinaciones de unos y ceros que vayan a ser procesadas dentro del mismo. Estas combinaciones de unos y ceros consisten tanto en las instrucciones binarias como en los datos binarios que vayan a ser manipulados dentro del microprocesador de acuerdo con las instrucciones que se le dán en lenguaje de máquina. Para que la información pueda ser procesada rápidamente, de manera electrónica, se vuelve deseable tener una memoria externa al microprocesador en la cual se puedan depositar, en forma de palabras binarias de unos y ceros, tanto los datos como las instrucciones. Esta es la memoria RAM (acrónimo de la expresión en inglés Random Access Memory) de la que tanto se habla. De este modo, la operación del microprocesador y la operación de cualquier computadora desde las más pequeñas hasta las más poderosas se puede resumir en una secuencia sencilla de pasos:

(1) Al recibir el primer pulso de la señal de reloj CLK (la misma señal de reloj que vimos en el estudio de circuitos secuenciales como el flip-flop J-K y los contadores binarios), el microprocesador busca en la memoria la primera instrucción que deberá ser ejecutada.

(2) En el siguiente pulso de la señal de reloj o en los siguientes pulsos de la señal de reloj, el microprocesador siguiendo la primera instrucción que ha recogido de la memoria busca en la misma memoria el dato o los datos sobre los cuales se llevará a cabo alguna operación lógica o aritmética.

(3) Siempre bajo la acción secuencial de los pulsos en la señal de reloj, efectuada la operación el microprocesador deposita en la memoria el resultado intermedio o lo mantiene temporalmente en alguno de sus registros internos mientras se prepara para buscar la siguiente instrucción de la memoria RAM.

(4) El microprocesador busca en la memoria la siguiente instrucción que deberá ser ejecutada.

(5) Si la segunda instrucción lo pide, el microprocesador busca en la memoria el dato o los datos sobre los cuales se llevará a cabo algún procesamiento.

(6) Efectuada la segunda operación, el microprocesador deposita el resultado en la memoria o lo mantiene temporalmente en alguno de sus registros internos mientras se prepara para buscar la siguiente instrucción de la memoria RAM.

(7) El proceso se repite mecánicamente de la misma manera, hasta que el microprocesador encuentra una instrucción que le dice que el procesamiento ha terminado.

Para buscar instrucciones o datos en la memoria, cualquier microprocesador usa siempre el sistema del "domiciliamiento", poniendo el domicilio binario a la entrada de la memoria en el cual está contenida la instrucción o el dato que busca. Para identificar este domicilio, se usa una palabra binaria con combinación de unos y ceros que identifica cada domicilio en la memoria, la cual para un domicilio de 16 bits se representa simbólicamente de una manera como la siguiente (se usa la letra "A" porque en la lengua inglesa es la primera letra de la palabra address que significa domicilio):

A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0

De este modo, el domicilio que en nuestro lenguaje cotidiano decimal sería el domicilio 33, en el mundo del microprocesador sería:

A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0

= 0000000000100001

En este domicilio, el microprocesador tal vez encuentre algo como el siguiente byte:

D7D6D5D4D3D2D1D0 = 00001110

que represente la primera instrucción o el primer dato sobre el cual se va a llevar a cabo alguna operación.

Esquemáticamente hablando, este proceso de búsqueda y depósito de información del microprocesador tanto de instrucciones como datos de y hacia la memoria se puede representar como sigue:


Idealmente, es deseable usar líneas separadas para enviar la señal binaria que proporciona el domicilio y para recolectar o depositar los datos. Sin embargo, cuando esto no es posible por razones de costo, entonces se usan las mismas líneas, recurriéndose a algún "truco" (como la técnica del multiplex) para que se pueda distinguir lo uno de lo otro.

Veamos ahora la asignación funcional de los 40 pins de los que consta el circuito integrado en el cual se encuentra encapsulada la pastilla de silicón del microprocesador 8086 para ver cómo trabaja en la práctica lo que hemos mencionado anteriormente:




Lo primero que resalta es el pin 19 en donde le aplicamos al circuito integrado la señal de reloj CLK. Toda la secuencia ordenada de pasos que lleve a cabo el microprocesador estará coordinada por la señal de reloj. Si la señal de reloj se detiene, el microprocesador quedará detenido sin ir a ninguna parte.

La terminal 40 identificada como Vcc (en la cual se aplica un voltaje de +5 volts) y las terminales de tierra 1 y 20 (GND o ground) identificadas como Vss son las terminales con las cuales se le alimenta al microprocesador de energía eléctrica.

Al encenderse la máquina, al comenzar a trabajar el microprocesador, lo primero que se lleva a cabo es el "reseteo" del mismo "limpiando" todos sus registros e inicializando todo a su punto de partida en una forma como la que se señaló arriba con el circuito integrado MAX809. Este es el propósito de la terminal 21. La terminal RESET o su equivalente es una terminal que podemos encontrar en todos los microprocesadores, en la cual al aplicársele un voltaje o una señal de "1" todos los registros internos del microprocesador son "reseteados" a cero, poniendo al microprocesador en un estado de "inicio". Esta terminal por su forma de actuar es reminiscente (llevando a cabo la misma función) que la que lleva a cabo la terminal RESET en el flip-flop R-S, excepto que aquí la acción se aplica a todos los registros internos del microprocesador.

De los pins 16 al 2 incluyendo al pin 39 tenemos los pins que van conectados a la memoria RAM precisamente para sacar o depositar en la misma datos binarios. Obsérvese que estos pins pueden actuar como terminales para enviarle a la memoria RAM un domicilio (fetch Adress) ó para sacar y meter datos (Data) en la memoria RAM. Esto fue una necesidad de diseño con el fin de economizar la cantidad de terminales saliendo del microprocesador.

A continuación, veamos cómo podemos ensamblar una microcomputadora básica utilizando ese circuito integrado llamado microprocesador. En su forma más primitiva, la arquitectura esencial de operación es la siguiente:




Obsérvese cómo se ha incorporado en el diagrama una línea especial, una línea conocida en la literatura técnica como la línea R/W (acrónimo del inglés Read/Write, que significa Leer/Escribir), la cual podemos identificar como una línea de control. Esta línea es indispensable para que el microprocesador pueda sacar y meter datos de la memoria RAM , esta es la línea con la cual el microprocesador "prepara" a la memoria RAM para un proceso de lectura (operación R) de alguno de los datos contenidos en ella, o un proceso de escritura (operación W) de algún dato dentro de la memoria RAM. Dependiendo de la lógica utilizada, una señal lógica de "1" en la línea R/W puede significar "Leer" mientras que una señal lógica de "0" puede significar "Escribir", o viceversa en el caso contrario. En este diagrama se ha adoptado también la convención universal de simplificar los diagramas esquemáticos en los cuales varias líneas de conducción eléctrica que llevan información simultánea perteneciente al mismo grupo de datos son agrupadas y representadas como una sola flecha "gorda" en vez de las cuatro u ocho líneas que de otro modo tendrían que ser dibujadas sin que este exceso de líneas resulte en una mayor claridad de información. Esta "flecha gorda" que aparea datos pertenecientes a información del mismo género es conocida como bus, (posiblemente en similitud con la palabra inglesa con la cual se designa a los camiones públicos utilizados para transportar a varias personas a la vez). Así, lo que tenemos a la derecha en la siguiente figura representa lo mismo que lo que tenemos a la izquierda de la misma:




El microprocesador UP es conocido también en la literatura técnica como la Unidad de Procesamiento Central CPU (acrónimo de la palabra inglesa Central Processing Unit). Se ha mostrado explícitamente la terminal de entrada para la señal de reloj CLK, en la cual cada pulso de la señal de reloj va moviendo al sistema de un estado a otro. Esta señal de reloj es frecuentemente generada por un pequeño cristal de cuarzo (como los que usan los relojes de baterías que llevamos puestos en nuestras manos). Podemos imaginarnos al microprocesador en su interior construído a base de cientos o quizá miles de flip-flops J-K que están siendo movidos de un estado al siguiente precisamente bajo las señales de pulsos de reloj, complementado con cientos o quizá miles de compuertas lógicas así como registros internos de memoria construídos también con flip-flops J-K. A fin de cuentas, todo lo que hay dentro de un microprocesador está construído a partir de las tres funciones lógicas básicas, al igual que prácticamente cualquier computadora digital sobre la que pongamos nuestras manos.

La forma en la cual trabajaría el anterior sistema sería la siguiente: Al recibir el primer pulso de la señal de reloj en la terminal de entrada CLK, el microprocesador pondría en el bus de domicilios el primer domicilio binario, el domicilio "0000000000000000", y activaría la línea R/W con una señal lógica con la cual le dice a la memoria RAM que va a leer la palabra binaria que está contenida en este domicilio. La memoria RAM, bajo la señal que le fué dada en la línea R/W por el microprocesador, pone el dato en el bus de datos, para que sea recogido por el microprocesador. En el siguiente pulso de reloj, el microprocesador recoge este dato del bus de datos para procesarlo. Esta secuencia se va repitiendo una y otra vez, cientos, miles, o quizá millones de veces, en una rutina que nos podría parecer hasta monótona. Pero paso tras paso, la microcomputadora va procesando información, usando frecuentemente a la memoria RAM no sólo como suministradora de información sino también como retenedora de datos intermedios de carácter temporal, los cuales son desechados en cuanto el procesamiento que requieren ha sido concluído.

Tenemos ya un esquema básico, primitivo, que en principio debe ser capaz de funcionar. Pero para echar a andar esto tenemos que resolver primero un problema operativo: al encender el sistema, al aplicarle energía eléctrica a lo que tenemos arriba, la memoria RAM está completamente "limpia", no hay absolutamente nada dentro de ella, porque es una memoria volátil que al apagarse la microcomputadora pierde toda la información contenida en ella, si es que anteriormente tenía alguna información. Esta es la razón por la cual cualquier computadora por sofisticada que sea tiene que tener instalada esa unidad que llamamos el disco duro. En los tiempos en los que se diseñaron y construyeron las primeras computadoras, los discos duros no existían. Entonces, ¿qué podían hacer los pioneros en las ciencias computacionales, en su mayoría ingenieros electrónicos al no existir en esos tiempos ingenieros en computación por no existir ni siquiera las carreras en ciencias computacionales, para echar a andar sus crudos prototipos? Una salida a este dilema se muestra a continuación, en la cual tenemos no sólo una memoria volátil RAM conectada al microprocesador sino tenemos también una memoria ROM, una memoria de lectura únicamente, la cual a diferencia de la memoria RAM no pierde sus datos cuando la microcomputadora es apagada. En cierta forma, esta ventaja es también su desventaja, porque las "instrucciones de arranque" no pueden ser alteradas, están grabadas de modo permanente. De este modo, nuestro esquema mejorado es el siguiente:




Bajo este esquema, y con el fin de evitar conflictos, un rango de domicilios está reservado para la memoria ROM, mientras que el resto está reservado para la memoria RAM. Por ejemplo, podemos resevar los primeros 640 domicilios para las instrucciones y datos contenidos en la memoria ROM, y el resto para la memoria RAM. Al arrancar la microcomputadora, lo primero que hará será dirigirse a la memoria ROM buscando la primera instrucción que debe llevarse a cabo, tras lo cual irá avanzando paso a paso.

En la práctica, resulta algo complicado e ineficiente diseñar un circuito integrado que contenga tanto la memoria ROM como la memoria RAM, y esta es la razón por la cual generalmente ambos tipos de memorias se fabrican en circuitos integrados separados, independientes el uno del otro. A la hora de interconectar todo, no hay ningún problema con este tipo de arreglo, siempre y cuando la memoria ROM tenga asignado su propio rango de domicilios con los cuales el microprocesador la puede identificar distinguiéndola de la memoria RAM que debe tener su propio rango de domicilios independientes de los domicilios asignados a la memoria ROM.

Tal vez el mejor ejemplo de una memoria ROM, presente en toda computadora personal ya sea de escritorio o portátil, lo sea el circuito integrado BIOS (acrónimo de la expresión en inglés Basic Input/Output System), del cual se muestra a continuación uno montado en una tarjeta:




contiene esencialmente un conjunto de rutinas que trabajan estrechamente con el microprocesador para inicializar el sistema al encenderse la computadora. Esta es la "memoria" que nunca se borra cuando se apaga la computadora. Una de las primeras cosas que hace el BIOS, además de enviar información al monitor de una computadora para que el usuario sepa que la computadora ya está empezando a trabajar, es llevar a cabo un chequeo completo de la memoria RAM para verificar que cada uno los domicilios de la memoria RAM puedan almacenar ceros y unos adecuadamente y que no exista falla alguna en la cual algún bit esté "atorado en 1" (con lo cual la memoria RAM estaría impedida de poder almacenar un "0") o "atorado en 0" (con lo cual la memoria RAM estaría impedida de poder almacenar un "1"). Pero el BIOS no sólo lleva a cabo las labores de inicialización durante el encendido de la máquina. Tras esto, apoya la transferencia de información entre los elementos del sistema, como los discos, el monitor, el reloj del sistema y las tarjetas de expansión. Desde los tiempos en que hicieron su aparición las primeras computadoras personales de escritorio IBM PC XT, IBM PC AT y sus compatibles (clones), el BIOS -o ROM BIOS- ha estado incorporado en un chip de memoria de sólo lectura ROM integrado en la "tarjeta madre" (motherboard) en la cual se encuentra montado el microprocesador junto con los componentes electrónicos principales, siendo popularizado el uso del BIOS como la "memoria de arranque" en las computadoras personales por las compañías Phoenix y AMI. Aunque es fundamental para el funcionamiento, el BIOS es normalmente invisible a los usuarios de los equipos. Es importante recalcar que el BIOS, como memoria de lectura únicamente, sólo puede contener instrucciones para ser procesadas por el microprocesador. Por sí solo, el BIOS es incapaz de poder procesar información alguna. El BIOS es un derivado directo de lo que anteriormente se conocía como un programa "cargador" o loader, un programa que al arrancar la máquina se encargaba de "cargar" la memoria RAM con un conjunto mucho mayor de instrucciones en lenguaje de máquina, instrucciones con las cuales se echaba a andar un programa más sofisticado como el sistema operativo, lo cual no ha cambiado ya que una vez que la computadora ha terminado de ejecutar las rutinas de inicialización contenidas en el circuito integrado BIOS el control es transferido a otro programa como el sistema operativo Windows que tiene su propio programa "cargador" que se encarga de "cargar" la memoria RAM con el paquete de instrucciones con las cuales una buena parte del sistema operativo se instalará permanentemente en la memoria RAM, tomando control del microprocesador hasta que la máquina será apagada.

Con todo lo que hemos visto, nuestro prototipo de máquina no cuenta con todo lo necesario para que podamos ensamblar una microcomputadora a partir de sus circuitos básicos que al echarla a andar pueda llevar a cabo algunas funciones. Desafortunadamente, nos queda pendiente el problema de tener que poder comunicarnos como humanos con ella de alguna manera. Resulta obvio que tenemos que refinar nuestro diseño básico para poder comunicarnos de alguna manera con la microcomputadora. Tenemos que poder conectar unidades periféricas para la Entrada y Salida de Datos, una función general designada en la literatura técnica como I/O (acrónimo de Input/Output).





El esquema anteriormente mostrado es algo ineficiente en el sentido de que se necesita un bus de datos para poder meter y sacar información de las memorias RAM y ROM, y otro bus de datos para poder enviar y recibir información de las unidades periféricas de Entrada/Salida. ¿Y por qué no utilizar un solo bus de datos para meter y sacar información tanto de las memorias RAM y ROM como de las unidades periféricas? Esto es precisamente lo que se hace en la práctica. Naturalmente, todo tiene que estar coordinado cuidadosamente para evitar conflictos, tales como la posibilidad de que por un lado la memoria RAM pona un cero ("0", el equivalente de "tierra" eléctrica) en una de las línas de datos para enviárselo al microprocesador, y al mismo tiempo una unidad periférica ponga un uno ("1", el equivalente de un voltaje que puede ser algo como +3 volts) en la misma línea de datos, porque tal cosa equivaldría a crear un corto circuito dentro del sistema. ¿Y quién se encarga de impedir que puedan ocurrir este tipo de conflictos? En principio, el mismo microprocesador. Todo lo que ocurre en el sistema está siempre bajo el control directo del microprocesador. Es por esto que el microprocesador es verdaderamente el cerebro de la máquina. Sin embargo, conforme comienzan a incrementarse las tareas que debe llevar a cabo el microprocesador, puede ocurrir que el microprocesador se encuentre tan atareado llevando a cabo estas tareas de control externo que le queda muy poco tiempo para sus verdaderas y genuinas funciones, tales como llevar a cabo operaciones aritméticas. A la larga, esto puede disminuír la rapidez del sistema volviéndolo lento. Es por ello que, con el fin de evitar que el microprocesador terminara haciéndolo todo él sólo, se desarrollaron circuitos integrados especializados para el manejo y pre-procesamiento de información externa. Es así como se fueron desarrollando, en orden creciente de complejidad, circuitos integrados para el manejo de la información mostrada en los monitores monocromáticos de blanco y negro (estos circuitos fueron utilizados ampliamente en las tarjetas de gráficos Hércules Graphics Card), los cuales evolucionaron posteriormente para poder manejar información destinada a monitores de colores de 256 bits, evolucionando posteriormente para poder manejar información destinada a monitores de colores de 17 pulgadas y 19 pulgadas alcanzando los monitores planos actuales de plasma. Estos circuitos integrados de apoyo al microprocesador se han vuelto casi tan complejos como el mismo microprocesador, y típicamente contienen varios cientos de miles o inclusive millones de transistores.

Estructuralmente, en el interior de todo microprocesador existe por lo menos una unidad conocida como la Unidad de Aritmética Lógica (ALU, acrónimo del inglés Arithmetic Logic Unit) representada simbólicamente de la siguiente manera:




El propósito de esta unidad es llevar a cabo, en lenguaje de máquina, las labores más elementales de cómputo aritmético que se espera que pueda efectuar cualquier computadora. La unidad ALU debe poder realizar una suma binaria en la cual los sumandos son introducidos como la palabras A y B, y el resultado es puesto de inmediato en la salida identificada apropiadamente como R. La terminal de entrada F es una señal de control interna al microprocesador que indica la operación que deberá llevarse a cabo, y la terminal D es utilizada para indicar que el resultado ya está disponible en la terminal R. Una unidad ALU un poco más sofisticada debe ser capaz de poder llevar a cabo no solo una suma binaria sino también una suma Boleana, precisamente como la que vimos en el capítulo 3 titulado "El Algebra Boleana". A continuación tenemos el diseño, usando circuitos lógicos, de una unidad ALU con una modesta capacidad de dos bits:




En la actualidad, los microprocesadores que hacen funcionar a las computadoras de escritorio incorporan dentro de sí no solo una sino varias unidades ALU. La mayoría de las unidades ALU son capaces de poder llevar a cabo las siguientes operaciones:

(1) Operaciones aritméticas de números enteros (suma y resta, y posiblemente hasta multiplicación y división, aunque esto tiende a incrementar el costo del circuito integrado).

(2) Operaciones de álgebra Boleana sobre bits (AND, NOT, OR, XOR).

(3) Operaciones de corrimientos de bits ya sea hacia la izquierda o hacia la derecha, los cuales pueden ser interpretados como multiplicaciones por 2 o divisiones entre 2 (propias de la numeración binaria).

Cabe agregar aquí que poco antes de la llegada del microprocesador, dentro de la familia electrónica TTL de circuitos lógicos la empresa Texas Instruments introdujo el circuito integrado 74181 (74S181 en su versión fabricada con diodos Schottky de alta velocidad), que en aquél entonces era la primera unidad ALU completa fabricada en un solo circuito integrado, y el cual incluía el equivalente de unas 75 compuertas lógicas básicas.

Además de la unidad ALU, todo microprocesador incorpora dentro de sí varios registros temporales (ya sea con flip-flops R-S, flip-flops J-K, flip-flops D, etc.). Estos registros temporales son utilizados para "afianzar" información que va a ser procesada. Por ejemplo, en la unidad ALU, en un ciclo de máquina un registro puede recibir del exterior un sumando, y tras esto en otro ciclo de máquina otro registro es "cargado" con el otro sumando. Con ambos sumandos puestos en cada registro, el siguiente ciclo de máquina se encargaría de recoger el resultado de la suma binaria (o la suma boleana) puesto por la ALU en su salida R.

Además de la ALU y los registros internos para almacenar información temporal, dentro de todo microprocesador encontramos lo que se conoce como el Contador de Programa (PC, acrónimo del inglés que significa Program Counter). Este es un registro que le indica a la microcomputadora en qué parte se encuentra en la lista de instrucciones que está ejecutando. Dependiendo de los detalles del diseño, puede contener el domicilio de la instrucción que está siendo ejecutada, o el domicilio de la siguiente instrucción a ser ejecutada. El Contador de Programa es incrementado en 1 para cada ciclo de instrucción de modo tal que las instrucciones puedan ser recabadas en orden secuencial, una tras otra, de la memoria RAM. Este conteo ascendente es un simple conteo binario ascendente, y el lector ya sabe cómo construír uno a base de flip-flops J-K si leyó con cuidado el capítulo 6 que trata sobre el flip-flop J-K.

Un programa educativo sumamente interesante que resume lo que se ha descrito arriba y que detalla en forma simple pero efectiva el funcionamiento interno de un microprocesador (o de la unidad de procesamiento central de una computadora) paso por paso siguiendo el flujo de la información generada por un pequeño "programa" hipotético está disponible en el siguiente domicilio (se recomienda abrir el enlace en una ventana separada):

http://courses.cs.vt.edu/~csonline/MachineArchitecture/Lessons/CPU/sumprogram.html

Esta demostración educativa simula una suma binaria. Para echar a andar el programa, se activa la entrada al mismo titulada "Start Tuturial". Una vez adentro, la demostración dá la opción de listar el "programa" hipotético bajo el cual está corriendo el microprocesador ya sea en código de lenguaje ensamblador (assembly code) o en código de lenguaje de máquina (machine code). El "circuito integrado" que simboliza al microprocesador contiene lo que ya hemos visto (el registro de instrucciones IR, el contador de programa PC, la unidad de procesamiento aritmético ALU, el registro conocido comúnmente como el acumulador AC, y una pequeña memoria RAM situada del lado derecho en donde está almacenado el programa hipotético que está siendo ejecutado por la simulación).

Otro programa educativo que dá una perpectiva adicional sobre el funcionamiento interno de un microprocesador está disponible por cortesía de la Universidad de Hamburgo en Alemania en el siguiente domicilio:

http://tams-www.informatik.uni-hamburg.de/applets/hades
_________/webdemos/50-rtlib/90-prima/prima.html

Se trata de una "máquina primitiva" primitive machine o PRIMA de ocho bits, que incorpora: (1) una memoria RAM con capacidad de 256 bytes, (2) un registro acumulador de ocho bits de capacidad con un registro (flip-flop individual) adicional para la función de "Llevar" o de "Sobreflujo" según sea el caso, (3) una Unidad de Aritmética Lógica ALU de ocho bits, (4) un Registro de Instrucciones de ocho bits (llamado Befehlsregister que en alemán significa precisamente "registro de instrucciones"), (4) un Contador de Programa PC de ocho bits y, (5) lógica de control para la ejecución de las microinstrucciones implementada en un bloque STW (de la palabra alemana Steuerwerk con el mismo significado). Obsérvese que en la parte inferior media del circuito están puestos los indicadores de los ciclos fetch (búsqueda de dato) y execute (ejecución de instrucción) mencionados arriba, con la adición de un "ciclo" intermedio que en realidad no es más que la adjudicación del domicilio en la memoria RAM que está siendo accesado. La simulación mostrada consiste en un pequeño programa que primero "carga" el valor 1 en el acumulador, tras lo cual incrementa el acumulador, almacena el resultado en el domicilio 20 de la memoria RAM, y salta otra vez hacia la instrucción de incremento. Este es un bucle perpetuo, de modo tal que el programa continuará incrementando el valor del acumulador guardando dicho valor en el acumulador una y otra vez. La terminal "botón" en el extremo inferior del diagrama del circuito hace las veces del reajuste RESET, y al activarla (poniéndola en "blanco") la máquina es puesta en las condiciones necesarias para volver a empezar de nuevo desde el principio. Esta terminal "botón" normalmente tiene que estar en "1" (de color rojo) para que pueda llevarse a cabo la simulación. La simulación se puede llevar a cabo, paso por paso, "pulsando" la terminal de "reloj" clock en el extremo inferior izquierdo. Conforme avanza la simulación, es importante ver cómo el Contador de Programa PC "lleva la cuenta", como también vale la pena ver cómo la máquina va ciclando a través de los ciclos fetch y execute. La simulación de la Unidad de Control (Steuerwerk) para esta máquina PRIMA está accesible en el siguiente domicilio:

http://tams-www.informatik.uni-hamburg.de/applets/hades
__________/webdemos/50-rtlib/90-prima/prima-control.html

Del mismo modo, el secuenciador de los tres ciclos de esta máquina (fetch, address, execute) está disponible en el siguiente domicilio:

http://tams-www.informatik.uni-hamburg.de/applets/hades
__________/webdemos/50-rtlib/90-prima/sequencer.html

No debe asombrar a estas alturas que el secuenciador está compuesto a base de dos flip-flops D, y de hecho no es más que un "contador" sencillo. (La terminal PRESET en el diagrama tiene que estar "encendida", de color rojo, para que se pueda llevar a cabo la simulación.)

Aún cuando el diseño de un microprocesador ha sido concluído y se le considere un caso "cerrado" sin posibilidad alguna de poder agregarle más funciones y capacidades de procesamiento al modelo que está siendo puesto ya a la venta en el mercado, siempre es posible diseñar componentes externos que conectados en proximidad con el microprocesador lleven a cabo algunas labores de procesamiento aligerando la carga del microprocesador dejándolo libre para otras tareas. Tal es el caso del co-procesador matemático 8087 que fue diseñado para auxiliar al microprocesador 8086 de Intel en el manejo de operaciones matemáticas de números binarios representativos de números reales con punto decimal y notación exponencial:


Otra mejora que los microprocesadores integran dentro de sus arquitecturas algo conocido como las banderas (flags). Estas "banderas" no son más que una señal puesta en un registro que indica que algo ha sucedido que debería llamar la atención del programa que se está ejecutando. Una "bandera" típica será la bandera de sobreflujo (overflow), la cual será puesta a "1" cuando en alguna operación binaria tal como una operación de suma o de multiplicación el resultado haya excedido la capacidad de la máquina. El registro encargado de almacenar el valor dado por esta "bandera" se podría llamar en este caso bandera de sobreflujo (overflow flag). Cuando esta bandera está puesta en un "0" lógico, ello quiere decir que no ha habido sobreflujo alguno en la operación aritmética binaria llevada a cabo más recientemente, pero cuando alguna operación produce un sobreflujo entonces la "bandera" (que usamos como sinónimo de un registro implementado por un flip-flop) será puesta al valor lógico de "1". Es importante señalar que, como en todo lo que concierne al microprocesador, el microprocesador por sí solo no hará absolutamente nada cuando alguna bandera es puesta a un valor lógico de "1" por alguna situación especial; corresponde al programador del microprocesador estar verificando constantemente el valor de dichas banderas por si alguna condición especial ha ocurrido, y en caso de ser así el mismo programador debe escribir una rutina para el manejo adecuado de la situación.

El conjunto mínimo de banderas (registros) incorporados en la mayoría de los microprocesadores es el siguiente:

OVERFLOW. Sobreflujo. Esta bandera es puesta en "1" cuando el resultado de alguna operación binaria como suma o multiplicación ha excedido la capacidad de la máquina.

SIGN. Signo. Esta bandera es puesta en "1" cuando como resultado de alguna operación hemos llegado a un número negativo. Cuando el resultado de la operación binaria es un número binario positivo, esta bandera está normalmente en "0".

ZERO. Cero. Esta bandera es puesta en "1" cuando el resultado de alguna operación binaria (o Boleana) ha producido una palabra binaria de cero, esto es, con puros bits de "0".

CARRY. Llevar. Esta bandera es el resultado de alguna operación binaria como la operación de suma aritmética binaria en la cual la suma de dos cantidades ha excedido la capacidad de la máquina y se está indicando aquí que hay una función de "llevar".

PARITY. Paridad. Esta bandera es puesta a "0" cuando la paridad de alguna palabra binaria es impar, y es puesta a "1" cuando la paridad de la palabra binaria es par. (La paridad consiste en determinar si en una palabra binaria la cantidad de "unos" y "ceros" es la misma, ó si hay más "unos" que "ceros" ó "viceversa". Un "bit" extra de paridad que puede ser "1" ó "0" es añadido para ayudarle a la máquina el poder detectar si hay alguna palabra binaria errónea cuando su paridad es impar en lugar de par.)

Un componente frecuentemente integrado dentro de los diseños de una gran variedad de microprocesadores es la implementación en "hardware" de una estructura de datos conocida como la pila. La pila es una estructura cronológica, es un bloque LIFO (Last-In, First-Out) del tipo "lo último que entra es lo primero que sale", que acumula eventos (o símbolos) en el orden en el que van siendo depositados. El símbolo más "viejo" es localizado al fondo de la pila, y el más "joven" en el tope de la misma. Frecuentemente se le compara con una pila de platos en un restaurante, en donde los platos son apilados uno encima del otro en un agujero cilíndrico con un resorte puesto en el fondo del mismo. Los platos van siendo puestos en el tope de esta pila, y cuando un plato es removido de la pila siempre es tomado del tope. El acceso normal a una pila siempre es a través de su tope. Se puede detectar la presencia de una pila dentro de la arquitectura de un microprocesador o la capacidad de poder manejar una pila a través de una porción de la memoria RAM reservada para ello porque en el conjunto de instrucciones del microprocesador aparecen invariablemente las dos instrucciones que la delatan: push (empujar) y pop (botar, ó sacar). (Véase el Suplemento 4a: El conjunto de instrucciones del 8086 adicionado a estos suplementos.) Una operación push deposita los contenidos de un registro en el tope de una pila, mientras que una operación pop removerá el contenido del el tope de una pila y lo depositará en un registro (ampliar imagen):


Los datos que son puestos en la pila o tomados de la pila usualmente son tomados del bus de datos o puestos por la pila en el bus de datos:


Las pilas son indispensables para proporcionar la capacidad para manejar requisiciones de "interrupción" y para manejar niveles de sub-rutinas. También son usadas en forma intensa en cálculos aritméticos que deben ser llevados a cabo con la mayor eficiencia posible. Por ejemplo, la operación aritmética (A+B)(C+D) se llevaría a cabo de la manera siguiente con la ayuda de una pila:



La pila es precisamente el concepto matemático detrás de la notación polaca reversa (reverse polish notation ó RPN, también conocida como notación postfix) inventada por el matemático polaco Jan Lukasiewicz, utilizada ampliamente por las calculadoras de bolsillo comercializadas por la empresa Hewlett-Packard hasta que el abaratamiento de las computadoras y el aumento en su eficiencia produjo un declive en las aplicaciones que requerían al usuario aprender este tipo de matemáticas no tan intuitivas como las que usualmente conocemos. Y si bien la pila se ha dejado de usar "por fuera", en el interior de muchos programas elaborados para el microprocesador la pila sigue siendo indispensable por la mejora que puede ofrecer para el manejo de datos que de otra manera requerirían una cantidad mayor de recursos. Es precisamente la pila lo que se utiliza para que una computadora pueda "traducir" una expresión algebraica ordinaria como (AB)+(C/D) en algo que la computadora puede llevar a cabo en su propio lenguaje de máquina, recurriéndose para ello a la "construcción" de un "árbol" de símbolos:


en un proceso conocido como parsificación (parsing). Esto es justo lo que usa la computadora para compilar programas ejecutables que puedan corrar en una máquina como la que el lector está utilizando en estos instantes.

La pila puede ser implementada no sólo dentro de la arquitectura de un microprocesador (hardware) sino también por la vía de la programación haciendo accesos a la memoria RAM (software). Cuando es implementada dentro de la arquitectura de un microprocesador, se hace con un conjunto de registros internos puestos directamente en el "chip" del microprocesador, lo cual representa la ventaja de una mayor velocidad que si se implementa por la vía de la programación. La desventaja es que la profundidad de una pila integrada dentro de un microprocesador está limitada a una cantidad N de registros internos.

Una mejora que se puede llevar a cabo en muchos sistemas digitales con la cual se puede aumentar en forma apreciable la velocidad de los mismos es la incorporación dentro del circuito integrado de una memoria caché, la cual no es más que una pequeña memoria RAM puesta en el mismo substrato semiconductor en el cual está el microprocesador. Un microprocesador sin memoria caché tiene que buscar toda la información en la memoria RAM externa, la cual por estar situada físicamente a una distancia de varios centímetros del microprocesador consume una cantidad apreciable de tiempo tan sólo en el envío y la recepción de la información, un tiempo que mejor podría aprovecharse en el procesamiento de la información. Con una memoria caché integrada al microprocesador, el microprocesador actuando bajo las instrucciones del programa que está corriendo puede "cargar" de la memoria RAM a su memoria caché una buena cantidad de datos e instrucciones, digamos 1 Megabyte de información, que bien pueden ser un "paquete" que contiene las siguientes instrucciones a ser ejecutadas junto con los datos más próximos. De este modo, antes de ir hasta la memoria RAM para buscar un dato binario o la siguiente instrucción a ser ejecutada, el microprocesador buscará en su memoria caché tal información, y si la encuentra allí se evitará la pérdida de tiempo involucrada en accesar externamente a la memoria RAM. El concepto de la memoria caché es tan eficiente, que inclusive hasta los discos duros de alta calidad tienen integrada dentro de su electrónica una memoria caché (a manera de ejemplo, los discos duros Western Digital Caviar de 80 Gigabytes tienen en su electrónica una memoria caché de 8 Megabytes), de modo tal que antes de enviar información hacia afuera desde el disco duro primero se vacía una porción de la información solicitada en la memoria caché, y mientras el microprocesador está recolectando información de la memoria caché del disco duro el disco duro la electrónica del disco duro va preparando el siguiente vaciamiento de información hacia su memoria caché. Generalmente hablando, la presencia de una memoria caché en cualquier sistema digital es sinónimo de una mayor velocidad del sistema. Hay dos tipos de memoria caché: la memoria caché de Nivel 1 (Level One ó L1) y la memoria caché de Nivel 2 (Level 2 ó L2). La memoria caché L1 es considerada más "cercana" al microprocesador (y por lo tanto más eficiente y más rápida) que la memoria caché L2, como lo sugiere el siguiente esquemático:


A modo de ejemplo, el microprocesador AMD Athlon 64 (en proceso de obsolescencia) contiene una memoria caché L1 de 128Kbytes y una memoria caché L2 de 512 Kbytes.

El microprocesador no sirve únicamente como pieza central para construír computadoras personales de escritorio. En combinación con algún programa fijo grabado permanentemente en una memoria inalterable ROM, puede ser utilizado para algo tan sencillo como operar el panel del usuario de un horno de microondas. En este caso, al encenderse el horno de microondas, el microprocesador empieza ejecutando un programa perpetuo (sin instrucción de "alto") revisando continuamente el teclado para ver si el usuario no ha oprimido alguna tecla numérica o de control. Todo lo que hacen los hornos de microondas modernos está bajo el control de algún microprocesador que está ciclando todo el tiempo en un mismo programa hasta que se apaga la máquina. Por lo general, la memoria RAM requerida por estos sistemas especializados es de tamaño tan pequeño que frecuentemente es integrada junto con el mismo microprocesador, dentro de un solo circuito integrado. Cuando un microprocesador se utiliza para construír esta clase de sistemas digitales de control de uso general, se le conoce con el nombre de microcontrolador, y al igual que el microprocesador, al microcontrolador es a veces se le simboliza como µC.

El más reciente paso evolutivo del microprocesador, aprovechando la creciente concentración de componentes electrónicos en el mismo substrato, consiste en poner dos microprocesadores en lugar de uno solo en el circuito integrado, en la misma "pastilla" de silicón, trabajando de manera independiente. Esto es precisamente lo que tenemos en la siguiente pieza del microprocesador conocido actualmente como dual-core (núcleo-doble):




Como lo muestra la fotografía, cada uno de los dos microprocesadores (identificados como Core 1 y Core 2) de este "chip" construído por la empresa AMD tiene respectivamente a su lado una memoria caché L2 de 1 Megabyte de capacidad. Entre los más recientes microprocesadores construídos en torno a esta tecnología podemos citar los modelos 185, 285 y 885 de la familia de microprocesadores Opteron así como el microprocesador Athlon X2, ambos de la empresa AMD, mientras que la empresa Intel responde a esta competencia con microprocesadores como el Intel Core 2 e inclusive con el siguiente paso evolutivo, cuatro microprocesadores construídos en el mismo circuito integrado, un esquema conocido como quad-core (núcleo cuádruple) de lo cual el microprocesador Core 2 Quad bautizado originalmente como "Kentsfield" e introducido el 2 de noviembre de 2006 es el paradigma de esta innovación tecnológica. Y el microprocesador octal (ocho microprocesadores en una misma "pastilla") ya viene en camino, ya se está experimentando en los laboratorios de investigación y desarrollo de empresas como Intel y AMD con los primeros prototipos; sólo es cuestión de tiempo para que eventualemente lleguen al mercado.

El problema principal en utilizar dos microprocesadores en lugar de uno solo es el problema central enfrentado por la mayoría de los esquemas de lo que se conoce como multiprocesadores: la competencia entre los dos microprocesadores actuando de manera independiente por los recursos de la única memoria RAM disponible en el sistema bajo la arquitectura clásica:




Este problema es el mismo que enfrenta un sistema computacional construído con más de dos microprocesadores. El único punto de acuerdo universalmente aceptado es la conveniencia de poner una memoria caché próxima a cada microprocesador con el fin de no sobrecargar a la memoria RAM del sistema:




Más allá de este principio universal de diseño, no existe una teoría sencilla como el álgebra Boleana o el mapa de Karnaugh que nos permita "conectar" microprocesadores en alguna configuración "en serie" o "en paralelo" con la garantía de proporcionarnos una respuesta de procesamiento óptima. Lo que hay publicado sobre este tema se tiene que buscar y consultar en publicaciones elaboradas para ser leídas y entendidas por investigadores y personal académico trabajando a nivel de postgrado.

Pero este no es el único gran problema presentado por la disposición de utilizar dos microprocesadores en lugar de uno solo en un mismo sistema. El otro problema substancial es que los programas que van a correr en un sistema multiprocesador no pueden "adivinar" la presencia de dos microprocesadores en lugar de uno solo, esto lo tiene que tomar en consideración el equipo de programadores que está elaborando los programas que se van a ejecutar bajo este tipo de arquitecturas. Y escribir programas para un sistema que cuenta con más de un microprocesador no es una tarea fácil. Si anteriormente ya era de por sí bastante complicado escribir programas (con la ayuda de un lenguaje de alto nivel como C++ o como VisualBasic) para los microprocesadores convencionales, escribir programas para computadoras con más de un microprocesador se convierte en una labor que prácticamente requiere contar con un equipo sofisticado de varios ingenieros en software con títulos de maestría y doctorado en computación. Esto no significa que una labor de esta índole sea imposible de llevar a cabo. Se está haciendo en estos momentos en las supercomputadoras que recurren al uso de varios microprocesadores con el fin de dividir la carga computacional entre ellos acelerando así la velocidad de procesamiento de estos sistemas. Lo que significa es que con la inclusión de más de varios microprocesadores en un solo circuito integrado la elaboración de programas eficientes para sistemas comerciales construídos en torno a estos componentes puede terminar convirtiéndose en un "cuello de botella" que no será fácil de solventar inclusive para empresas con amplios recursos económicos como Microsoft.

Podemos obtener más información acerca de los esquemas multiprocesadores basados en el empleo de más de un microprocesador en un solo sistema yéndonos a los siguientes enlaces disponibles en la Wikipedia:

http://en.wikipedia.org/wiki/Multi-core_(computing)

http://en.wikipedia.org/wiki/Multiprocessing

http://en.wikipedia.org/wiki/List_of_Intel_Core_2_microprocessors

http://en.wikipedia.org/wiki/Intel_Core_2#Kentsfield